WebGL 着色器概述
着色器(Shader)是在 GPU 上运行的小程序,用于处理图形渲染的各个阶段。WebGL 使用 GLSL(OpenGL Shading Language)编写着色器代码。
着色器的类型
WebGL 主要有两种着色器:
- 顶点着色器(Vertex Shader)
- 片段着色器(Fragment Shader)
顶点着色器(Vertex Shader)
功能
- 处理每个顶点的数据
- 负责坐标变换
- 将 3D 坐标转换为裁剪空间坐标
输入
attribute:顶点属性(位置、颜色、法线、纹理坐标等)uniform:统一变量(变换矩阵、光照参数等)
输出
gl_Position:裁剪空间中的顶点位置(必需)varying:传递给片段着色器的插值数据
代码示例
glsl// 顶点着色器 attribute vec3 a_position; attribute vec3 a_color; attribute vec2 a_texCoord; uniform mat4 u_modelMatrix; uniform mat4 u_viewMatrix; uniform mat4 u_projectionMatrix; varying vec3 v_color; varying vec2 v_texCoord; void main() { // 坐标变换:模型空间 → 世界空间 → 相机空间 → 裁剪空间 mat4 mvp = u_projectionMatrix * u_viewMatrix * u_modelMatrix; gl_Position = mvp * vec4(a_position, 1.0); // 传递数据给片段着色器 v_color = a_color; v_texCoord = a_texCoord; }
片段着色器(Fragment Shader)
功能
- 处理每个片段(潜在的像素)
- 计算最终像素颜色
- 执行纹理采样、光照计算等
输入
varying:从顶点着色器插值而来的数据uniform:统一变量(纹理、材质参数等)
输出
gl_FragColor:最终像素颜色(WebGL 1.0)out vec4 fragColor:输出变量(WebGL 2.0)
代码示例
glsl// 片段着色器 precision mediump float; varying vec3 v_color; varying vec2 v_texCoord; uniform sampler2D u_texture; uniform float u_opacity; void main() { // 纹理采样 vec4 texColor = texture2D(u_texture, v_texCoord); // 混合顶点颜色和纹理颜色 vec3 finalColor = v_color * texColor.rgb; // 设置最终颜色 gl_FragColor = vec4(finalColor, texColor.a * u_opacity); }
顶点着色器 vs 片段着色器对比
| 特性 | 顶点着色器 | 片段着色器 |
|---|---|---|
| 执行频率 | 每个顶点执行一次 | 每个片段执行一次 |
| 主要任务 | 坐标变换 | 颜色计算 |
| 必需输出 | gl_Position | gl_FragColor |
| 典型操作 | 矩阵乘法、顶点光照 | 纹理采样、像素级光照 |
| 性能影响 | 顶点数量少时影响小 | 通常对性能影响更大 |
| 精度要求 | 通常使用 highp | 常用 mediump 优化性能 |
着色器变量类型
attribute(顶点着色器专用)
glslattribute vec3 a_position; // 顶点位置 attribute vec2 a_texCoord; // 纹理坐标 attribute vec3 a_normal; // 法线向量
uniform(两种着色器都可用)
glsluniform mat4 u_matrix; // 变换矩阵 uniform sampler2D u_texture; // 纹理采样器 uniform vec3 u_lightColor; // 光照颜色
varying(顶点→片段传递数据)
glsl// 顶点着色器 varying vec2 v_texCoord; // 片段着色器 varying vec2 v_texCoord; // 同名变量接收插值数据
着色器编译和链接流程
javascript// 1. 创建着色器对象 const vertexShader = gl.createShader(gl.VERTEX_SHADER); const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER); // 2. 设置源码 gl.shaderSource(vertexShader, vertexSource); gl.shaderSource(fragmentShader, fragmentSource); // 3. 编译着色器 gl.compileShader(vertexShader); gl.compileShader(fragmentShader); // 4. 创建程序对象 const program = gl.createProgram(); // 5. 附加着色器 gl.attachShader(program, vertexShader); gl.attachShader(program, fragmentShader); // 6. 链接着色器程序 gl.linkProgram(program); // 7. 使用程序 gl.useProgram(program);
性能优化建议
- 减少 varying 变量数量:减少顶点与片段着色器间的数据传输
- 在顶点着色器中计算:尽可能在顶点级别而非片段级别计算
- 使用适当精度:移动端使用
mediump或lowp - 避免分支语句:GPU 不擅长处理条件分支
- 预计算常量:将 uniforms 而非反复计算