Qt+OpenGL——着色器

定义与结构

着色器是使用一种叫GLSL的类C语言写成的运行在GPU上的小程序。这些小程序为图形渲染管线的某个特定部分而运行。从基本意义上来说,着色器只是一种把输入转化为输出的程序。着色器也是一种非常独立的程序,因为它们之间不能相互通信;它们之间唯一的沟通只有通过输入和输出。——LearnOpenGL CN

一个典型的着色器有下面的结构:

#version version_number     ————这里写明OpenGL的版本,例如3.3.0就写:#version 330 core
in type in_variable_name;   ————输入值的变量类型与变量名
in type in_variable_name;   ————输入值的变量类型与变量名

out type out_variable_name; ————输出值的变量类型与变量名

uniform type uniform_name;  ————全局变量的变量类型与变量名

int main()
{
  // 处理输入并进行一些图形操作
  ...
  // 输出处理过的结果到输出变量
  out_variable_name = weird_stuff_we_processed;
}

Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,但uniform和顶点属性有些不同。首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。——LearnOpenGL CN

简单的着色器程序代码

初期的OpenGL教程中着色器分为顶点着色器和片段着色器,但无论哪种着色器其结构都是与上述结构相符合的。一个简单的顶点着色器的代码如下:

#version 330 core
layout (location = 0) in vec3 aPos; // 位置变量的属性位置值为0

out vec4 vertexColor; // 为片段着色器指定一个颜色输出

void main()
{
    gl_Position = vec4(aPos, 1.0); // 注意我们如何把一个vec3作为vec4的构造器的参数
    vertexColor = vec4(0.5, 0.0, 0.0, 1.0); // 把输出变量设置为暗红色
}

其中的gl_Position便是用于显示在界面上的图形的顶点。

编译

着色器程序的编译并不是由编译器来完成,而是用C++编写代码来进行编译。OpenGL封装好了编译着色器的方法,我们只需要进行调用即可。编译着色器的代码如下:

unsigned int vertexShader;
vertexShader = glCreateShader(GL_VERTEX_SHADER);//创建顶点着色器
glShaderSource(vertexShader,1,&vertexShaderSource,NULL);//参数2为需要编译的着色器源码的数量
glCompileShader(vertexShader);
 
int  success;
char infoLog[512];
glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);//检查着色器编译是否成功
if(!success)
{
    glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);//获取错误消息
    qDebug() << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog;
}

layout (location = 0)到底是啥?

初次看到着色器中的这行代码时内心全是波动甚至不知道是干什么的,直到仔细阅读教程发现它的奥秘。

为了定义顶点数据该如何管理,我们使用location这一元数据指定输入变量,这样我们才可以在CPU上配置顶点属性。我们已经在前面的教程看过这个了,layout (location = 0)。顶点着色器需要为它的输入提供一个额外的layout标识,这样我们才能把它链接到顶点数据。——LearnOpenGL CN

其中的关键就在于location=0中的0,这里的0对应的是在进行顶点缓冲数据的加载时的第一个参数,也就是:

glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);

glVertexAttribPointer函数的参数含义入下图:

uniform关键字的使用

着色器中定义全局变量就要使用unifrom关键字,这个关键字不仅代表所定义的变量是全局变量,还可以将这个数据与C++进行交互来达到动态改变图形的目的。使用示例如下:

//着色器代码
#version 330 core
out vec4 FragColor;

uniform vec4 ourColor; // 在OpenGL程序代码中设定这个变量

void main()
{
    FragColor = ourColor;
}

C++代码如下:

int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUseProgram(shaderProgram);
glUniform4f(vertexColorLocation, 0.0f, 1.0, 0.0f, 1.0f);

其中,vertexColorLocation表示获取到的uniform变量的位置,shaderProgram为编译着色器程序的ID。修改unifrom变量的关键函数便是glUniform4f。

若我们要修改的是其它的值,那么将使用不同的glUniform,参考如下:

 

 

 

参考网址:

https://learnopengl-cn.github.io/01%20Getting%20started/05%20Shaders/#_1

https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/

 

注:本文中所有的代码都来源于参考网址中的示例代码,只是为了与Qt进行结合做了部分改动。

发布了28 篇原创文章 · 获赞 4 · 访问量 7394

猜你喜欢

转载自blog.csdn.net/JuicyActiveGilbert/article/details/90511270