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. 常见“黑屏”原因自查表
当你满怀期待刷新页面,却看到一片漆黑:
- 相机在物体里面吗?
- 尝试把
camera.position.z设大一点。
- 尝试把
- 物体在相机后面吗?
- 检查物体位置
(0,0,0)和相机朝向lookAt。
- 检查物体位置
- 光照计算为负数?
- 漫反射
dot(N, L)如果小于 0 就是黑的。 - 调试:直接输出
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0)。如果能看到红色剪影,说明几何体没问题,是光照逻辑挂了。
- 漫反射
- 纹理没加载完?
- 如果用了
texture2D,纹理可能是黑的。 - 调试:暂时把纹理颜色换成固定颜色
vec4 texColor = vec4(1.0)。
- 如果用了
- 变量名写错?
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 繁琐的配置" 分离开。先确算法是对的,再放进项目里。