着色器语言 GLSL
GLSL 简介
GLSL(OpenGL Shading Language)是用于编写 WebGL 着色器的高级编程语言,语法类似 C 语言,但增加了向量和矩阵等图形计算特性。
GLSL 版本
| 版本 | OpenGL ES 版本 | WebGL 版本 | 说明 |
|---|---|---|---|
| GLSL ES 1.0 | OpenGL ES 2.0 | WebGL 1.0 | 基础版本 |
| GLSL ES 3.0 | OpenGL ES 3.0 | WebGL 2.0 | 增强版本 |
数据类型
标量类型
glsl
// 基本标量类型
float f = 1.0; // 浮点数(必须有小数点)
int i = 1; // 整数
bool b = true; // 布尔值向量类型
GLSL 提供了 2、3、4 维的向量和浮点数向量:
glsl
// 浮点向量
vec2 v2 = vec2(1.0, 2.0);
vec3 v3 = vec3(1.0, 2.0, 3.0);
vec4 v4 = vec4(1.0, 2.0, 3.0, 4.0);
// 整数向量
ivec2 iv2 = ivec2(1, 2);
ivec3 iv3 = ivec3(1, 2, 3);
ivec4 iv4 = ivec4(1, 2, 3, 4);
// 布尔向量
bvec2 bv2 = bvec2(true, false);
bvec3 bv3 = bvec3(true, false, true);
bvec4 bv4 = bvec4(true, false, true, false);向量构造
glsl
// 从标量构造
vec3 v1 = vec3(1.0); // (1.0, 1.0, 1.0)
// 从多个标量构造
vec3 v2 = vec3(1.0, 2.0, 3.0);
// 从向量构造
vec4 v3 = vec4(v2, 4.0); // (1.0, 2.0, 3.0, 4.0)
vec3 v4 = vec3(v3); // (1.0, 2.0, 3.0),截取前三个分量
// 混合构造
vec4 v5 = vec4(v2.xy, v2.z, 4.0);向量分量访问
GLSL 支持多种分量访问方式:
glsl
vec4 v = vec4(1.0, 2.0, 3.0, 4.0);
// 方式一:xyzw(位置坐标)
float x = v.x; // 1.0
float y = v.y; // 2.0
float z = v.z; // 3.0
float w = v.w; // 4.0
// 方式二:rgba(颜色)
float r = v.r; // 1.0
float g = v.g; // 2.0
float b = v.b; // 3.0
float a = v.a; // 4.0
// 方式三:stpq(纹理坐标)
float s = v.s; // 1.0
float t = v.t; // 2.0
float p = v.p; // 3.0
float q = v.q; // 4.0
// 重组(swizzling)
vec3 v1 = v.xyz; // (1.0, 2.0, 3.0)
vec3 v2 = v.rgb; // (1.0, 2.0, 3.0)
vec2 v3 = v.xy; // (1.0, 2.0)
vec3 v4 = v.xxx; // (1.0, 1.0, 1.0)
vec4 v5 = v.zyxw; // (3.0, 2.0, 1.0, 4.0)矩阵类型
glsl
// 浮点矩阵
mat2 m2; // 2x2 矩阵
mat3 m3; // 3x3 矩阵
mat4 m4; // 4x4 矩阵
// 非方阵(GLSL ES 3.0+)
mat2x3 m23; // 2列3行
mat3x2 m32; // 3列2行
mat2x4 m24; // 2列4行矩阵构造与访问
glsl
// 对角矩阵
mat3 m1 = mat3(1.0); // 单位矩阵
// 列优先构造(按列填充)
mat3 m2 = mat3(
1.0, 0.0, 0.0, // 第一列
0.0, 1.0, 0.0, // 第二列
0.0, 0.0, 1.0 // 第三列
);
// 从向量构造
vec3 col0 = vec3(1.0, 0.0, 0.0);
vec3 col1 = vec3(0.0, 1.0, 0.0);
vec3 col2 = vec3(0.0, 0.0, 1.0);
mat3 m3 = mat3(col0, col1, col2);
// 访问列
vec3 firstColumn = m2[0]; // 第一列
// 访问元素
float element = m2[0][0]; // 第一行第一列
float element = m2[0].x; // 等价写法采样器类型
采样器用于纹理采样:
glsl
// 基本采样器
sampler2D tex2D; // 2D 纹理采样器
samplerCube texCube; // 立方体纹理采样器
// WebGL 2.0 新增
sampler3D tex3D; // 3D 纹理采样器
sampler2DShadow shadow2D; // 深度纹理采样器
isampler2D itex2D; // 整数纹理采样器
usampler2D utex2D; // 无符号整数纹理采样器变量限定符
GLSL 提供多种变量限定符,控制变量的存储和行为。
存储限定符
| 限定符 | 说明 | 使用场景 |
|---|---|---|
const | 常量,不可修改 | 固定值、配置参数 |
attribute | 顶点属性变量 | 顶点着色器输入(WebGL 1.0) |
uniform | 全局一致变量 | 变换矩阵、光照参数 |
varying | 传递变量 | 顶点到片段着色器(WebGL 1.0) |
in | 输入变量 | WebGL 2.0 替代 attribute/varying |
out | 输出变量 | WebGL 2.0 替代 varying |
const 常量
glsl
const float PI = 3.14159265359;
const vec3 LIGHT_COLOR = vec3(1.0, 1.0, 1.0);
const int MAX_LIGHTS = 8;
// 编译时常量
const int array[3] = int[3](1, 2, 3);attribute 属性变量
glsl
// 顶点着色器中定义
attribute vec3 a_Position; // 顶点位置
attribute vec3 a_Normal; // 法线
attribute vec2 a_TexCoord; // 纹理坐标
attribute vec4 a_Color; // 顶点颜色
// JavaScript 中传递数据
const positionLoc = gl.getAttribLocation(program, 'a_Position');
gl.vertexAttribPointer(positionLoc, 3, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(positionLoc);uniform 一致变量
glsl
// 着色器中定义
uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ProjectionMatrix;
uniform vec3 u_LightPosition;
uniform sampler2D u_Texture;
// JavaScript 中传递数据
const modelMatrixLoc = gl.getUniformLocation(program, 'u_ModelMatrix');
gl.uniformMatrix4fv(modelMatrixLoc, false, modelMatrix);
const lightPosLoc = gl.getUniformLocation(program, 'u_LightPosition');
gl.uniform3fv(lightPosLoc, lightPosition);
const textureLoc = gl.getUniformLocation(program, 'u_Texture');
gl.uniform1i(textureLoc, 0); // 纹理单元 0varying 可变变量
glsl
// 顶点着色器
varying vec3 v_Color;
varying vec2 v_TexCoord;
varying vec3 v_Normal;
void main() {
v_Color = a_Color;
v_TexCoord = a_TexCoord;
v_Normal = a_Normal;
}
// 片段着色器
varying vec3 v_Color;
varying vec2 v_TexCoord;
varying vec3 v_Normal;
void main() {
gl_FragColor = vec4(v_Color, 1.0);
}WebGL 2.0 的 in/out
glsl
// 顶点着色器
#version 300 es
in vec3 a_Position;
in vec2 a_TexCoord;
out vec2 v_TexCoord;
out vec3 v_Normal;
void main() {
gl_Position = vec4(a_Position, 1.0);
v_TexCoord = a_TexCoord;
}
// 片段着色器
#version 300 es
precision highp float;
in vec2 v_TexCoord;
in vec3 v_Normal;
out vec4 fragColor;
void main() {
fragColor = vec4(v_TexCoord, 0.0, 1.0);
}精度限定符
glsl
// 设置默认精度
precision highp float;
precision mediump int;
// 单个变量精度
lowp vec4 color; // 低精度颜色
mediump vec2 texCoord; // 中等精度纹理坐标
highp vec3 position; // 高精度位置
// 精度范围
// lowp: -2^8 ~ 2^8
// mediump: -2^10 ~ 2^10
// highp: -2^16 ~ 2^16精度选择建议
| 变量类型 | 推荐精度 | 原因 |
|---|---|---|
| 顶点位置 | highp | 需要精确的空间计算 |
| 纹理坐标 | mediump | 中等精度足够 |
| 颜色 | lowp | 颜色精度要求低 |
| 法线 | mediump | 中等精度即可 |
| 深度值 | highp | 深度精度影响遮挡 |
运算符
算术运算符
glsl
// 标量运算
float a = 10.0;
float b = 5.0;
float c = a + b; // 15.0
float d = a - b; // 5.0
float e = a * b; // 50.0
float f = a / b; // 2.0
// 向量运算
vec3 v1 = vec3(1.0, 2.0, 3.0);
vec3 v2 = vec3(4.0, 5.0, 6.0);
vec3 v3 = v1 + v2; // (5.0, 7.0, 9.0)
vec3 v4 = v1 * 2.0; // (2.0, 4.0, 6.0)
vec3 v5 = v1 * v2; // 分量乘法 (4.0, 10.0, 18.0)
// 矩阵运算
mat3 m1 = mat3(1.0);
mat3 m2 = mat3(2.0);
mat3 m3 = m1 + m2; // 矩阵加法
mat3 m4 = m1 * m2; // 矩阵乘法
vec3 v6 = m1 * v1; // 矩阵向量乘法关系运算符
glsl
// 标量比较
bool b1 = 1.0 < 2.0; // true
bool b2 = 1.0 > 2.0; // false
bool b3 = 1.0 <= 2.0; // true
bool b4 = 1.0 >= 2.0; // false
bool b5 = 1.0 == 1.0; // true
bool b6 = 1.0 != 2.0; // true
// 向量比较(返回布尔向量)
vec3 v1 = vec3(1.0, 2.0, 3.0);
vec3 v2 = vec3(1.0, 2.0, 4.0);
bvec3 bv = lessThan(v1, v2); // (false, false, true)逻辑运算符
glsl
bool a = true;
bool b = false;
bool c = a && b; // 逻辑与
bool d = a || b; // 逻辑或
bool e = !a; // 逻辑非控制流
条件语句
glsl
// if-else
if (a > 0.0) {
// 条件为真
} else if (a < 0.0) {
// 条件为假
} else {
// 等于零
}
// 三元运算符
float result = a > 0.0 ? a : -a;循环语句
glsl
// for 循环
for (int i = 0; i < 10; i++) {
// 循环体
}
// while 循环
int i = 0;
while (i < 10) {
i++;
}
// do-while 循环
int i = 0;
do {
i++;
} while (i < 10);
// 循环控制
for (int i = 0; i < 10; i++) {
if (i == 5) continue; // 跳过当前迭代
if (i == 8) break; // 跳出循环
}注意事项
glsl
// ❌ 错误:循环条件不能使用 uniform
uniform int u_MaxIterations;
for (int i = 0; i < u_MaxIterations; i++) { } // 编译错误
// ✅ 正确:使用常量或固定循环次数
const int MAX_ITER = 10;
for (int i = 0; i < MAX_ITER; i++) { }
// ✅ WebGL 2.0 允许动态循环
#version 300 es
uniform int u_MaxIterations;
for (int i = 0; i < u_MaxIterations; i++) { }函数
函数定义
glsl
// 基本函数
float add(float a, float b) {
return a + b;
}
// 向量参数
vec3 add(vec3 a, vec3 b) {
return a + b;
}
// 无返回值
void main() {
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}参数限定符
| 限定符 | 说明 |
|---|---|
in | 输入参数(默认),只读 |
out | 输出参数,只写 |
inout | 输入输出参数,可读写 |
glsl
// in 参数(默认)
float square(in float x) {
return x * x;
}
// out 参数
void getValues(out float a, out float b) {
a = 1.0;
b = 2.0;
}
// inout 参数
void swap(inout float a, inout float b) {
float temp = a;
a = b;
b = temp;
}
// 使用示例
float x = 2.0;
float y = square(x); // y = 4.0
float a, b;
getValues(a, b); // a = 1.0, b = 2.0
float m = 1.0, n = 2.0;
swap(m, n); // m = 2.0, n = 1.0递归
GLSL 不支持递归:
glsl
// ❌ 错误:不支持递归
int factorial(int n) {
if (n <= 1) return 1;
return n * factorial(n - 1); // 编译错误
}
// ✅ 使用循环代替
int factorial(int n) {
int result = 1;
for (int i = 2; i <= n; i++) {
result *= i;
}
return result;
}内置函数
三角函数
glsl
float angle = 0.5;
float sinValue = sin(angle); // 正弦
float cosValue = cos(angle); // 余弦
float tanValue = tan(angle); // 正切
float asinValue = asin(sinValue); // 反正弦
float acosValue = acos(cosValue); // 反余弦
float atanValue = atan(tanValue); // 反正切
float sinhValue = sinh(angle); // 双曲正弦
float coshValue = cosh(angle); // 双曲余弦
float tanhValue = tanh(angle); // 双曲正切指数函数
glsl
float x = 2.0;
float p = pow(x, 3.0); // x^3 = 8.0
float e = exp(x); // e^x
float l = log(x); // ln(x)
float l2 = log2(x); // log2(x)
float s = sqrt(x); // √x
float i = inversesqrt(x); // 1/√x常用数学函数
glsl
float x = -2.5;
float a = abs(x); // 绝对值:2.5
float s = sign(x); // 符号:-1.0
float f = floor(x); // 向下取整:-3.0
float c = ceil(x); // 向上取整:-2.0
float r = fract(x); // 小数部分:0.5
float m = mod(x, 2.0); // 取模:1.5
float mn = min(1.0, 2.0); // 最小值:1.0
float mx = max(1.0, 2.0); // 最大值:2.0
float cl = clamp(x, -1.0, 1.0); // 限制范围:-1.0
float mi = mix(0.0, 1.0, 0.5); // 线性插值:0.5
float st = step(0.0, x); // 阶梯函数:0.0 或 1.0
float sm = smoothstep(0.0, 1.0, 0.5); // 平滑阶梯:0.5向量函数
glsl
vec3 v1 = vec3(1.0, 2.0, 3.0);
vec3 v2 = vec3(4.0, 5.0, 6.0);
float len = length(v1); // 向量长度
float dist = distance(v1, v2); // 向量距离
float dot = dot(v1, v2); // 点积
vec3 cross = cross(v1, v2); // 叉积
vec3 norm = normalize(v1); // 归一化
vec3 refl = reflect(v1, norm); // 反射
vec3 refr = refract(v1, norm, 1.5); // 折射
vec3 face = faceforward(v1, v2, norm); // 面向正向矩阵函数
glsl
mat3 m = mat3(1.0);
mat3 t = transpose(m); // 转置
// mat3 inv = inverse(m); // 逆矩阵(WebGL 2.0)纹理采样函数
glsl
uniform sampler2D u_Texture;
varying vec2 v_TexCoord;
// 基本采样
vec4 color = texture2D(u_Texture, v_TexCoord);
// WebGL 2.0
vec4 color = texture(u_Texture, v_TexCoord);
// 带偏移的采样
vec4 color = texture2D(u_Texture, v_TexCoord, lodBias);
// 投影纹理采样
vec4 color = texture2DProj(u_Texture, vec3(v_TexCoord, 1.0));
// 立方体纹理
uniform samplerCube u_CubeMap;
varying vec3 v_Reflection;
vec4 color = textureCube(u_CubeMap, v_Reflection);结构体与数组
结构体
glsl
// 定义结构体
struct Light {
vec3 position;
vec3 color;
float intensity;
};
// 声明和初始化
Light light;
light.position = vec3(0.0, 1.0, 0.0);
light.color = vec3(1.0, 1.0, 1.0);
light.intensity = 1.0;
// 构造函数初始化
Light light2 = Light(
vec3(0.0, 1.0, 0.0),
vec3(1.0, 1.0, 1.0),
1.0
);
// 访问成员
vec3 pos = light.position;
float inten = light.intensity;数组
glsl
// 固定大小数组
float arr1[3];
float arr2[] = float[3](1.0, 2.0, 3.0);
// 访问元素
float value = arr2[0];
// 向量数组
vec3 positions[4];
positions[0] = vec3(0.0, 0.0, 0.0);
// 结构体数组
Light lights[8];
lights[0].position = vec3(0.0, 1.0, 0.0);预处理指令
GLSL 支持预处理指令,在编译前处理代码。
条件编译
glsl
// 定义宏
#define PI 3.14159265359
#define MAX_LIGHTS 8
// 条件编译
#ifdef USE_TEXTURE
uniform sampler2D u_Texture;
varying vec2 v_TexCoord;
#endif
#ifndef USE_TEXTURE
varying vec4 v_Color;
#endif
// 条件表达式
#if MAX_LIGHTS > 4
// 多光源处理
#elif MAX_LIGHTS > 0
// 少量光源
#else
// 无光源
#endif
// 取消定义
#undef MAX_LIGHTS宏函数
glsl
#define SQUARE(x) ((x) * (x))
#define MAX(a, b) ((a) > (b) ? (a) : (b))
float result = SQUARE(2.0); // 4.0
float maxValue = MAX(1.0, 2.0); // 2.0内置宏
glsl
// GLSL 版本
#if __VERSION__ == 100
// WebGL 1.0 / GLSL ES 1.0
#elif __VERSION__ == 300
// WebGL 2.0 / GLSL ES 3.0
#endif
// 行号和文件名
int line = __LINE__;
string file = __FILE__;着色器编译与调试
编译着色器
javascript
function compileShader(gl, type, source) {
const shader = gl.createShader(type);
gl.shaderSource(shader, source);
gl.compileShader(shader);
// 检查编译状态
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
const info = gl.getShaderInfoLog(shader);
console.error('着色器编译错误:\n', info);
// 显示错误行号
const lines = source.split('\n');
info.match(/ERROR: \d+:(\d+):/)?.forEach(match => {
const lineNum = parseInt(match[1]);
console.error(`行 ${lineNum}: ${lines[lineNum - 1]}`);
});
gl.deleteShader(shader);
return null;
}
return shader;
}链接程序
javascript
function linkProgram(gl, vertexShader, fragmentShader) {
const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
// 检查链接状态
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
const info = gl.getProgramInfoLog(program);
console.error('程序链接错误:\n', info);
gl.deleteProgram(program);
return null;
}
// 验证程序
gl.validateProgram(program);
if (!gl.getProgramParameter(program, gl.VALIDATE_STATUS)) {
console.error('程序验证失败:\n', gl.getProgramInfoLog(program));
}
return program;
}调试技巧
可视化调试
glsl
// 在片段着色器中输出调试信息
void main() {
// 输出顶点位置作为颜色
gl_FragColor = vec4(v_Position, 1.0);
// 输出法线
gl_FragColor = vec4(v_Normal * 0.5 + 0.5, 1.0);
// 输出纹理坐标
gl_FragColor = vec4(v_TexCoord, 0.0, 1.0);
// 输出深度值
float depth = gl_FragCoord.z;
gl_FragColor = vec4(vec3(depth), 1.0);
}错误处理辅助函数
javascript
/**
* 创建完整的着色器程序
*/
function createShaderProgram(gl, vsSource, fsSource) {
// 编译顶点着色器
const vs = compileShader(gl, gl.VERTEX_SHADER, vsSource);
if (!vs) return null;
// 编译片段着色器
const fs = compileShader(gl, gl.FRAGMENT_SHADER, fsSource);
if (!fs) {
gl.deleteShader(vs);
return null;
}
// 链接程序
const program = linkProgram(gl, vs, fs);
// 清理着色器对象
gl.deleteShader(vs);
gl.deleteShader(fs);
return program;
}
/**
* 查询着色器变量信息
*/
function queryShaderVariables(gl, program) {
const info = {
attributes: [],
uniforms: []
};
// 获取 attribute 变量数量
const numAttribs = gl.getProgramParameter(program, gl.ACTIVE_ATTRIBUTES);
for (let i = 0; i < numAttribs; i++) {
const attrib = gl.getActiveAttrib(program, i);
info.attributes.push({
name: attrib.name,
type: attrib.type,
size: attrib.size
});
}
// 获取 uniform 变量数量
const numUniforms = gl.getProgramParameter(program, gl.ACTIVE_UNIFORMS);
for (let i = 0; i < numUniforms; i++) {
const uniform = gl.getActiveUniform(program, i);
info.uniforms.push({
name: uniform.name,
type: uniform.type,
size: uniform.size
});
}
return info;
}常见错误及解决方法
| 错误信息 | 原因 | 解决方法 |
|---|---|---|
ERROR: 0:1: '' : syntax error | 语法错误 | 检查拼写、分号 |
undefined variable | 变量未定义 | 添加变量声明 |
type mismatch | 类型不匹配 | 确保运算类型一致 |
redefinition | 变量重定义 | 避免重复声明 |
varying not matched | varying 不匹配 | 确保顶点和片段着色器中名称一致 |
最佳实践
1. 精度选择
glsl
// 顶点着色器:默认 highp
precision highp float;
// 片段着色器:根据需求选择
precision mediump float; // 推荐:平衡性能和质量
// precision highp float; // 需要高精度时
// precision lowp float; // 仅颜色计算时2. 常量定义
glsl
// ✅ 使用常量
const float PI = 3.14159265359;
const vec3 LIGHT_DIR = normalize(vec3(1.0, 1.0, 1.0));
// ❌ 避免在着色器中硬编码
float angle = 3.14159265359;3. 向量化计算
glsl
// ✅ 向量化计算(高效)
vec3 result = a + b;
// ❌ 分量逐个计算(低效)
vec3 result;
result.x = a.x + b.x;
result.y = a.y + b.y;
result.z = a.z + b.z;4. 避免分支
glsl
// ❌ 分支语句(可能影响性能)
if (x > 0.0) {
result = x;
} else {
result = -x;
}
// ✅ 使用内置函数
result = abs(x);
// ❌ 条件判断
if (a > b) {
result = a;
} else {
result = b;
}
// ✅ 使用内置函数
result = max(a, b);5. 着色器代码组织
glsl
// 推荐:将通用代码放入头文件
// common.glsl
#ifndef COMMON_GLSL
#define COMMON_GLSL
const float PI = 3.14159265359;
vec3 packNormal(vec3 normal) {
return normal * 0.5 + 0.5;
}
vec3 unpackNormal(vec3 packed) {
return packed * 2.0 - 1.0;
}
#endif小结
关键要点
- GLSL 是类 C 语言,支持向量和矩阵运算
- 变量限定符控制数据流:
attribute、uniform、varying - 丰富的内置函数简化图形计算
- 预处理指令支持条件编译和宏定义
- 合理选择精度,平衡性能和质量
学习建议
- 多实践:编写各种着色器,加深理解
- 参考示例:学习优秀的着色器代码
- 调试技巧:掌握着色器调试方法
- 性能优化:理解 GPU 架构,编写高效代码
下一步
继续学习几何图形绘制,实践着色器编程。