目录
本章节源码请点击此处
一 简介
GLSL(Graphics Library Shader Language))是一种类C语言,它是一种为OpenGL设计的编程语言,用于编写图形着色器程序。着色器是运行在图形处理器(GPU)上的小型程序,它们允许开发者对图形管线的各个阶段进行编程控制,以实现复杂的视觉效果和实时渲染功能。
这里主要借鉴的openGL中文手册,唯一不同的是,测试示例和代码改成了Qt+OpenGL,而并非采用原生的C++版本的测试调用
在学习这部分之前要对OpenGL的基础先有所了解
二 数据类型
GLSL中包含C等其它语言大部分的默认基础数据类型:int
、float
、double
、uint
和bool
。GLSL也有两种容器类型,它们会在这个教程中使用很多,分别是向量(Vector)和矩阵(Matrix)。
2.1 向量
GLSL中的向量是一个可以包含有2、3或者4个分量的容器,分量的类型可以是前面默认基础类型的任意一个。它们可以是下面的形式(n
代表分量的数量):
类型 | 含义 |
---|---|
vecn | 包含n 个float分量的默认向量 |
bvecn | 包含n 个bool分量的向量 |
ivecn | 包含n 个int分量的向量 |
uvecn | 包含n 个unsigned int分量的向量 |
dvecn | 包含n 个double分量的向量 |
- 向量跟结构体类似,是有自己的分量的,对于一个拥有四个分量的向量(vec4),我们可以这样访问:vec.x vec.y vec.z vec.w 注意
vec.w
分量不是用作表达空间中的位置的(我们处理的是3D不是4D),而是用在所谓透视除法(Perspective Division)上。
2.1.1 向量重组
- 向量的分量是允许灵活重组的,但你不能在一个vec2的向量中去获取一个z元素
- 一个向量可以作为参数传递给不同的向量构造函数,以减少需求参数的数量
vec2 someVec;
vec4 differentVec = someVec.xyxx;
vec3 anotherVec = differentVec.zyw;
vec4 otherVec = someVec.xxxx + anotherVec.yxzy;
vec2 vect = vec2(0.5, 0.7);
vec4 result = vec4(vect, 0.0, 0.0);
vec4 otherResult = vec4(result.xyz, 1.0);
2.2 输入与输出
GLSL关键字 :每个着色器都有输入和输出,这样才能进行数据交流和传递。GLSL定义了in
和out
关键字专门来实现这个目的。
- in: 着色器接收输入
- out:着色器输出
只要一个输出变量与下一个着色器的输入相匹配,它就会传递下去。
但在顶点有所不同,需要使用loaction中指定输入变量.
- location:layout (location = n)
2.2.1 顶点着色器
#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); // 把输出变量设置为暗红色
}
2.2.2 片段着色器
注意: 只有上一个阶段的输出和下一个阶段的输入变量类型命令都相同时,才会被传递
#version 330 core
out vec4 FragColor;
in vec4 vertexColor; // 从顶点着色器传来的输入变量(名称相同、类型相同)
void main()
{
FragColor = vertexColor;
}
2.2.3 Uniform
- uniform:Uniform是一种从CPU中的应用向GPU中的着色器发送数据的方式,可以被任何着色器在任意阶段访问,因为他是全局的。
// 顶点着色器
#version 330 core
layout(location = 0) in vec3 aPos;
uniform vec4 ourColor; // 从CPU 也就是程序接收一个vec4的变量
out vec4 vertexColor; // 输出一个vec4变量的数据
void main(){
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);
vertexColor = ourColor;
}
// 片段着色器
#version 330 core
in vec4 vertexColor; // 接收命名为vertexColor的vec4的变量,并输出给下一个阶段
out vec4 FragColor;
void main(){
FragColor = vertexColor;
}
解释: 定义了uniform变量 等待程序输入一个vec4变量类型的值,然后将这个值作为输出传递给片段着色器
CPU传值部分代码:这样就可以实现整体的
makeCurrent(); // 拿到当前的上下文 这是很关键的
int timeValue = QTime::currentTime().second();
float greenValue = (sin(timeValue)/2.0f) + 0.5f;
shaderProgramObject.bind();
QVector4D colorVec(0.0f, greenValue, 0.0f, 1.0f);
// 设置Uniform变量的值
shaderProgramObject.setUniformValue("ourColor",colorVec);
shaderProgramObject.release();
update();
doneCurrent();
三 更多属性
对于顶点数据是可以把颜色也存放进去的,
float vertices_data[24] = {
// 所有的值是在[-1, 1]之间的
0.25f, 0.0f, 0.0f,1.0f, 0.0f, 0.0f,
0.0f, 0.5f, 0.0f,0.0f, 1.0f, 0.0f,
-0.25f, -0.0f, 0.0f,0.0f, 0.0f, 1.0f
};
这时候对于顶点着色器就需要修改了,与上面的唯一不同是不再用uniform传递颜色
// 顶点着色器
#version 330 core
layout(location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor; // 颜色变量的属性位置值为 1
out vec4 vertexColor; // 输出一个vec4变量的数据
void main(){
gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0f);
vertexColor = vec4(aColor,1.0f);
}
// 片段着色器
#version 330 core
in vec4 vertexColor; // 接收命名为vertexColor的vec4的变量,并输出给下一个阶段
out vec4 FragColor;
void main(){
FragColor = vertexColor;
}
这时候就需要更新顶点数据的格式
// 位置属性
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
// 颜色属性
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3* sizeof(float)));
glEnableVertexAttribArray(1);
四 练习
对应练习的QT版本源码在附录中已经做了具体实现
- 修改顶点着色器让三角形上下颠倒: 修改顶点着色器中的y左边为相反数
- 使用uniform定义一个水平偏移量,在顶点着色器中使用这个偏移量把三角形移动到屏幕右侧:
-
shaderProgramObject.setUniformValue("xOffset",0.5f);
使用out
关键字把顶点位置输出到片段着色器,并将片段的颜色设置为与顶点位置相等(来看看连顶点位置值都在三角形中被插值的结果):
- 因为把顶点位置输出给了片段着色器中,所以片段着色器中的左下角位置使用的是-0.5f,-0.5f,0.0f,但是颜色取值是0-1f的所有就会被转换成0.0f,0.0f,0.0f--->黑色