GLSL(OpenGL 着色器语言)基础语法

GLSL(OpenGL 着色器语言)基础语法

GLSL(OpenGL Shading Language)是 OpenGL 计算着色器的语言,语法类似于 C 语言,但提供了针对 GPU 的特殊功能,如向量运算和矩阵运算。

着色器的开头总是要声明版本,接着是输入和输出变量、uniform 和 main 函数。每个着色器的入口点都是main函数,在这个函数中我们处理所有的输入变量,并将结果输出到输出变量中。

cpp 复制代码
#version version_number
in type in_variable_name;
out type out_variable_name;
uniform type uniform_name;

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

基本语法

版本声明

每个 GLSL 着色器的第一行通常需要指定版本号:

c 复制代码
#version 330 core // 330 core 表示使用 OpenGL 3.3 及以上的核心模式
#version 120	// OpenGL 2.1
#version 450 core(OpenGL 4.5	// OpenGL 4.5

变量声明

c 复制代码
类型 变量名;
常见的基本数据类型
类型 说明 示例
int 整数 int a = 5;
float 浮点数 float b = 3.14;
bool 布尔类型 bool flag = true;
vec2 2D向量 (x, y) vec2 v = vec2(1.0, 2.0);
vec3 3D向量 (x, y, z) vec3 color = vec3(1.0, 0.5, 0.2);
vec4 4D向量 (x, y, z, w) vec4 position = vec4(1.0, 2.0, 3.0, 1.0);
mat4 4×4 矩阵 mat4 transform;
纹理类型(Texture Types)

纹理类型用于表示不同的纹理对象,它们在图形渲染中用于存储图片或纹理数据

类型 说明
sampler2D 2D纹理采样器,通常用于访问2D纹理图像
sampler3D 3D纹理采样器,用于访问3D纹理
samplerCube 立方体纹理采样器,用于访问立方体贴图(环境映射)
sampler2DArray 2D纹理数组采样器,访问多个2D纹理层
sampler2DShadow 用于阴影映射的2D纹理采样器
samplerCubeShadow 用于阴影映射的立方体纹理采样器
纹理采样示例
c 复制代码
#version 330 core

in vec2 TexCoords; // 传入的纹理坐标
out vec4 FragColor;// 输出的颜色

uniform sampler2D texture1; // 纹理变量,代表 GPU 采样的 2D 贴图

void main()
{
    FragColor = texture(texture1, TexCoords); // 从纹理中获取颜色,并输出
}

uniform关键字:uniform 变量用于在 C++ 代码和 Shader 之间传递数据。
与 in/out 不同,uniform 是全局变量,在着色器的所有调用中都保持相同的值。

聚合类型(Aggregate Types)

这些类型可以用来组合多个基本类型,提供更复杂的数据结构。

(1)数组类型(Array types):

cpp 复制代码
float arr[10];	// 一个包含 10 个 float 的数组。
vec3 arr[5];   	// 一个包含 5 个 vec3 向量的数组。

(2)结构体类型(Structure types):

cpp 复制代码
sstruct Light {
    vec3 position;
    vec3 color;
    float intensity;
};
内置变量类型(Built-in Variables)

OpenGL着色器程序中预定义的变量,用于接收从应用程序传递的状态或传递给其他着色器阶段的值。

(1)顶点着色器中常见的内建变量

变量 说明
gl_Position 指定当前顶点的屏幕空间位置。它是 vec4 类型,必须在顶点着色器中设置
gl_PointSize 指定绘制点的大小(只有在 GL_POINTS 绘制模式时有效)
gl_VertexID 当前顶点的索引

(2)片元着色器中常见的内建变量

变量 说明
gl_FragColor 最终输出的颜色。片元着色器必须设置它来确定每个像素的颜色
gl_FragDepth 指定片元的深度值

(3)全局变量

变量 说明
gl_FragCoord 片元着色器中的当前片元坐标
gl_TexCoord 纹理坐标,用于在片元着色器中进行纹理采样

向量操作

GLSL 提供了一些专门用于向量操作的语法

c 复制代码
vec3 v1 = vec3(1.0, 2.0, 3.0);
vec3 v2 = vec3(0.5, 0.5, 0.5);

vec3 sum = v1 + v2;  // 向量加法
vec3 diff = v1 - v2; // 向量减法
vec3 scale = v1 * 2.0; // 向量数乘
float dotProduct = dot(v1, v2); // 点积
vec3 crossProduct = cross(v1, v2); // 叉积

向量分量访问

cpp 复制代码
vec4 color = vec4(1.0, 0.5, 0.2, 1.0);
float red = color.r;    // red = 1.0
float green = color.g;  // green = 0.5
float blue = color.b;   // blue = 0.2
float alpha = color.a;  // alpha = 1.0

xyzw 和 rgba 可以混用

cpp 复制代码
vec3 rgb = color.rgb;  // 取前三个分量
vec2 xy = color.xy;    // 取前两个分量

矩阵运算

cpp 复制代码
mat4 transform = mat4(1.0); // 4×4 单位矩阵
vec4 position = vec4(1.0, 2.0, 3.0, 1.0);
vec4 result = transform * position; // 矩阵-向量乘法

GLSL 变量限定符

GLSL 提供了不同的变量限定符,决定变量的作用范围和生命周期

(1)变量存储限定符(Storage Qualifiers)

限定符 作用 适用着色器
in 输入变量,从上一阶段着色器传入的数据 顶点、片元着色器
out 输出变量,传递给下一阶段着色器 顶点、片元着色器
inout 既可作为输入,也可作为输出
uniform 全局变量,供 CPU 传入着色器,所有着色器阶段共享 顶点、片元着色器
attribute 旧版 GLSL(< 3.3),用于顶点着色器的输入变量,已被 in 取代 顶点着色器
varying 旧版 GLSL(< 3.3),用于顶点着色器和片元着色器之间传递数据,已被 in 和 out 取代 顶点、片元着色器
const 常量,在编译时确定,不能修改
buffer 访问 Shader Storage Buffer Object(SSBO),用于计算着色器
uniform

首先,uniform是全局的(Global)。全局意味着uniform变量必须在每个着色器程序对象中都是独一无二的,而且它可以被着色器程序的任意着色器在任意阶段访问。第二,无论你把uniform值设置成什么,uniform会一直保存它们的数据,直到它们被重置或更新。

代码示范

(1)在着色器中使用 uniform 关键字,并带上类型和名称。

cpp 复制代码
#version 330 core
out vec4 FragColor;
uniform vec4 ourColor;

void main() {
    FragColor = ourColor;
}

(2)在OpenGL程序代码中设置这个变量

cpp 复制代码
float timeValue = glfwGetTime();
float greenValue = (sin(timeValue) / 2.0f) + 0.5f;// 利用时间动态计算颜色或效果
int vertexColorLocation = glGetUniformLocation(shaderProgram, "ourColor");// 查询uniform ourColor的位置值
glUseProgram(shaderProgram);// 激活指定的着色器程序
glUniform4f(vertexColorLocation, 0.0f, greenValue, 0.0f, 1.0f);// 设置uniform值

QT代码示范

Qt 封装了glGetUniformLocation(),提供了uniformLocation()。

cpp 复制代码
// 获取 uniform 变量的位置
int location = shaderProgram.uniformLocation("ourColor");
if (location != -1) {
    shaderProgram.setUniformValue(location, QVector4D(0.0f, 1.0f, 0.0f, 1.0f));// 设置uniform值
}

或者,在QOpenGLShaderProgram中,甚至不需要手动获取Location,可以直接使用setUniformValue(),它会自动查找uniformLocation()并设置值😃:

cpp 复制代码
shaderProgram.setUniformValue("ourColor", QVector4D(0.0f, 1.0f, 0.0f, 1.0f));

总结

uniform变量用于在 程序(CPU)和 着色器(GPU)之间传递数据。uniform变量的值在渲染迭代中保持不变,直到显式地更新。可以用于传递光照、变换、时间等不随顶点或片段变化的参数。

"渲染迭代"是指每次绘制调用,每次调用glDrawArrays或 glDrawElements等函数时,都会渲染一批图形。在一次渲染调用中,uniform变量的值是固定的,直到显式地改变它。

(2)变量布局限定符(Layout Qualifiers)

关键字 作用
layout(location = N) 指定变量的绑定位置(常用于 in/out 变量)
layout(binding = N) 指定 uniform buffer、采样器的绑定点
layout(origin_upper_left) 设定坐标原点在左上角(用于片元着色器)
layout(pixel_center_integer) 指定像素中心对齐规则

(3)变量精度限定符(Precision Qualifiers)

关键字 作用
highp 高精度(32 位浮点数)
mediump 中精度(16 位浮点数)
lowp 低精度(10 位或 8 位浮点数)

在 OpenGL ES(移动端)上,必须显式声明精度,但桌面版 GLSL 默认 highp。

(4)插值限定符(Interpolation Qualifiers)

关键字 作用
smooth 默认插值方式(透视校正插值)
flat 禁用插值,所有片元使用同一顶点的值
noperspective 线性插值(无透视校正)

(5)片元着色器输出限定符(Fragment Shader Output Qualifiers)

关键字 作用
discard 丢弃当前片元,不进行颜色写入
depth_any 允许片元写入任何深度值
depth_greater 片元只能写入更大的深度值
depth_less 片元只能写入更小的深度值

渲染管线的基本流程

OpenGL 渲染管线是一个从顶点数据到最终像素颜色输出的流水线处理过程,主要分为以下几个阶段:

(1)应用阶段(CPU端)

1️⃣ 准备数据(VAO、VBO、EBO、Shader、Texture)

2️⃣设置状态(glEnable(GL_DEPTH_TEST) 等)

3️⃣执行绘制(glDrawArrays() / glDrawElements())

👉GPU 端(渲染管线)

(2)顶点着色器(Vertex Shader)

运行在 GPU 上,对每个顶点执行一次。

关键任务

处理顶点数据(如顶点坐标、颜色、法线、纹理坐标) 。

计算 顶点的最终位置(gl_Position),将物体坐标系转换为相机坐标系(裁剪空间),渲染相机范围内的物体。

计算并输出其他顶点属性:颜色、纹理坐标等数据,以传递给下一阶段。

输入
(1)顶点数据:顶点的位置、法线、颜色、纹理坐标等,这些数据通常保存在顶点缓冲对象(VBO)中。
(2)顶点着色器的输入变量:包括位置、法线、颜色等,通过layout关键字来指定。

处理

(1)顶点坐标变换:

使用 模型矩阵 (Model Matrix):将顶点从模型空间 转换到世界空间。

使用 视图矩阵 (View Matrix):将顶点从世界空间 转换到相机空间,通常相当于相机的视图矩阵。

使用投影矩阵 (Projection Matrix):将顶点从相机空间 转换到裁剪空间 ,通常有两种类型的投影:

1)正交投影(Orthographic projection)

2)透视投影(Perspective projection)

特性 正交投影(Orthographic Projection) 透视投影Perspective Projection)
投影线 平行 收敛(有消失点)
物体大小 不受视距影响,距离远近无关 物体距离远,投影变小;距离近,投影变大
深度感 无深度感,所有物体看起来是平的 有深度感,远近物体具有明显的大小差异
常见应用 2D 游戏、CAD、技术图纸 3D 游戏、虚拟现实、电影动画
适用场景 精确的比例和尺寸绘制(如建筑图纸、设计图纸) 创建真实的场景深度感,模拟人眼视角
投影效果 所有物体大小一致,不会变形 物体随着距离远近而缩小或放大,产生透视效果

(2)法线变换:

法线需要从模型空间变换到世界空间或视图空间,通常要用到模型矩阵的逆转置矩阵来正确处理缩放等非线性变换,以防止模型缩放影响法线的方向。

(3)顶点属性计算:

顶点着色器可以根据顶点的其他属性(如法线、颜色、纹理坐标)来计算光照、颜色等值,传递给片段着色器。

输出

(1)gl_Position:这是裁剪空间中的顶点位置,是后续阶段(如光栅化)裁剪的基础。

(2)gl_PointSize:在绘制点时,控制点的大小。

(3)gl_ClipDistance:用于顶点裁剪的距离参数。

(4)其他顶点属性(如颜色、法线、纹理坐标等)可以输出到后续的片段着色器。

代码示例

c 复制代码
#version 330 core

layout (location = 0) in vec3 aPos;  // 传入顶点位置(绑定顶点位置到 location = 0)
layout (location = 1) in vec3 aColor; // 传入顶点颜色
layout (location = 2) in vec3 inNormal; // 顶点法线

out vec3 vColor; // 传递给片元着色器的颜色
out vec3 fragNormal;  // 法线,传递给片段着色器

uniform mat4 model; // 模型矩阵
uniform mat4 view;  // 视图矩阵
uniform mat4 projection; // 投影矩阵

void main()
{
	// 将法线从模型空间变换到世界空间
    fragNormal = mat3(transpose(inverse(model))) * inNormal;

    gl_Position = projection * view * model * vec4(aPos, 1.0); // 计算最终的顶点位置,并赋值给 gl_Position
    vColor = aColor; // 颜色传递给片元着色器
}
layout(location = n) 解释

layout(location = n) 用来指定顶点属性的绑定位置 n。这个 n 位置是在OpenGL中通过glVertexAttribPointer或glVertexAttribPointer函数指定的。

(1)为了明确匹配顶点数据和着色器变量

顶点着色器需要使用 特定位置的属性 来获得顶点数据,例如顶点位置、颜色或纹理坐标。通过 layout(location = n),可以明确告诉 OpenGL 每个顶点属性的输入顺序和位置索引。避免因顺序错误或不一致导致数据混乱的问题。

(2) 对于多属性的支持

当你顶点数据包含多个属性时,如:位置、颜色、法线、纹理坐标等,需要为每个属性指定一个独立的 location,确保 OpenGL 正确地将数据传递给着色器。

在QT中写法如下:

👉 顶点数据的位置索引(n)必须和着色器中声明的 layout(location = n) 对应。

gl_Position的解释

gl_Position是OpenGL中一个非常特殊的内部变量,其类型是 vec4(四维向量),包含了顶点在三维空间中的位置以及齐次坐标的 w 分量。

cpp 复制代码
vec4 gl_Position = vec4(x, y, z, w);

gl_Position定义 了 顶点在裁剪空间中的位置,位于投影变换后的空间。
gl_Position作用: 当顶点着色器的输出 gl_Position 被送入裁剪阶段和光栅化阶段后,OpenGL 会进行裁剪和透视除法:

  • 裁剪:将视野外的顶点丢弃,只保留视野内的顶点。
  • 透视除法:通过除以 gl_Position.w 来将顶点从裁剪空间转换为屏幕空间,得到最终的窗口坐标。
    注:裁剪阶段和光栅化阶段是OpenGL渲染管线中的固定阶段,由OpenGL内部自动处理。

在上面的示例中,aPos 是传入的顶点坐标。我们将模型矩阵、视图矩阵和投影矩阵与 aPos 相乘,最终得到顶点的位置,并赋值给 gl_Position。

(3)图元装配

将顶点组合成 点(Point)、线(Line)、三角形(Triangle)。

(4)细分着色器(Tessellation Shader,可选)

用于将少量控制点细分成更多的几何数据,以生成更精细的模型(如地形细节)。

细分控制着色器(Tessellation Control Shader, TCS)

细分评估着色器(Tessellation Evaluation Shader, TES)

细分曲面,提高模型精度

(5)几何着色器(Geometry Shader,可选)

运行在 GPU 上,以整个图元(如点、线段、三角形)为单位进行处理,而不是单独处理每个顶点。

关键任务

修改或生成新的顶点(如扩展为线段或面)。

可以输出多个顶点来生成新的几何形状。

继续传递颜色、纹理坐标等数据。

代码示例

cpp 复制代码
#version 330 core
layout(triangles) in;  // 输入图元类型(三角形)
layout(triangle_strip, max_vertices = 3) out; // 输出图元类型(三角形条带)

in vec3 vColor[]; // 从顶点着色器接收颜色数据
out vec3 fColor; // 传递给片元着色器

void main()
{
    for (int i = 0; i < 3; i++) // 处理每个输入顶点
    {
        gl_Position = gl_in[i].gl_Position; // 继承输入顶点的位置信息
        fColor = vColor[i]; // 继承颜色信息
        EmitVertex(); // 输出该顶点
    }
    EndPrimitive(); // 结束当前图元
}

(6)光栅化(Rasterization)

将顶点数据转换为像素点(片元),并决定哪些像素应该被绘制填充。以顶点为边界,中间做插值运算,填充像素点。

(7)片元着色器(Fragment Shader)

运行在 GPU 上,对每个片元(像素)执行一次。

关键任务

纹理采样:从纹理的像素 赋值给上一阶段产生的像素点上;

计算最终颜色(纹理、光照、阴影、颜色混合计算);

输出颜色给帧缓冲

【注】假若,顶点着色器4个,到了光栅化,插值后为100 100,那么到了片段着色器,则会进行100 * 100次运算。所以,在这个阶段的运算量是指数级增长。故能在顶点着色器运算的,就不要放到片段着色器中运算,大大影响效率。

代码示例

cpp 复制代码
#version 330 core
in vec3 vColor;  // 从顶点着色器传入的颜色
//in vec3 fColor; // 从几何着色器接收颜色
out vec4 FragColor; // 输出最终颜色

void main()
{
	FragColor = vec4(vColor, 1.0); // 计算最终颜色
    // FragColor = vec4(fColor, 1.0); // 计算最终颜色
}

注意:顶点着色器输出的变量和片元着色器输入的变量的名称必须一致!

顶点着色器的输出变量(out 变量)会作为输入传递给片元着色器(in 变量)。OpenGL会自动匹配具有相同名称的变量,从而使得顶点着色器的输出可以作为片元着色器的输入。

在OpenGL渲染管线中,着色器阶段之间的数据传递是通过 变量名称、类型和顺序 来确保一致性的。通常,顶点着色器的输出(out 变量)会作为下游阶段(如片元着色器或几何着色器)的输入(in 变量)。确保变量名称、类型和顺序一致是实现数据正确传递的关键。

(8)测试与混合

深度测试(Depth Test) → 确保正确的遮挡关系。

模板测试(Stencil Test) → 实现裁剪效果。

混合(Blending) → 处理透明度,如 glEnable(GL_BLEND)。

(9)Framebuffer帧缓冲

最终颜色数据被写入帧缓冲区(Framebuffer),并显示到屏幕上。帧缓冲有个地址,是在内存里。我们通过不停的向Framebuffer中写入数据, 显示控制器就自动的从Framebuffer中取数据并显示出来。

多个着色器都可以输出颜色?

顶点着色器:负责处理每个顶点的颜色,并传递给几何着色器或片元着色器。

几何着色器:(如果存在) 可以修改顶点的颜色并重新输出给片元着色器。

片元着色器: 计算最终的像素颜色,并写入帧缓冲区。

总结

✅GLSL语法类似C语言,但更适合GPU并行计算。

顶点着色器 主要进行坐标变换

片元着色器 主要计算像素颜色

✅变量修饰符 in、out、uniform 控制数据流。

✅向量、矩阵 计算是GLSL 的核心,优化GPU性能。

相关推荐
滴水成川8 天前
Metal 着色器与渲染管线
着色器·metal
stevenzqzq8 天前
openGl片段着色器的含义
opengl·着色器
Allen74748 天前
往期项目shader着色器实践效果应用合集
着色器·ta
不收藏找不到我16 天前
10、基于osg引擎生成热力图高度图实现3D热力图可视化、3D热力图实时更新(带过渡效果)
3d·数据可视化·着色器
不收藏找不到我21 天前
7、基于osg引擎实现读取vtk数据通过着色器实现简单体渲染(1)
着色器
MessiGo21 天前
OpenGL(4)着色器
apache·着色器
太妃糖耶22 天前
Shader中着色器的编译目标级别
unity·shader·着色器
HELLOMILI23 天前
[Unity3D] 动态立方体贴图系统
游戏·unity·游戏引擎·图形渲染·着色器
HELLOMILI25 天前
第四章:反射-Reflecting Your World《Unity Shaders and Effets Cookbook》
游戏·unity·游戏引擎·游戏程序·图形渲染·材质·着色器