第 13 课:Shader 调试与日常问题排查

返回教程目录 · 打开本课练习 · 复制调试清单

让不可见的中间值可见

Shader 没有普通 JavaScript 的 console.log。最直接的调试法是把片元中的数值转换成颜色:vec4(vec3(x),1.0) 把 0–1 的 float 变成灰度,vec4(uv,0.0,1.0) 把 UV 的两个分量映射到红绿通道。黑、白以及平滑过渡对应输入数值,不是任意装饰。若变量可能超出 0–1,要先定义可视化范围,例如 (x-low)/(high-low) 后 clamp;否则大片纯黑或纯白只能说明数值被截断,不能显示内部结构。

前四题依次画出 uv.x、uv、平滑噪声和 step(0.5,n) 的结果。阈值题的白区面积变化能帮助确认数值范围与条件方向。顶点 Shader 的 uv 来自几何体 attribute,经 varying 在三角形内插值;片元 Shader 读到的是插值后的值。调试顶点形变时,必须辨清是局部位置、观察空间位置还是屏幕 UV,不能把不同空间的值混在同一颜色判断。

从编译错误到具体行

编译日志通常包含阶段、行号与诊断信息,例如 undeclared identifier、类型不匹配或重载函数找不到。行号可能对应 Three.js 注入后的完整源码,而非 shader.frag 的直接行号,因此要同时看完整编译源码与附近代码。第 5、6 题用独立 WebGL2 context 编译 bad.frag 并把真实日志显示在页面;主画布继续渲染有效 Shader。修复 bad.frag 后可重复点击验证,不需要故意把整个练习页面弄坏。

Three.js 层先确认 material 实际被绘制、uniform 名字与类型一致、纹理异步加载已完成、renderer 的颜色空间和 tone mapping 符合预期。material.onBeforeCompile 适合检查 Three.js 内建材质改写后的源码;需要排除自动注入时可用 RawShaderMaterial,但必须自行写完整 GLSL 声明。复杂问题用 Spector.js 抓帧,检查每个 draw call 的程序、uniform、纹理和 framebuffer。浏览器性能面板用于分析 JS 与渲染流程;工具名称和功能会随浏览器版本变化,故以当前可用工具为准。

二分定位与颜色编码

全黑时先用常量亮色输出,确认几何体与材质能够绘制;然后依次输出输入 UV、中间距离、遮罩和最终颜色。第 7 题故意把阈值设到永远达不到的范围,形成全黑;切换模式后依次看到距离灰度和修复后的圆形。这样能判断错误出在采样/传值、数学范围,还是最后的颜色组合。复杂表达式应拆成临时变量,不要在一行里猜哪一步出了问题。

多分支 Shader 可以给每个分支固定颜色,例如红、绿、蓝;画面哪部分进入哪条路径一眼可见。第 9 题演示三段颜色编码。第 10 题封装 debugFloat(value,low,high),显式处理零宽范围并归一化输出;它适合临时调试模式,发布时关闭或剔除调试路径。

按症状排查

全黑或全白:先输出常量,查编译、几何体、材质、uniform 和数值范围,再看除零、pow/sqrt 的定义域。闪烁或撕裂:区分相机深度冲突、时间精度、UV 不连续、逻辑分支与资源更新;第 8 题用大时间输入与折返时间对照。边缘锯齿:先检查实际像素比与抗锯齿,再考虑 smoothstep/fwidth 过渡。偏色:检查颜色纹理 sRGB 标记、数据纹理无颜色空间、输出转换和 tone mapping;第 12 题把同一图片以两种解释并排显示。卡顿:固定条件记录帧间隔、画布像素、draw call、纹理采样与 overdraw,必要时用 GPU 计时工具;第 11 题演示 draw call 计数,不能将它当作 GPU 时间。

资源与可复现记录

JavaScript 负责创建 ShaderMaterial、uniform 和纹理;顶点 Shader 变换属性,varying 经光栅化插值后交给片元 Shader。故障可能出现在链路任何一段。每次诊断记录设备、浏览器、页面 URL、源码版本、截图、控制台日志、复现步骤与修复前后的对照。使用 调试检查清单 可避免遗漏。练习默认保持可运行;故障仅由明确的按钮或模式触发。

练习

难度 题目 可观察验收
⭐ 把 uv.x 画成灰度 左黑右白
⭐ 把 UV 画进红绿通道 左下黑、右上黄
⭐⭐ 噪声中间值的灰度视图 噪声结构可见
⭐⭐ 用 step 标出大于 0.5 的区域 黑白分明
⭐⭐ 读取未声明变量的编译日志 真实日志与错误行
⭐⭐⭐ 读取类型不匹配的编译日志 定位类型错误
⭐⭐⭐ 用三步二分定位全黑画面 全黑、灰度、修复图可切换
⭐⭐⭐ 区分时间精度与逻辑闪烁 时间对照可见
⭐⭐⭐ 用颜色编码每个分支 三分支颜色不同
⭐⭐⭐⭐ 可复用的 debugFloat 函数 多变量灰度可切换
⭐⭐⭐⭐ 记录 draw call 并用 Spector 复核 面板与抓帧一致
⭐⭐⭐⭐ 定位 sRGB 与 Linear 偏色 两种颜色空间对照