{T}

着色器语言 GLSL

GLSL 简介

GLSL(OpenGL Shading Language)是用于编写 WebGL 着色器的高级编程语言,语法类似 C 语言,但增加了向量和矩阵等图形计算特性。

GLSL 版本

版本OpenGL ES 版本WebGL 版本说明
GLSL ES 1.0OpenGL ES 2.0WebGL 1.0基础版本
GLSL ES 3.0OpenGL ES 3.0WebGL 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);  // 纹理单元 0

varying 可变变量

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 matchedvarying 不匹配确保顶点和片段着色器中名称一致

最佳实践

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 语言,支持向量和矩阵运算
  • 变量限定符控制数据流:attributeuniformvarying
  • 丰富的内置函数简化图形计算
  • 预处理指令支持条件编译和宏定义
  • 合理选择精度,平衡性能和质量

学习建议

  1. 多实践:编写各种着色器,加深理解
  2. 参考示例:学习优秀的着色器代码
  3. 调试技巧:掌握着色器调试方法
  4. 性能优化:理解 GPU 架构,编写高效代码

下一步

继续学习几何图形绘制,实践着色器编程。