一、着色器基础结构
-
版本声明与入口函数
- 首行版本声明:必须指定 GLSL 版本和模式(如
#version 450 core
)。#version 450 core // 声明使用 OpenGL 4.5 Core Profile
- 入口函数:所有着色器的入口均为
main()
函数,负责处理输入变量并输出结果。
- 首行版本声明:必须指定 GLSL 版本和模式(如
-
输入/输出变量
- 顶点属性输入:通过
layout(location=N)
指定顶点属性位置(如位置、法线、纹理坐标),通过in
关键字声明。layout (location = 0) in vec3 aPos; // 位置属性(location 对应 VBO 的布局) layout (location = 1) in vec2 aTexCoord; // 纹理坐标
- 跨阶段传递:顶点着色器的
out
变量需与片段着色器的in
变量名称、类型一致以实现数据传递。out vec2 TexCoord; // 顶点着色器输出,片段着色器输入
- 顶点属性输入:通过
-
Uniform 全局变量
- 作用:用于从 CPU 向 GPU 传递全局数据(如变换矩阵、光源参数)。
- 特点:同一着色器程序中所有着色器共享相同的
uniform
值。uniform mat4 model; // 模型矩阵 uniform sampler2D tex; // 纹理采样器
二、着色器类型与功能
-
顶点着色器 (Vertex Shader)
- 职责:处理顶点属性(位置、法线等),执行模型-视图-投影变换,输出裁剪空间坐标。
- 限制:可用的顶点属性数量由硬件决定,需通过
GL_MAX_VERTEX_ATTRIBS
查询上限。
-
片段着色器 (Fragment Shader)
- 职责:计算像素颜色,支持纹理采样、光照计算,输出最终颜色到帧缓冲区。
- 优化场景:逐片元计算光照可避免逐顶点着色的视觉瑕疵(如马赫带效应)。
-
其他着色器类型
- 几何着色器:动态生成/修改图元拓扑结构(如将点扩展为四边形)。
- 曲面细分着色器:控制曲面细分级别,生成高精度几何体。
三、核心语法与数据操作
-
数据类型
- 基础类型:
float
、int
、bool
、vec2、vec3、vec4
、mat3、mat4
、sampler2D等。 - 容器操作:向量支持分量重组(如
vec3 pos = myVec.xyz
),矩阵为列优先存储。vec3 color = vec3(1.0, 0.0, 0.0); // 红色 mat4 view = mat4(1.0); // 单位矩阵
-
内置变量:
顶点着色器必须赋值gl_Position。
gl_Position
接收顶点经过模型-视图-投影(MVP)矩阵变换后的坐标值,其类型为vec4
。gl_Position = projection * view * model * vec4(aPos, 1.0);
裁剪空间坐标范围通常为[-1, 1],超出此范围的顶点会被裁剪。
-
片段着色器必须赋值
FragColor
:out vec4 FragColor; // 输出颜色到帧缓冲区
- 基础类型:
-
控制流与函数
- 分支/循环:支持
if-else
、for
、while
语句。 - 自定义函数:类似 C 语言风格,支持参数传递和返回值。
float calculateLight(vec3 normal, vec3 lightDir) {return max(dot(normal, lightDir), 0.0); }
- 分支/循环:支持
-
纹理采样
- 函数调用:使用
texture(sampler2D, uv)
读取纹理,需确保 UV 坐标在 [0,1] 范围内。
- 函数调用:使用
四、完整示例:带光照的纹理着色器
顶点着色器(Vertex Shader)
#version 450 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoord;out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoord;uniform mat4 model;
uniform mat4 view;
uniform mat4 projection;void main() {FragPos = vec3(model * vec4(aPos, 1.0)); // 世界空间坐标Normal = mat3(transpose(inverse(model))) * aNormal; // 法线矩阵修正TexCoord = aTexCoord;gl_Position = projection * view * model * vec4(aPos, 1.0);
}
片段着色器(Fragment Shader)
#version 450 core
in vec3 FragPos;
in vec3 Normal;
in vec2 TexCoord;out vec4 FragColor;uniform vec3 lightPos; // 光源位置
uniform vec3 lightColor; // 光源颜色
uniform sampler2D diffuseTex; // 漫反射贴图void main() {// 采样纹理vec3 diffuse = texture(diffuseTex, TexCoord).rgb;// 计算光照vec3 norm = normalize(Normal);vec3 lightDir = normalize(lightPos - FragPos);float diff = max(dot(norm, lightDir), 0.0);vec3 result = diff * lightColor * diffuse;FragColor = vec4(result, 1.0);
}
五、关键注意事项
-
变量精度修饰符(可选但推荐)
precision highp float; // 指定浮点数精度(highp/mediump/lowp)
-
矩阵乘法顺序
GLSL 矩阵乘法是列优先,需注意与 CPU 端矩阵操作的顺序一致性:mat4 mvp = projection * view * model; // 正确顺序
-
纹理采样
- 使用
texture(sampler, uv)
函数采样纹理 - 纹理坐标需归一化到 [0,1] 范围
- 使用
-
错误排查
通过glGetShaderInfoLog
获取编译错误信息:// Qt 中的调试代码示例 if (!shaderProgram.link()) {qDebug() << "Shader Error:" << shaderProgram.log(); }
六、常见问题
-
变量未传递:确保
uniform
变量在 CPU 端正确设置(如setUniformValue("model", matrix)
)。 -
版本不匹配:GLSL 版本需与 OpenGL 上下文版本兼容(如 450 core 需 OpenGL 4.5+)。
-
纹理绑定错误:检查纹理单元是否激活(
glActiveTexture(GL_TEXTURE0)
)。 -
调试日志输出:通过
glGetShaderInfoLog
获取编译错误详情。