文章目录
- 一、顶点着色器和片段着色器代码分析
- 1. 着色器1
- 2. 顶点着色器2
- 二、使用步骤
- 1. 使用着色器1
- 2. 使用着色器2
- 3. 在着色器2中使用EBO
- 三、完整代码
一、顶点着色器和片段着色器代码分析
1. 着色器1
用到的坐标矩阵, 四个四边形顶点坐标
float vertices_data[36] = {
// 所有的值是在[-1, 1]之间的
-0.5f, 0.4f, 0.0f,
-0.5f, 0.2f, 0.0f,
-0.3f, 0.2f, 0.0f,
-0.3f, 0.4f, 0.0f,
-0.1f, 0.4f, 0.0f,
-0.1f, 0.2f, 0.0f,
0.1f, 0.2f, 0.0f,
0.1f, 0.4f, 0.0f,
0.3f, 0.4f, 0.0f,
0.3f, 0.2f, 0.0f,
0.5f, 0.2f, 0.0f,
0.5f, 0.4f, 0.0f
};
// 顶点着色器1
//! 1. "#version 450 core": 版本号; GLSL版本号要和OpenGL版本号匹配
//! 2. "layout(location = 0) in vec3 aPos;": 顶点属性;
//! (1) glsl中,layout修饰符,(location = 0) 把顶点属性和特定缓冲区关联起来,并且顶点属性的识别号为0(创建顶点着色器处对比说明),
//! (2) in:输入顶点属性
//! (3) out:输出顶点属性
//! (4) vec3是数据类型,三元组
//! (5) aPos是定义的变量。即定义一个vec3类型变量aPos,将缓冲区0位置的顶点属性输入
//! 3. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);": 是GLSL的内建变量,会成为该顶点着色器的输出; 是一个4维向量(vec4),它包含了3个空间坐标(x, y, z)和齐次坐标(w), 通常将w设置为1.0
//! 4. "out vec4 FragColor":定义一个vec4变量FragColor, 并输出
//!
//! gl_Position设置的值会成为该顶点着色器的输出。由于我们的输入是一个3分量的向量,我们必须把它转换为4分量的向量(vec4)
//! 因为GLSL中没有一个3分量的向量的构造函数可以直接把一个vec3作为参数,所以我们必须先把vec3转换为vec4,然后再把它赋值给gl_Position
//! gl_Position 是一个vec4类型的输出变量,它会被转换为裁剪空间坐标
//! vec4是一个4维向量,它包含了3个空间坐标(x, y, z),还有一个齐次坐标(w), 通常将w设置为1.0
//! 裁剪空间坐标会被转换为屏幕空间坐标,同时也会被进行透视除法(Perspective Division) 除以w分量
//! 透视除法会将裁剪空间坐标的xyz分量分别除以w分量,最终得到的结果就是标准化设备坐标(Normalized Device Coordinates)
const char *vertexShaderSource =
"#version 450 core\n"
"layout(location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
"}\n\0";
//片段着色器1
const char *fragmentShaderSource =
"#version 450 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(0.4f, 0.6f, 0.8f, 1.0f);\n"
"}\n\0";
2. 顶点着色器2
用到的矩阵:坐标矩阵(包含坐标和颜色)、索引矩阵
float vertices_data2[72] = {
// 所有的值是在[-1, 1]之间的
//坐标 //颜色
-0.5f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.3f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.3f, -0.2f, 0.0f, 1.1f, 1.0f, 0.0f,
-0.1f, -0.2f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.1f, -0.4f, 0.0f, 1.0f, 0.0f, 1.0f,
0.1f, -0.4f, 0.0f, 0.0f, 1.0f, 1.0f,
0.1f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f,
0.3f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
0.3f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.2f, 0.0f, 1.0f, 1.0f, 0.0f
};
//控制绘制顺序
GLuint vertex_index[20] = {
0, 1, 2, 3,
2, 3, 4, 5,
4, 5, 6, 7,
6, 7, 8, 9,
8, 9, 10,11
};//!
// 顶点着色器2
//! 顶点着色器从矩阵中读取颜色,通过"out vec3 mcolor;"输出给片段着色器
注意此处的layout(location = 0)和layout(location = 1),对应下文解析数据时的参数
const char *vertexShaderSource2 =
"#version 450 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec3 aColor;\n"
"out vec3 mcolor;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
"mcolor = aColor;\n"
"}\n\0";
//片段着色器2 接收顶点着色器输出的颜色
const char *fragmentShaderSource2 =
"#version 450 core\n"
"out vec4 FragColor;\n"
"in vec3 mcolor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(mcolor, 1.0f);\n"
"}\n\0";
二、使用步骤
1. 使用着色器1
void MOpenGLWidgetShader::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0, 0.2, 0.3, 1.0);
// ------------------------一、VAO和VBO------------------------
// 1.创建VAO和VBO对象,并赋予ID(使用Gen)
glGenVertexArrays(1, &VAO_id);
glGenBuffers(1, &VBO_id);
// 2.绑定VAO,开始记录属性相关
glBindVertexArray(VAO_id);
// 3.绑定VBO(一定是先绑定VAO再绑定VBO)
glBindBuffer(GL_ARRAY_BUFFER, VBO_id);
// 4.把数据放进VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data), vertices_data, GL_STATIC_DRAW);
// 5.解析数据
//! glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
//! index 指定要配置的顶点属性的编号。
//! size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
//! type 指定每个分量的数据类型,可以是 GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT 或 GL_DOUBLE。
//! normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
//! stride (步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。
//! pointer 指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 6.开启location = 0的属性解析(就是上面顶点着色器处未说明的)
glEnableVertexAttribArray(0);
// 7.解绑VBO和VAO; 绑定一个ID为0,也就是不存在的ID,相当于接绑定(绑定新的ID,之前绑定的也就解除了)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// -----------------------------end---------------------------
// ------------------------二、着色器相关------------------------
// 1.创建顶点着色器
unsigned int vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader_id, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader_id);
// 2.创建片段着色器
unsigned int fragmentShader_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader_id, 1, &fragmentShaderSource, NULL);
//编译代码
glCompileShader(fragmentShader_id);
// -----------------------------end---------------------------
// -----------------------三、着色器程序相关----------------------
shaderProgram1_id = glCreateProgram();
glAttachShader(shaderProgram1_id, vertexShader_id);
glAttachShader(shaderProgram1_id, fragmentShader_id);
glLinkProgram(shaderProgram1_id);
// -----------------------------end---------------------------
// 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
glDeleteShader(vertexShader_id);
glDeleteShader(fragmentShader_id);
}
void MOpenGLWidgetShader::paintGL()
{
//清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
// 【画一个图形时需要说使用哪个着色器】
glUseProgram(shaderProgram1_id);
// 使用时还需要再绑定一次
glBindVertexArray(VAO_id);
// 开始绘制
//! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
//! first,从数组缓存中的哪一位开始绘制,一般为0。
//! count,数组中顶点的数量。
glPointSize(8);
glDrawArrays(GL_POINTS, 0, 12);
glDrawArrays(GL_QUADS, 0, 12);
}
结果如图
2. 使用着色器2
void MOpenGLWidgetShader::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0, 0.2, 0.3, 1.0);
//***********************************************************************
//***********************************************************************
//! VAO2、VBO2、EBO2
//***********************************************************************
//***********************************************************************
glGenVertexArrays(1, &VAO2_id);
glBindVertexArray(VAO2_id);
glGenBuffers(1, &VBO2_id);
glBindBuffer(GL_ARRAY_BUFFER, VBO2_id);
// glGenBuffers(1, &EBO2_id);
// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);
//读取矩阵数据到VBO2
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data2), vertices_data2, GL_STATIC_DRAW);
//! 告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)。
//! 参数1:要配置的顶点属性(layout(location = 0),故0),
//! 参数2:指定顶点属性的大小(vec3,故3),
//! 参数3:指定数据的类型,
//! 参数4:是否希望数据被标准化,
//! 参数5:在连续的顶点属性组之间的间隔,6*sizeof(float)
//! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset),0*sizeof(float)。
// 解析数据:按layout(location = 0)解析,每6位一组,每组偏移0处读取3位
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//! 参数1:要配置的顶点属性(layout(location = 1),故1)
//! 参数2:指定顶点属性的大小(vec3,故3),
//! 参数3:指定数据的类型,
//! 参数4:是否希望数据被标准化,
//! 参数5:在连续的顶点属性组之间的间隔,
//! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset), 3*sizeof(float)。
// 解析数据:按layout(location = 1)解析,每6位一组,每组偏移3处读取3位
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);
// //加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
// glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
// glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// ------------------------二、着色器相关------------------------
// 创建顶点着色器
unsigned int vertexShader2_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader2_id, 1, &vertexShaderSource2, NULL);
glCompileShader(vertexShader2_id);
// 创建片段着色器
unsigned int fragmentShader2_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader2_id, 1, &fragmentShaderSource2, NULL);
glCompileShader(fragmentShader2_id);
//判断着顶点色器编译是否成功
int success;
char infoLog[512];
glGetShaderiv(vertexShader2_id, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader2_id, 512, NULL, infoLog);
qDebug() << "ERROR::VERTEX::COMPILATION_FAILED\n" << infoLog;
};
//判断着片段色器编译是否成功
glGetShaderiv(fragmentShader2_id, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader2_id, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog;
};
// -----------------------------end---------------------------
// -----------------------三、着色器程序相关----------------------
shaderProgram2_id = glCreateProgram();
glAttachShader(shaderProgram2_id, vertexShader2_id);
glAttachShader(shaderProgram2_id, fragmentShader2_id);
glLinkProgram(shaderProgram2_id);
//判断着片段色器编译是否成功
glGetShaderiv(shaderProgram2_id, GL_LINK_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(shaderProgram2_id, 512, NULL, infoLog);
qDebug() << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog;
};
// -----------------------------end---------------------------
// 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
glDeleteShader(vertexShader2_id);
glDeleteShader(fragmentShader2_id);
}
void MOpenGLWidgetShader::paintGL()
{
//清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
// 【画一个图形时需要说使用哪个着色器】
glUseProgram(shaderProgram2_id);
// 使用时还需要再绑定一次
glBindVertexArray(VAO2_id);
// 开始绘制
//! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
//! first,从数组缓存中的哪一位开始绘制,一般为0。
//! count,数组中顶点的数量。
glPointSize(8);
glDrawArrays(GL_POINTS, 0, 12);
glDrawArrays(GL_QUADS, 0, 12);
// glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
// glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
}
结果如图:
3. 在着色器2中使用EBO
将(二、2)中
initializeGL()
函数中被注释的如下代码取消注释:
glGenBuffers(1, &EBO2_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);
......
//加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);
...
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
将(二、2)中paintGL()函数的如下代码替换注释
glDrawArrays(GL_POINTS, 0, 12);
glDrawArrays(GL_QUADS, 0, 12);
//! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
// glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
// glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
替换为
// glDrawArrays(GL_POINTS, 0, 12);
// glDrawArrays(GL_QUADS, 0, 12);
//! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
结果如图
三、完整代码
#ifndef MOPENGLWIDGETSHADER_H
#define MOPENGLWIDGETSHADER_H
#include <QObject>
#include <QWidget>
#include <GL/glu.h>
#include <QGL>
#include <QtOpenGL>
#include <QGLWidget>
#include <QOpenGLWidget>
#include <QOpenGLFunctions_4_5_Core>
#include <QGridLayout>
#include <QDebug>
class MOpenGLWidgetShader : public QOpenGLWidget, QOpenGLFunctions_4_5_Core
{
Q_OBJECT
public:
explicit MOpenGLWidgetShader(QWidget* parent = nullptr);
// 顶点的数据:没有解析的数据是没有意义的
// 内存中的数据,关键是如何将内存中的数据给显卡
float vertices_data[36] = {
// 所有的值是在[-1, 1]之间的
-0.5f, 0.4f, 0.0f,
-0.5f, 0.2f, 0.0f,
-0.3f, 0.2f, 0.0f,
-0.3f, 0.4f, 0.0f,
-0.1f, 0.4f, 0.0f,
-0.1f, 0.2f, 0.0f,
0.1f, 0.2f, 0.0f,
0.1f, 0.4f, 0.0f,
0.3f, 0.4f, 0.0f,
0.3f, 0.2f, 0.0f,
0.5f, 0.2f, 0.0f,
0.5f, 0.4f, 0.0f
};
float vertices_data2[72] = {
// 所有的值是在[-1, 1]之间的
//坐标 //颜色
-0.5f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.3f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
-0.3f, -0.2f, 0.0f, 1.1f, 1.0f, 0.0f,
-0.1f, -0.2f, 0.0f, 0.0f, 0.0f, 1.0f,
-0.1f, -0.4f, 0.0f, 1.0f, 0.0f, 1.0f,
0.1f, -0.4f, 0.0f, 0.0f, 1.0f, 1.0f,
0.1f, -0.2f, 0.0f, 1.0f, 1.0f, 1.0f,
0.3f, -0.2f, 0.0f, 0.0f, 0.0f, 0.0f,
0.3f, -0.4f, 0.0f, 1.0f, 0.0f, 0.0f,
0.5f, -0.4f, 0.0f, 0.0f, 1.0f, 0.0f,
0.5f, -0.2f, 0.0f, 1.0f, 1.0f, 0.0f
};
//控制绘制顺序
GLuint vertex_index[20] = {
0, 1, 2, 3,
2, 3, 4, 5,
4, 5, 6, 7,
6, 7, 8, 9,
8, 9, 10,11
};
// VAO和VBO变量(无符号整型)
unsigned int VAO_id,VBO_id;
// 着色器变量
unsigned int shaderProgram1_id;
// VAO和VBO变量(无符号整型)、索引缓冲对象
unsigned int VAO2_id,VBO2_id, EBO2_id;
// 着色器变量
unsigned int shaderProgram2_id;
public:
protected:
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
protected:
virtual void mousePressEvent(QMouseEvent *event);
virtual void mouseReleaseEvent(QMouseEvent *event);
virtual void mouseMoveEvent(QMouseEvent *event);
virtual void wheelEvent(QWheelEvent *event);
};
#endif // MOPENGLWIDGETSHADER_H
#include "mopenglwidgetshader.h"
//! 1. "#version 450 core": 版本号; GLSL版本号要和OpenGL版本号匹配
//! 2. "layout(location = 0) in vec3 aPos;": 顶点属性;
//! (1) glsl中,layout修饰符,(location = 0) 把顶点属性和特定缓冲区关联起来,并且顶点属性的识别号为0(创建顶点着色器处对比说明),
//! (2) in:输入顶点属性
//! (3) out:输出顶点属性
//! (4) vec3是数据类型,三元组
//! (5) aPos是定义的变量。即定义一个vec3类型变量aPos,将缓冲区0位置的顶点属性输入
//! 3. "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);": 是GLSL的内建变量,会成为该顶点着色器的输出; 是一个4维向量(vec4),它包含了3个空间坐标(x, y, z)和齐次坐标(w), 通常将w设置为1.0
//! 4. "out vec4 FragColor":定义一个vec4变量FragColor, 并输出
//!
//! gl_Position设置的值会成为该顶点着色器的输出。由于我们的输入是一个3分量的向量,我们必须把它转换为4分量的向量(vec4)
//! 因为GLSL中没有一个3分量的向量的构造函数可以直接把一个vec3作为参数,所以我们必须先把vec3转换为vec4,然后再把它赋值给gl_Position
//! gl_Position 是一个vec4类型的输出变量,它会被转换为裁剪空间坐标
//! vec4是一个4维向量,它包含了3个空间坐标(x, y, z),还有一个齐次坐标(w), 通常将w设置为1.0
//! 裁剪空间坐标会被转换为屏幕空间坐标,同时也会被进行透视除法(Perspective Division) 除以w分量
//! 透视除法会将裁剪空间坐标的xyz分量分别除以w分量,最终得到的结果就是标准化设备坐标(Normalized Device Coordinates)
// 顶点着色器1
const char *vertexShaderSource =
"#version 450 core\n"
"layout(location = 0) in vec3 aPos;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
"}\n\0";
//片段着色器1
const char *fragmentShaderSource =
"#version 450 core\n"
"out vec4 FragColor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(0.4f, 0.6f, 0.8f, 1.0f);\n"
"}\n\0";
//!
//! 顶点着色器从矩阵中读取颜色,通过"out vec3 mcolor;"输出给片段着色器
//! //!
// 顶点着色器2
const char *vertexShaderSource2 =
"#version 450 core\n"
"layout(location = 0) in vec3 aPos;\n"
"layout(location = 1) in vec3 aColor;\n"
"out vec3 mcolor;\n"
"void main()\n"
"{\n"
"gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);\n"
"mcolor = aColor;\n"
"}\n\0";
//片段着色器2
const char *fragmentShaderSource2 =
"#version 450 core\n"
"out vec4 FragColor;\n"
"in vec3 mcolor;\n"
"void main()\n"
"{\n"
"FragColor = vec4(mcolor, 1.0f);\n"
"}\n\0";
MOpenGLWidgetShader::MOpenGLWidgetShader(QWidget *parent)
: QOpenGLWidget{parent}
{
}
void MOpenGLWidgetShader::initializeGL()
{
initializeOpenGLFunctions();
glClearColor(0.0, 0.2, 0.3, 1.0);
// ------------------------一、VAO和VBO------------------------
// 1.创建VAO和VBO对象,并赋予ID(使用Gen)
glGenVertexArrays(1, &VAO_id);
glGenBuffers(1, &VBO_id);
// 2.绑定VAO,开始记录属性相关
glBindVertexArray(VAO_id);
// 3.绑定VBO(一定是先绑定VAO再绑定VBO)
glBindBuffer(GL_ARRAY_BUFFER, VBO_id);
// 4.把数据放进VBO
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data), vertices_data, GL_STATIC_DRAW);
// 5.解析数据
//! glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)
//! index 指定要配置的顶点属性的编号。
//! size 指定每个顶点属性的分量数(1、2、3 或 4,就像向量的维度一样)。
//! type 指定每个分量的数据类型,可以是 GL_BYTE、GL_UNSIGNED_BYTE、GL_SHORT、GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT 或 GL_DOUBLE。
//! normalized1 指定是否将数据归一化到 [0,1] 或 [-1,1] 范围内。
//! stride (步长)指定连续两个顶点属性间的字节数。如果为 0,则表示顶点属性是紧密排列的。
//! pointer 指向缓冲对象中第一个顶点属性的第一个分量的地址。(offset的作用)
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
// 6.开启location = 0的属性解析(就是上面顶点着色器处未说明的)
glEnableVertexAttribArray(0);
// 7.解绑VBO和VAO; 绑定一个ID为0,也就是不存在的ID,相当于接绑定(绑定新的ID,之前绑定的也就解除了)
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
// -----------------------------end---------------------------
// ------------------------二、着色器相关------------------------
// 1.创建顶点着色器
unsigned int vertexShader_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader_id, 1, &vertexShaderSource, NULL);
glCompileShader(vertexShader_id);
// 2.创建片段着色器
unsigned int fragmentShader_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader_id, 1, &fragmentShaderSource, NULL);
//编译代码
glCompileShader(fragmentShader_id);
// -----------------------------end---------------------------
// -----------------------三、着色器程序相关----------------------
shaderProgram1_id = glCreateProgram();
glAttachShader(shaderProgram1_id, vertexShader_id);
glAttachShader(shaderProgram1_id, fragmentShader_id);
glLinkProgram(shaderProgram1_id);
// -----------------------------end---------------------------
// 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
glDeleteShader(vertexShader_id);
glDeleteShader(fragmentShader_id);
//***********************************************************************
//***********************************************************************
//! VAO2、VBO2、EBO2
//***********************************************************************
//***********************************************************************
glGenVertexArrays(1, &VAO2_id);
glBindVertexArray(VAO2_id);
glGenBuffers(1, &VBO2_id);
glBindBuffer(GL_ARRAY_BUFFER, VBO2_id);
glGenBuffers(1, &EBO2_id);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO2_id);
//读取矩阵数据到VBO2
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices_data2), vertices_data2, GL_STATIC_DRAW);
//! 告诉OpenGL该如何解析顶点数据(应用到逐个顶点属性上)。
//! 参数1:要配置的顶点属性(layout(location = 0),故0),
//! 参数2:指定顶点属性的大小(vec3,故3),
//! 参数3:指定数据的类型,
//! 参数4:是否希望数据被标准化,
//! 参数5:在连续的顶点属性组之间的间隔,6*sizeof(float)
//! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset),0*sizeof(float)。
// 解析数据:按layout(location = 0)解析,每6位一组,每组偏移0处读取3位
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
//! 参数1:要配置的顶点属性(layout(location = 1),故1)
//! 参数2:指定顶点属性的大小(vec3,故3),
//! 参数3:指定数据的类型,
//! 参数4:是否希望数据被标准化,
//! 参数5:在连续的顶点属性组之间的间隔,
//! 参数6:表示位置数据在缓冲中起始位置的偏移量(Offset), 3*sizeof(float)。
// 解析数据:按layout(location = 1)解析,每6位一组,每组偏移3处读取3位
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3*sizeof(float)));
glEnableVertexAttribArray(1);
//加载索引矩阵数据; GL_ELEMENT_ARRAY_BUFFER
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(vertex_index), vertex_index, GL_STATIC_DRAW);
glBindVertexArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
// ------------------------二、着色器相关------------------------
// 创建顶点着色器
unsigned int vertexShader2_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertexShader2_id, 1, &vertexShaderSource2, NULL);
glCompileShader(vertexShader2_id);
// 创建片段着色器
unsigned int fragmentShader2_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragmentShader2_id, 1, &fragmentShaderSource2, NULL);
glCompileShader(fragmentShader2_id);
//判断着顶点色器编译是否成功
int success;
char infoLog[512];
glGetShaderiv(vertexShader2_id, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(vertexShader2_id, 512, NULL, infoLog);
qDebug() << "ERROR::VERTEX::COMPILATION_FAILED\n" << infoLog;
};
//判断着片段色器编译是否成功
glGetShaderiv(fragmentShader2_id, GL_COMPILE_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(fragmentShader2_id, 512, NULL, infoLog);
qDebug() << "ERROR::SHADER::COMPILATION_FAILED\n" << infoLog;
};
// -----------------------------end---------------------------
// -----------------------三、着色器程序相关----------------------
shaderProgram2_id = glCreateProgram();
glAttachShader(shaderProgram2_id, vertexShader2_id);
glAttachShader(shaderProgram2_id, fragmentShader2_id);
glLinkProgram(shaderProgram2_id);
//判断着片段色器编译是否成功
glGetShaderiv(shaderProgram2_id, GL_LINK_STATUS, &success);
if(!success)
{
glGetShaderInfoLog(shaderProgram2_id, 512, NULL, infoLog);
qDebug() << "ERROR::PROGRAM::LINKING_FAILED\n" << infoLog;
};
// -----------------------------end---------------------------
// 删除顶点着色器和片段着色器(不能将着色器程序也给delete掉)
glDeleteShader(vertexShader2_id);
glDeleteShader(fragmentShader2_id);
}
void MOpenGLWidgetShader::resizeGL(int w, int h)
{
//防止height为0
if (h == 0) { h = 1; }
//重置当前的视口
glViewport(0, 0,(GLint)w, (GLint)h);
}
void MOpenGLWidgetShader::paintGL()
{
//清除屏幕
glClear(GL_COLOR_BUFFER_BIT);
// 【画一个图形时需要说使用哪个着色器】
glUseProgram(shaderProgram2_id);
// 使用时还需要再绑定一次
glBindVertexArray(VAO2_id);
// 开始绘制
//! GL_APICALL void GL_APIENTRY glDrawArrays (GLenum mode, GLint first, GLsizei count);
//! first,从数组缓存中的哪一位开始绘制,一般为0。
//! count,数组中顶点的数量。
glPointSize(8);
// glDrawArrays(GL_POINTS, 0, 12);
// glDrawArrays(GL_QUADS, 0, 12);
//! 绘图类型 索引矩阵中有20个索引, 索引数据类型是无符号整形
glDrawElements(GL_POINTS, 20, GL_UNSIGNED_INT, 0);
glDrawElements(GL_QUADS, 20, GL_UNSIGNED_INT, 0);
}
void MOpenGLWidgetShader::wheelEvent(QWheelEvent *event)
{
QOpenGLWidget::wheelEvent(event);
}
void MOpenGLWidgetShader::mousePressEvent(QMouseEvent *event)
{
QOpenGLWidget::mousePressEvent(event);
}
void MOpenGLWidgetShader::mouseReleaseEvent(QMouseEvent *event)
{
QOpenGLWidget::mouseReleaseEvent(event);
}
void MOpenGLWidgetShader::mouseMoveEvent(QMouseEvent *event)
{
QOpenGLWidget::mouseMoveEvent(event);
}