Skip to main content

14. 调试技巧:像手术刀一样调试 Shader

写 Shader 最痛苦的是什么? 没有 console.log

当你的屏幕一片漆黑,或者物体变成了奇怪的颜色,你不知道是数据没传进去,还是算法写错了。 本章教你如何用"可视化"的方法来调试 GLSL。


1. 原则:把一切都变成颜色

Shader 的唯一输出就是颜色。所以,调试的核心思路是: 把你想检查的数据,映射到 RGB 颜色上。

1.1 检查 UV 坐标

最常用的起手式。如果物体黑了,先看看 UV 对不对。

void main() {
// x 轴是红的,y 轴是绿的
gl_FragColor = vec4(vUv.x, vUv.y, 0.0, 1.0);
}
  • 预期结果:你应该看到一个平滑的红绿渐变。
  • 如果全黑vUv 数据没传进来,或者全是 0。
  • 如果全黄 (1,1,0)vUv 可能全是 1。

1.2 检查法线 (Normals)

法线是光照计算错误的主要原因。

void main() {
// 法线范围通常是 -1.0 到 1.0
// 颜色范围是 0.0 到 1.0
// 所以需要映射:normal * 0.5 + 0.5
vec3 debugNormal = vNormal * 0.5 + 0.5;

gl_FragColor = vec4(debugNormal, 1.0);
}
  • 预期结果
    • 朝上的面是浅绿色 (0, 1, 0) -> 映射后 (0.5, 1.0, 0.5)。
    • 朝右的面是浅红色。
    • 朝前的面是浅蓝色。
  • 故障排查:如果颜色在随着物体旋转而剧烈闪烁,或者颜色不平滑,说明法线变换矩阵 (normalMatrix) 可能有问题,或者顶点法线数据本身就是乱的。

1.3 检查数值范围 (二值化)

有时候你想知道一个变量 val 到底是大是小。

void main() {
float val = dot(N, L); // 比如检查光照因子

// 如果 val > 0.5 显示红色,否则显示蓝色
vec3 debugColor = mix(vec3(0,0,1), vec3(1,0,0), step(0.5, val));

gl_FragColor = vec4(debugColor, 1.0);
}

2. 常见“黑屏”原因自查表

当你满怀期待刷新页面,却看到一片漆黑:

  1. 相机在物体里面吗?
    • 尝试把 camera.position.z 设大一点。
  2. 物体在相机后面吗?
    • 检查物体位置 (0,0,0) 和相机朝向 lookAt
  3. 光照计算为负数?
    • 漫反射 dot(N, L) 如果小于 0 就是黑的。
    • 调试:直接输出 gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0)。如果能看到红色剪影,说明几何体没问题,是光照逻辑挂了。
  4. 纹理没加载完?
    • 如果用了 texture2D,纹理可能是黑的。
    • 调试:暂时把纹理颜色换成固定颜色 vec4 texColor = vec4(1.0)
  5. 变量名写错?
    • uniform 变量如果在 Shader 里被使用,但 JS 里没传,通常默认是 0。
    • 注意:如果 Shader 里声明了 uniform没用到,GPU 编译器可能会优化掉它,导致你在 JS 里调试 gl.getUniformLocation 找不到它(这不是 bug,是特性)。

3. 终极调试法:glsl-canvas 插件

如果你在 VS Code 里写 Shader,强烈推荐安装 "Shader languages support for VS Code""glsl-canvas" 插件。

  • glsl-canvas 允许你打开一个 GLSL 文件,右键 "Show glslCanvas",直接在一个小窗口里预览 Shader 效果(它会自动提供 u_time, u_mouse 等变量)。
  • 这能帮你把 "算法逻辑验证""WebGL 繁琐的配置" 分离开。先确算法是对的,再放进项目里。