OpenGL学习笔记-4

GLSL语言简单入门

为什么会有着色器语言

GLSL(OpenGL Shader Language) 是一种类C语言,它包含了一些针对向量和矩阵的特性。

它是着色器(Shader)使用的语言,着色器是运行在GPU上的小程序,供渲染管线中的某个特定部分使用。

GLSL的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#version version_number
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;
}

首先声明使用的OpenGL版本

其次由于渲染管线的结构,每一个着色器之间通过输入输出变量来通信,所以需要声明输入和输出变量,uniform和main函数。

当我们特别谈论到顶点着色器的时候,每个输入变量也叫顶点属性(Vertex Attribute)。我们能声明的顶点属性是有上限的,它一般由硬件来决定。OpenGL确保至少有16个包含4分量的顶点属性可用,但是有些硬件或许允许更多的顶点属性,你可以查询GL_MAX_VERTEX_ATTRIBS来获取具体的上限:

1
2
3
int nrAttributes;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &nrAttributes);
std::cout << "Maximum nr of vertex attributes supported: " << nrAttributes << std::endl;

数据类型

默认基础数据类型:int、float、double、uint、bool

容器类型:Vector、Matrix

向量

向量是一个可以包含2、3或4个分量的容器。

分量的类型可以是之前默认基础类型的任何一个。

获取分量可以通过vec.x来获取

分量的重组

1
2
3
4
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;

矩阵将在后面介绍

输入与输出

GLSL定义了in和out关键词来负责渲染管线中不同着色器之间的数据的交流和传递。

但是在顶点和片段着色器中有一些特殊的地方

顶点着色器

顶点着色器作为渲染管线中的第一个着色器,它接受的不是其他着色器输出的变量,而是从缓存中获取的顶点数据,我们在笔记2中,使用了glVertexAttribPointer函数来规定顶点数据该如何使用,其中第一个参数叫做顶点参数的位置值,他就是我们在编写顶点着色器中声明的。

方法是layout(location = 0)作为in关键词的前缀。location是用来标记顶点数据的码(Key)。

片段着色器

它需要一个vec4颜色输出变量,因为片段着色器需要生成一个最终输出的颜色。如果你在片段着色器没有定义输出颜色,OpenGL会把你的物体渲染为黑色(或白色)。

Uniform

Uniform类似于c++中全局变量的意思,这意味着它必须在每个着色器程序对象中都是第一无二的,它可以被着色器程序d饿任意着色器在任意阶段访问,第二,无论你把uniform设置为什么,uniform会一直保存。

我们可以在一个着色器中添加uniform关键字至类型和变量名前来声明一个GLSL的uniform。

1
2
3
4
5
6
7
8
9
#version 330 core
out vec4 FragColor;

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

void main()
{
FragColor = ourColor;
}

我们在片段着色器中声明了一个uniform vec4的ourColor,并把片段着色器的输出颜色设置为uniform值的内容。这个uniform值的内容是一个颜色,这个颜色在OpenGL程序中设定好了,我们现在要做的就是找到它的索引值,这样才能更新它。

我们用glGetUniformLocation查询uniform ourColor的位置值。

这里我们用uniform,来制作一个颜色会随着时间改变而改变的三角形。

使用如上的片段着色器,然后在渲染循环中加上如下的代码

1
2
3
4
float timeValue = glfwGetTime();
float greenValue = sin(timeValue) / 2.0f + 0.5f;
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);

我们使用glfwGetTime()函数来获取时间,通过sin函数来进行标准化处理,作为我们的颜色值.

使用glGetUniformLocation来查询uniform ourcolor的值,我们提供对应的着色器程序和uniform的名字,如果glGetUniformLocation返回-1就代表没有找到这个位置值。最后,我们可以通过glUniform4f函数设置uniform值。注意,查询uniform地址不要求你之前使用过着色器程序,但是更新一个uniform之前你必须先使用程序(调用glUseProgram),因为它是在当前激活的着色器程序中设置uniform的。

这样我们的uniform变量就设置完成了。