OpenGL渲染管线:
0、顶点数据:CPU将顶点数据传入GPU,包括顶点的坐标、颜色、法向量、纹理坐标等。
1、顶点着色器Vertex Shader:加工顶点数据,把顶点坐标进行平移、旋转、缩放等各种变换操作,修改各种顶点数据,比如纹理坐标,法向量等。
2、图元装配:将顶点装配成指定图元的形状,
3、几何着色器:生成新的顶点,修改输出的图元类型
4、光栅化:将几何数据转换为像素
5、片段着色器Fragment Shader:计算像素颜色
6、测试:筛选像素,丢弃部分像素
7、混合:表现物体透明度
QOpenGLWidget 类:
QOpenGLWidget 类是用于渲染 OpenGL 图形的Widget。
QOpenGLWidget提供了显示集成到Qt应用程序中的OpenGL图形的功能。它使用起来非常简单:让你的类继承自它,并像使用任何其他QWidget一样使用子类,此外还可以选择使用和标准OpenGL渲染命令。
QOpenGLWidget 提供了三个方便的虚函数,可以在子类中重新实现这些函数以执行典型的 OpenGL 任务:
- paintGL()-渲染 OpenGL 场景。每当需要更新widget时调用。
- resizeGL()-设置OpenGL的viewport、投影projection等。每当widget调整大小时调用(以及首次显示widget时,因为所有新创建的widget都会自动获得resize事件)。
- initializeGL()-设置 OpenGL 资源和状态。在第一次调用paintGL()或resizeGL()之前调用一次。
一、顶点数据和着色器程序
顶点数组vertices:四个顶点数据,每一行对应一个顶点,有八个float数值,分别为坐标(3),颜色(3),纹理(2)。
float vertices[] = {
// positions // colors // texture coords
0.9f, 0.9f, 0.0f, 1.0f, 0.5f, 0.0f, 1.0f, 1.0f, // top right
0.9f, -0.9f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // bottom right
-0.9f, -0.9f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // bottom left
-0.9f, 0.9f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // top left
};
索引数组indices:实现顶点的复用。0,1,3对应顶点数组中第一行、第二行、第四行,为一个三角形,1,2,3同理,两个三角形即一个矩形。
unsigned int indices[] = { // note that we start from 0!
0, 1, 3, // first Triangle
1, 2, 3 // second Triangle
};
顶点着色器程序:从顶点数据中直接接收输入,需要为输入提供一个额外的layout标识,这样才能链接到顶点数据。
#version 330 core
layout (location = 0) in vec3 aPos;//位置变量的属性位置值为 0
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = vec2(aTexCoord.x,1- aTexCoord.y);
}
片段着色器程序:需要生成一个最终输出的颜色–vec4颜色输出变量。
out和in用来在着色器程序中传递数据,uniform全局可见、变量唯一、一直保存。
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
// texture sampler
uniform sampler2D texture1;
void main()
{FragColor = texture2D(texture1, TexCoord);}
二、重写initializeGL()
VAO,Vertex Array Object顶点数组,一个元素对应一个顶点属性
VBO,Vertex Buffer Objects顶点缓冲区,一个元素对应一个顶点数据
glGenVertexArrays(1, &VAO);
glGenBuffers(1, &VBO);//分配 GPU 里的显存
VAO绑定,将该VAO中的数据提交给vertex shader
VBO绑定,GL_ARRAY_BUFFER为全局变量buffer(显存中),可以通过glVertexAttribPointer规定读写buffer的方式。
glBindVertexArray(VAO);
glBindBuffer(GL_ARRAY_BUFFER, VBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);//把数据从主存传到分配的显存
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
glBufferData(GL_ELEMENT_ARRAY_BUFFER,sizeof(indices),indices,GL_STATIC_DRAW);
glVertexAttribPointer:把对应的顶点属性存储到当前绑定的VAO的对应元素中,第六个参数指针表示位置数据在缓冲中起始位置的偏移量。
glEnableVertexAttribArray:将索引对应的属性状态置为enabled,此时着色器读取值才能生效。
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
绑定顶点着色器和片段着色器
shaderProgram.addShaderFromSourceFile(QOpenGLShader::Vertex,":/vshader.glsl");
shaderProgram.addShaderFromSourceFile(QOpenGLShader::Fragment,":/fshader.glsl");
shaderProgram.link();
纹理绑定
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture); //绑定纹理
//设置纹理包装参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
//将“纹理换行”设置为默认换行
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
//设置纹理过滤参数
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
纹理生成
int width, height, nrChannels;
unsigned char *data = stbi_load("D:\\qtproject\\untitled\\1.jpeg",&width,&height,&nrChannels,0);
//unsigned char *data = stbi_load("brickwall.jpg",&width,&height,&nrChannels,0);
if(data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
qDebug() << "Failed to load texture" ;
}
stbi_image_free(data);
三、重写paintGL()
glClearColor(0.1f, 0.9f, 0.3f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
shaderProgram.bind();
glBindVertexArray(VAO);
glBindTexture(GL_TEXTURE_2D, texture);
glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);//使用索引时的绘制方式
参考:
https://zhuanlan.zhihu.com/p/392128223
https://blog.csdn.net/weixin_42126427/article/details/122807819
https://zhuanlan.zhihu.com/p/108077199
https://zhuanlan.zhihu.com/p/56693625