卡通渲染 (Toon Shading)
用 if-else 把连续的光照强度切成几个固定色阶,核心是这段代码:
下面拆解它。
卡通分级是什么
普通光照是连续渐变的——从最亮到最暗无缝过渡。卡通渲染把这个连续渐变"量化"成几个固定的亮度档位,就像只有几种灰度的印刷品。这样光影边界变得清晰硬朗,产生手绘或漫画的视觉风格。
代码里按 intensity = dot(normal, lightDir) 把亮度分成 5 个档位:0.2 / 0.4 / 0.6 / 0.8 / 1.0。
轮廓线怎么实现
轮廓线同样是视角技巧:
球体边缘处法线和视线垂直,dot(normal, viewDir) 接近 0,edge 接近 1。smoothstep 把这个值转成 0 到 1 的遮罩,thickness 控制轮廓粗细。最后用 mix(color, vec3(0.0), edgeFactor * 0.8) 把轮廓区域混合成黑色。
动画
光源方向随时间旋转:
阴影边界在球面上移动,你可以直观感受到分级的阴影是如何"跳变"的。
试着改一改
| 改动 | 效果 |
|---|---|
| 把 5 个档位改成 3 个 | 阴影更粗糙,更像简单漫画 |
outlineThickness 改为 0.2 | 轮廓线变细 |
edgeFactor * 0.8 改为 edgeFactor * 1.0 | 轮廓线变全黑 |
练习
练习区的 toonShading 函数已有 4 个档位(阈值略不同),轮廓线 thickness 也已设好,当前代码可以正常运行。尝试把 4 个档位的亮度系数(1.0, 0.7, 0.45, 0.25)改为(1.0, 0.6, 0.3, 0.1),观察阴影对比度的变化。
答案解析
toonShading 函数把 intensity = dot(normal, lightDir) 按阈值分级:
四个档位:高于 0.75 的区域最亮,低于 0.2 的区域最暗。阈值之间没有平滑过渡——这就是卡通效果的关键,边界是"跳"过去的,不是渐变的。
轮廓线 thickness 越大,黑边越粗;sin(u_time) * 0.08 让轮廓粗细轻微呼吸。
试着把最低档位的系数从 0.25 改成 0.0,看阴影区域会不会变成完全的黑色。