把数值画出来
着色器同时在数百万个像素上运行,没法打断点,也没法打印。最可靠的调试手段,是把你怀疑的那个值直接输出成颜色。关键在于先把它映射到 0~1:
glsl
outColor = vec4(N * 0.5 + 0.5, 1.0); // 法线从 [-1,1] 映射到 [0,1]- 法线视图:红 = +x,绿 = +y,蓝 = +z。朝上的地面是绿色。如果出现颜色突变或全黑,说明法线没有归一化、没有变换,或者方向反了。
- UV 视图:红 = u,绿 = v,再叠一层棋盘格。拉伸、接缝、UV 翻转一眼就能看出。
- 线性深度:把视空间深度按 near/far 归一化,近白远黑。注意
gl_FragCoord.z是非线性的,直接显示几乎是一片白。 - 热力图:把标量映射成色带,并用醒目的颜色标出越界的值。本例中蓝色表示 < 0,红色表示 > 1。
N·L不做 clamp 时,背光面会立刻变蓝。
NaN 与 Inf
sqrt(-1.0)、0.0 / 0.0、对零向量做 normalize、pow(负数, 非整数) 都会产生 NaN。NaN 会“传染”:任何与它运算的结果都是 NaN,最后表现为黑块、白块或闪烁的像素,而且不同 GPU 的表现还不一样。用 isnan() / isinf() 把它们涂成品红色:
glsl
float risky = sqrt(vWorld.y - 0.3);
bool bad = isnan(risky) || isinf(risky);
c = bad ? vec3(1.0, 0.0, 1.0) : vec3(risky);修复方法通常是先 clamp 输入:sqrt(max(x, 0.0))、normalize(v + 1e-6)。
精度
precision mediump float 在许多移动 GPU 上只有约 16 位:大约 3 位有效数字,最大值约 65504。症状是带状色阶、远处坐标抖动、时间 uniform 运行久了动画开始卡顿。片元着色器里涉及位置和时间的计算,优先使用 highp。
读懂编译错误
信息日志的格式通常是 ERROR: 0:行号: 描述。注意行号是着色器源码中的行号,而且 three.js 会在前面插入大量前缀代码,所以要以日志里的描述为准。最常见的错误有:漏写分号、float a = 1;(GLSL 不做 int 到 float 的隐式转换)、向量维度不匹配,以及拼错变量名。用面板里的“Compile errors”逐个试一遍,看看控制台怎么报。
核心 API
isnanisinfgl.getShaderInfoLogprecision highpgl_FragCoordTHREE.ShaderMaterialrenderer.getContext动手试试
- 1切到 NaN finder,把 sqrt(vWorld.y - 0.3) 改成 sqrt(max(vWorld.y - 0.3, 0.0)),品红色就消失了。
- 2在 depth 视图里调整 near 和 far,看看深度精度如何分布。
- 3在顶点着色器里把 vNormal 改成 normal(不乘 modelMatrix),观察旋转时法线视图哪里出错。
还没有输出。console.log 的内容会显示在这里。
运行中