GLSL 3.0简介
1. 概念
着色语言Shading Language
用于调整渲染管线的GPU编程语言。
GLSL 3.0
OpenGL Shading Language 3.0。
顶点Vertex
顶点是构成3D模型的基本单位,包含位置、法线、纹理坐标、颜色等属性。例如,一个立方体由8个顶点定义,每个顶点存储其在三维空间中的坐标。
顶点着色器Vertex Shader
从顶点属性(位置、法线等)生成中间数据的函数。顶点着色器控制几何形态(如动画、变形)。
图元Primitive
图元是由顶点组合成的基础几何形状,如点、线、三角形。例如,两个顶点构成一条线,三个顶点构成一个三角形面片。图元作为中间媒介,连接顶点与片元处理阶段。图元自身没有独立的着色器,几何着色器可以修改图元。
片元Fragment
片元是光栅化后生成的像素候选,包含位置、颜色、深度等信息,但尚未通过最终测试(如深度测试)。例如,一个三角形覆盖的屏幕区域会被拆解为多个片元。
片元着色器Fragment Shader/Pixel Shader
根据顶点着色器输出的中间数据、纹理和全局变量uniform,计算最终色彩候选值。片元着色器控制视觉效果(如材质、光照)。
几何着色器Geometry Shader
可以新建或修改图元。性能开销大,在实时渲染中较少使用。
渲染管线
将3D场景转换为2D图像过程,通常分为以下阶段:应用(准备几何数据、设置渲染状态)、几何处理(变换顶点、细分曲面、修改图元、变换投影)、光栅化阶段(将图元转换为片元)、像素处理阶段(计算像素颜色、混合)。
着色语言Shading Language
GPU编程语言,用于编写在渲染管线的特定阶段执行的程序。
光栅化
将几何图元(主要是三角形)转换为像素或片元的过程。
2. 渲染管线中的协作流程
- 顶点着色器处理顶点。每个顶点独立执行,输出裁剪坐标和插值数据。
- 图元装配与光栅化。顶点组装为图元(如三角形),再离散化为片元。
- 片元着色器上色。对每个片元计算颜色,后续通过深度测试、混合等操作决定最终像素。
3. 对比编程语言
和传统编程语言相比,着色语言在核心对象、核心操作、运行模型、资源管理等方面存在较大不同。
编程语言 | 着色语言 | |
---|---|---|
核心对象 | 原生数据类型和抽象数据结构 | 向量矩阵和纹理采样器 |
核心操作 | 修改状态、分支、循环和子程序调用 | 线性代数运算、插值和采样 |
运行模型 | 单一强处理器顺序执行 | 多个弱处理器并发执行 |
资源管理 | 运行时动态分配 | 编译时静态分配 |
代码风格 | 过程式 | 函数式 |
简单来说,编写着色语言是在定义一个(近似)线性变换。
4. GSLS 3.0着色语言
4.1. 示例
代码1 顶点着色器
#version 130 // 声明使用GLSL 3.0版本
// 输入属性(使用layout显式指定位置)
layout(location = 0) in vec3 aPosition; // 顶点位置(属性位置0)
layout(location = 1) in vec3 aNormal; // 顶点法线(属性位置1)
layout(location = 2) in vec2 aTexCoord; // 纹理坐标(属性位置2)
// 统一变量(由应用程序传入)
uniform mat4 uModel; // 模型矩阵(局部空间->世界空间)
uniform mat4 uView; // 视图矩阵(世界空间->观察空间)
uniform mat4 uProjection; // 投影矩阵(观察空间->裁剪空间)
// 输出到片元着色器的变量(会被插值)
out vec3 vWorldPos; // 世界空间顶点位置
out vec3 vNormal; // 世界空间法线向量
out vec2 vTexCoord; // 纹理坐标
void main()
{
// 计算完整的模型视图投影矩阵
mat4 mvp = uProjection * uView * uModel;
// 将顶点位置转换到裁剪空间(必须赋值给gl_Position)
gl_Position = mvp * vec4(aPosition, 1.0);
// 计算世界空间位置(用于光照计算)
vWorldPos = vec3(uModel * vec4(aPosition, 1.0));
// 转换法线到世界空间(注意使用法线矩阵)
vNormal = mat3(transpose(inverse(uModel))) * aNormal;
// 直接传递纹理坐标
vTexCoord = aTexCoord;
}
代码2 片元着色器
#version 130 // 声明使用GLSL 3.0版本
// 从顶点着色器输入的变量(经过插值)
in vec3 vWorldPos; // 插值后的世界空间位置
in vec3 vNormal; // 插值后的世界空间法线
in vec2 vTexCoord; // 插值后的纹理坐标
// 统一变量
uniform sampler2D uTexture; // 纹理采样器
uniform vec3 uLightPos; // 光源位置(世界空间)
uniform vec3 uLightColor; // 光源颜色
uniform vec3 uViewPos; // 摄像机位置(世界空间)
// 片段着色器输出(可输出到多个渲染目标)
layout(location = 0) out vec4 fragColor; // 主颜色输出(位置0)
void main()
{
// 从纹理采样基础颜色
vec4 texColor = texture(uTexture, vTexCoord);
// 丢弃透明度过低的片段
if(texColor.a < 0.1) discard;
// 归一化法线(插值后长度可能不为1)
vec3 norm = normalize(vNormal);
// 计算光照方向
vec3 lightDir = normalize(uLightPos - vWorldPos);
// 漫反射计算(兰伯特光照)
float diff = max(dot(norm, lightDir), 0.0);
vec3 diffuse = diff * uLightColor;
// 镜面反射计算(布林-冯光照)
vec3 viewDir = normalize(uViewPos - vWorldPos);
vec3 reflectDir = reflect(-lightDir, norm);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), 32.0);
vec3 specular = spec * uLightColor;
// 环境光(简单常量)
vec3 ambient = vec3(0.1);
// 组合所有光照分量
vec3 result = (ambient + diffuse + specular) * texColor.rgb;
// 输出最终颜色(RGBA)
fragColor = vec4(result, texColor.a);
}
4.2. 基本类型和结构体
标量 | bool | int | uint | floag |
double | ||||
矩阵 | vec2/3/4 | ivec2/3/4 | uvec2/3/4 | mat2/3/4 |
mat2x3 | ||||
不透明类型 | sampler2D | samplerCube | isampler2D |
代码3 结构体
struct Light {
vec3 position;
vec3 color;
float intensity;
};
Light mainLight;
4.3. 存储限定符
in | 输入 |
out | 输出 |
inout | 输入输出 |
uniform | 全局变量 |
const | 常量 |
precision | 精度 |
4.4. 流程控制
代码4 分支
if (condition) {
// 代码块
} else if (anotherCondition) {
// 代码块
} else {
// 代码块
}
switch(intValue) {
case 0:
// 代码
break;
default:
// 代码
}
代码5 循环语句
for (int i = 0; i < 10; i++) {
if (i == 5) continue;
// 代码
if (i == 8) break;
}
while (condition) {
// 代码
}
do {
// 代码
} while (condition);
4.5. 内置变量
变量 | 说明 | 类型 | 示例 |
---|---|---|---|
顶点着色器输入变量 | |||
gl_VertexID | 当前顶点索引 | int | int idx = gl_VertexID; |
gl_InstanceID | 实例化渲染中当前实例的索引 | int | int instanceIdx = gl_InstanceID; |
顶点着色器输出变量 | |||
gl_Position | 必须设置的顶点裁剪空间坐标 | vec4 | gl_Position = MVP * aPosition; |
gl_PointSize | 点大小 | float | gl_PointSize = 10.0; |
片段着色器输入变量 | |||
gl_FragCoord | 片元窗口坐标 | vec4 | vec2 pos = gl_FragCoord.xy; |
gl_FrontFacing | 片元朝向 | bool | if (gl_FrontFacing) {…} |
gl_PointCoord | 点内坐标 | vec2 | vec2 uv = gl_PointCoord; |
片段着色器输出变量 | |||
gl_FragColor | 片元颜色值 | vec4 | gl_FragColor = vec4(1,0,0,1); |
gl_FragDepth | 可修改的片元深度值 | float | gl_FragDepth = 0.5; |
fragColor | 自定义颜色输出变量 | vec4 | out vec4 fragColor; |
几何着色器变量 | |||
gl_PrimitiveIDIn | 输入图元的ID | int | int id = gl_PrimitiveIDIn; |
gl_Layer | 指定渲染目标层 | int | gl_Layer = 1; |
计算着色器变量 | |||
gl_GlobalInvocationID | 全局调用ID | uvec3 | uint idx = gl_GlobalInvocationID.x; |
gl_LocalInvocationID | 局部工作组内ID | uvec3 | uint localIdx = gl_LocalInvocationID.y; |
废弃变量 | |||
gl_Vertex | |||
gl_Normal | |||
gl_MultiTexCoord0-7 | |||
gl_ModelViewMatrix | |||
gl_FragData[] |
4.6. 内置函数
分类 | 函数 | 说明 | 示例 |
---|---|---|---|
数学函数 | trunc | 向零取整(去除小数部分) | trunc(2.7) = 2.0 trunc(-1.3) = -1.0 |
roundEven | 四舍五入到最接近的偶整数 | roundEven(2.5) = 2.0 roundEven(3.5) = 4.0 | |
sin | 正弦函数(参数为弧度) | sin(radians(90.0)) = 1.0 | |
cos | 余弦函数(参数为弧度) | cos(radians(180.0)) = -1.0 | |
tan | 正切函数(参数为弧度) | tan(radians(45.0)) ~= 1.0 | |
sqrt | 平方根 | sqrt(4.0) = 2.0 | |
inversesqrt | 平方根的倒数(比1.0/sqrt(x)更快) | inversesqrt(4.0) = 0.5 | |
pow | 幂运算(x的y次方) | pow(2.0, 3.0) = 8.0 | |
log | 自然对数(底数为e) | log(exp(2.0)) ~= 2.0 | |
radians | 角度转弧度 | radians(180.0) = 3.14159 | |
degrees | 弧度转角度 | degrees(3.14159) ~= 180.0 | |
fract | 获取小数部分(x - floor(x)) | fract(2.7) = 0.7 | |
mod | 取模运算(浮点数兼容) | mod(5.2, 3.0) = 2.2 | |
smoothstep | 埃尔米特插值(在edge0和edge1间平滑过渡) | smoothstep(0.0, 1.0, 0.5) = 0.5 | |
向量运算 | length | 计算向量长度(模) | length(vec3(0,3,4)) = 5.0 |
normalize | 单位化向量(方向不变,模长为1) | normalize(vec2(1,1)) ~= vec2(0.707,0.707) | |
dot | 点积(返回两向量夹角的cos值) | dot(vec3(1,0,0), vec3(0,1,0)) = 0.0 | |
cross | 叉积(仅支持vec3,返回垂直向量) | cross(vec3(1,0,0), vec3(0,1,0)) = vec3(0,0,1) | |
lessThan | 逐元素比较(<,返回bvec) | bvec2 res = lessThan(vec2(1,3), vec2(2,2)); | |
any/all | 检查向量元素是否存在/全部为真 | if(any(bvec2(true, false))) {…} | |
纹理函数 | texture | 通用纹理采样(替换旧版texture2D)[9](@ref) | texture(sampler2D, vec2(0.5,0.5)) |
textureSize | 获取纹理尺寸(返回整数向量) | ivec2 size = textureSize(sampler, 0) | |
texelFetch | 用整数坐标直接获取纹素(不进行滤波)[11](@ref) | texelFetch(sampler, ivec2(10,20), 0) | |
textureLod | 指定LOD层级的纹理采样(常用于计算着色器) | textureLod(sampler, coord, 1.2) | |
textureGrad | 指定梯度采样的纹理(避免自动计算LOD) | textureGrad(sampler, uv, ddx, ddy); | |
textureProj | 投影纹理采样(坐标自动除以w分量) | textureProj(sampler, vec3(uv, 1.0)); | |
textureOffset | 带偏移量的纹理采样(需常量偏移) | textureOffset(sampler, uv, ivec2(1,0)); | |
几何函数 | transpose | 矩阵转置(行列互换) | transpose(mat2(1,2,3,4)) = mat2(1,3,2,4) |
determinant | 矩阵行列式(用于判断可逆性) | determinant(mat2(1,0,0,1)) = 1.0 | |
inverse | 逆矩阵(要求行列式非零) | inverse(mat2(1,0,0,1)) = mat2(1,0,0,1) | |
distance | 计算两点间欧氏距离 | distance(vec2(0,0), vec2(3,4)) = 5.0 | |
reflect | 反射向量(I - 2·dot(N,I)·N) | reflect(vec3(1,0,0), vec3(0,1,0)) = vec3(-1,0,0) | |
refract | 折射向量(需提供折射率) | refract(I, N, 0.9) | |
导数函数 | dFdx | 屏幕空间x方向偏导数(仅片段着色器) | float dx = dFdx(uv.x); |
dFdy | 屏幕空间y方向偏导数(仅片段着色器) | float dy = dFdy(uv.y); | |
fwidth | 偏导数绝对值之和(abs(dFdx) + abs(dFdy)) | float edge = fwidth(color); | |
浮点处理 | frexp | 分解浮点数为尾数和指数(x = m * 2^e) | frexp(8.0, m, e); // m=0.5, e=4 |
ldexp | 重构浮点数(x = m * 2^e) | ldexp(0.5, 4) = 8.0 | |
整数函数 | floatBitsToInt | 浮点数位表示转整数(保留二进制不变) | int i = floatBitsToInt(1.0); |
intBitsToFloat | 整数位表示转浮点数 | float f = intBitsToFloat(0x3f800000); // =1.0 | |
矩阵函数 | outerProduct | 外积(生成矩阵M = colVec * rowVec) | mat2 M = outerProduct(vec2(1,2), vec2(3,4)); |
4.7. 预处理指令
#version 130
#define PI 3.141592653589793
#ifdef USE_TEXTURE
// 纹理相关代码
#else
// 后备代码
#endif
#extension GL_ARB_explicit_attrib_location : enable
5. 其他着色语言
着色语言 | 平台 | 语法 |
---|---|---|
GLSL,OpenGL Shading Language | OpenGL/OpenGL ES/WebGL | 类C语言 |
SPIR-V,Standard Portable Intermediate Representation | 跨平台中间字节码 | |
HLSL,High-Level Shading Language | 微软DirectX | 类C语言 |
Metal Shading Language | 苹果Metal框架 | 类C++ |
AGSL,Android Graphics Shading Language | Android 12+ | 类C |
SKSL,Skia Shading Language | Google Skia | GPU无关中间代码 |
ShaderLab | Unity | |
WEBSL,WebGPU Shading Language | WebGPU | 类TypeScript |
Slang | 多平台 |
6. 参考资料
https://registry.khronos.org/OpenGL/specs/es/3.0/GLSL_ES_Specification_3.00.pdf