NPR材质与分层光照
全库目录221
本文目录22
把NdotL分成亮暗两档,很快能得到卡通明暗,但多个光源、阴影和不同材质一起出现时,分界未必还是想要的形状。NPR材质先定义明暗坐标和区域规则,再决定颜色、高光与遮挡怎样组合。每项遮罩能单独显示,才能知道风格来自哪份数据。
风格参数
同一套 Toon 着色器可能需要表达:
- 明暗分几层,边界是硬切还是软过渡;
- 阴影颜色是否受材质、环境和时间影响;
- 皮肤、布料、金属、头发是否使用不同层级;
- 主光、附加光、环境光和阴影如何组合;
- 角色和场景是否共享同一颜色 Script;
- 描边、Bloom、Fog 和色调映射是否改变色块。
这些规则应成为美术可调参数和资产规范。只给每个材质自由调 Ramp,最终会产生互不一致的角色和场景。
明暗坐标与颜色层
明暗坐标
Lambert 漫反射使用:
Toon 着色器可以对 做硬阈值:,再用 在暗色和亮色间插值。
硬 step 在低分辨率、法线变化和动画中容易闪烁。常用 smoothstep(t-w,t+w,x) 保留窄过渡带,过渡宽度还可结合 fwidth(x) 做屏幕空间抗锯齿。
明暗坐标也不一定直接使用 。可先加入:
- 阴影 Attenuation;
- AO 或材质遮罩;
- 光源 Wrap;
- Artist-authored Threshold 偏移;
- 角色局部空间方向修正。
运算顺序会改变含义。把实时阴影乘进 后再 Ramp,与先 Ramp 再乘阴影不是同一个结果。
例如明暗坐标为 0.8、阴影可见性为 0.5、阈值为 0.6,先相乘再分层得到 0.4,进入暗层;先按 0.8 分层则进入亮层,再乘 0.5 只降低亮层强度。两者可能都可用,但表达的是不同规则。调试视图分别显示比较前的坐标和选中的层,才能判断变化来自阴影输入还是调色结果。
Ramp 纹理
Ramp 用明暗坐标采样一维或二维 LUT。与写死 step 相比,它能直接控制层数、颜色和过渡宽度。
常见布局:
- U 表示明暗坐标;
- V 表示材质区域、时间段或风格 Variant;
- 不同角色共享 Ramp 图集的行;
- Base Map/ID Map 决定采用哪一行。
导入时要注意:
- 作为颜色 Ramp 时确认 sRGB 语义;
- 禁止无意的 Wrap;
- 小 LUT 的双线性过滤与 Mipmap 可能污染相邻行;
- 阶梯边界需要 Point/Linear 与抗锯齿策略配合;
- 压缩会在硬边产生色块和泄漏。
Ramp 分层采样
输入法线和光方向处于同一空间。halfLambert 把 映射到 ,材质行选择一条 Ramp:
float ndotl = dot(normalize(normalWS), normalize(lightDirWS));
float halfLambert = ndotl * 0.5 + 0.5;
float2 rampUv = float2(saturate(halfLambert), materialRowUv);
float3 ramp = LightRamp.SampleLevel(PointClamp, rampUv, 0).rgb;
float3 shadow = lerp(shadowTint, float3(1.0, 1.0, 1.0), visibility);
float3 litColor = baseColor * ramp * shadow;点采样适合保留硬分层,线性采样适合平滑过渡;两者都要关闭不期望的 Mip 混合或准备专用 Ramp Mip。把场景阴影直接乘黑会丢掉阴影层的艺术控制,示例用 shadowTint 保留色相。验证时分别显示 halfLambert、Ramp 和可见性,避免把法线、Ramp 与阴影贴图问题混在一起。
分层颜色
设两个明暗边界分别为a、b,且a<b。先计算u=smoothstep(a-wa,a+wa,x)、v=smoothstep(b-wb,b+wb,x),并选择宽度使整个区间内u>=v。二级阴影权重是1-u,一级阴影为u-v,亮部为v,三者均非负且和为一。若两个阈值顺序或软带宽度使u<v,中间层会出现负权重,结果可能越出色板范围。
一种常见结构是把基础色拆为亮部、一级阴影和二级阴影:
由 Ramp、阴影贴图、AO 和区域遮罩决定,且和为 1。
阴影色不应简单乘黑。可使用暖冷偏移、固定色板或感知颜色空间调整,但最终要在项目输出颜色空间与色调映射后检查。
多层边界应有明确优先级。例如实时阴影贴图应当能把亮部压入阴影层,但 AO 不应把整个材质无限变暗。
材质区域与高光
材质ID与区域控制
角色常用 ID Map 或通道打包区分皮肤、布料、金属、装饰和发光区域。每类区域可选择:
- Ramp 行与阈值;
- Specular 形状与颜色;
- Rim 光源;
- 阴影 Tint;
- Outline 宽度;
- SSS/MatCap/Emission 等附加项。
ID 边界会经过纹理过滤。若希望离散分类,应使用合适的通道值、边缘扩展和阈值,避免 Mipmap 后类别混合。
风格化高光
连续 PBR 高光常与大色块冲突。可以保留 或微表面 NDF 作为形状基础,再对结果做阈值或 Ramp:
是粗糙度或美术控制量。金属可用更硬、更有色的高光;皮肤和布料使用更柔和、受遮罩控制的层。
并非所有 NPR 都要删除 Fresnel。Fresnel 是否保留取决于视觉目标。若保留,应控制它不会在暗部制造无法管理的亮边;若移除,也要用 Rim/Specular 规则补回轮廓可读性。
皮肤Ramp与SSS近似
皮肤可在明暗交界加入一条偏暖的窄色带,近似次表面散射的视觉结果。这不是物理 SSS,只是风格化层。
可以使用 Thickness/Curvature/区域遮罩控制耳朵、鼻翼和面颊强度。若只依赖 ,所有物体都会出现相同暖边,看起来像统一描边而不是皮肤。
MatCap与视角空间细节
MatCap 用观察空间法线映射二维材质球,适合提供固定高光、金属或手绘反射。它不随世界光源正确变化,因此更像艺术层。
可把 MatCap 与光照权重、遮罩和 Fresnel 混合,避免角色转动时图案完全粘在屏幕上。相机 FOV 与观察空间约定改变时也要检查。
光照组合
多光源
把每盏光分别 Ramp 后相加,会快速把画面推白,也会产生多组互相冲突的硬边。常见控制方式:
- 只有主方向光决定大明暗;
- 附加光只贡献受限的 Specular/Rim;
- 按亮度选主光,但对切换做平滑;
- 把局部光汇总成连续补光,再由统一曲线限制;
- 对关键角色使用光照通道或专用灯光配置。
这些方案改变的是主光与补光如何塑造画面。固定一套规则后,分别开关各光源和遮罩,就能判断明暗层为何发生变化。
与PBR的结合路线
传统赛璐璐渲染的局限在于材质表达力:减少色阶带来了卡通感,也同时抹掉了区分金属、布料、皮革的信息。业界由此发展出几条与 PBR 结合的路线。
降色阶化。以完整 PBR 光照为底,在输出端减少色阶。它保留PBR计算的材质差异,色阶压缩也可能削弱高光层次。适用性由目标色板、线条和建模共同判断,不能仅按风格标签推定。
写实手办风。基本保持 PBR 光照,只对漫反射项做局部变形(例如提升暗部饱和度),卡通感主要来自建模造型与材质参数的风格化,而非光照模型。
分区并存。角色主体用传统 Toon 材质,金属、宝石、皮革等需要质感的部件走 PBR 或混合模型。这是一种可控的选择,因为它把“要色块”和“要质感”的区域分开处理,不必在一套模型里妥协。
选择依据是画面目标而非技术先进性。是否使用 IBL、保留哪些 BRDF 项、在哪个环节降色阶,都应当先由美术目标确定。
参数归属
同一个物体受到两盏灯照射时,过渡宽度由存储位置决定控制粒度。保存到GBuffer的宽度随像素材质变化,保存在光源的数据则随灯变化。
以明暗过渡的软硬度为例:
float level = 0.5 - toonLightOffset;
float toonNoL = smoothness > 0.0
? smoothstep(level - smoothness * 0.5, level + smoothness * 0.5, ndotl)
: step(level, ndotl);smoothness 为零时使用硬阈值,避免向 smoothstep 传入相等边界;负值在参数验证时拒绝。如果 smoothness 来自 GBuffer,那么同一个物体被任何光源照射时软硬度都一致——无法做到“一盏很硬的背光加一盏很软的补光”这种美术常用的打光手法。
反过来,高光的软硬度更适合放在物体上,因为它直接表达材质属性,不同部位需要不同取值。
一个实践中的划分是:漫反射的过渡软硬度放在光源,高光的软硬度放在物体。这种不对称初看割裂,但它对应了真实的需求差异——前者是打光意图,后者是材质属性。
参数归属由控制对象决定。若艺术目标同时需要灯光软硬和材质局部阈值,可以分别保存两者,再定义组合顺序,避免用单一参数承担两种职责。
数据与工程边界
二值化的固有问题
对光照做硬二值化时,明暗交界线常出现锯齿状的不规则边缘。原因有两层:
其一,插值法线在三角形内部平滑变化,相邻三角形的变化率却未必连续。硬阈值提取等值线以后,边界在三角形连接处可能改变曲率;像素覆盖不足又会增加屏幕锯齿。这两份误差分别来自几何/法线近似与光栅采样,需要分开检查。
其二,更根本的是——画师绘制光照时按画面好看来画,很多理想的明暗分布与三维结构本就不对应。这不是精度问题,无法靠提高面数解决。
一种资产侧处理是修改法线:调整法线朝向、用简模法线替换高模法线,让明暗边界服从美术意图而非几何结构。编辑后的法线需经导入、变换并写入实际着色输入;延迟路径的GBuffer必须保存着色器真正要使用的方向。
引擎侧也可以提供运行时的法线调整手段,代价是可控性不如离线编辑,且难以处理复杂造型。两者通常并存:主要角色走离线法线编辑,次要角色用运行时近似。
阴影与环境光
阴影贴图的结果是可见性,不应直接等同于最终阴影颜色。可以用可见性在亮/暗层之间选择,再由 Ramp 和材质定义颜色。
环境光、光照探针或 IBL 能防止暗部完全扁平。NPR 通常对其做压缩或色板映射,保留体积同时不破坏色块。
场景 Fog、Exposure 和色调映射会改变层级对比。调材质时必须经过最终后处理链,而不是只看未色调映射的中间缓冲。
风格一致性与质量档
建议把以下参数放入共享 Profile,而不是散落在材质实例:
- Ramp/颜色 Palette;
- 主光方向与阴影 Tint;
- 全局阈值、过渡宽度;
- Rim/Specular 上限;
- Outline 与距离缩放;
- Fog、Exposure 和色调映射配置。
低质量档可减少附加光、MatCap 层、额外遮罩和屏幕空间效果,但应尽量保持同一色板与层级关系。
验证方法
- 输出原始 、Ramp UV、层 Weight、阴影可见性和材质ID 调试视图。
- 用固定灯光旋转角色,检查边界连续性和法线接缝。
- 在不同 FOV、距离、HDR 曝光和色调映射下比较色块。
- 检查主光切换、多光源重叠、光照探针和实时阴影组合。
- 用灰球、标准角色和场景资产共享一套 Profile 做风格回归。
2026-10-22 执行 编译检查,DXC 将明暗坐标和平面 Ramp 采样两个片段编译为 ps_6_0,结果通过。该结果只验证带最小输入声明的 HLSL 片段,完整材质、资产绑定、引擎运行与 GPU 性能仍未验证。
相关主题
示例状态:本页代码用于解释对应的数据和调用约束,完整资源创建、类型与调用环境由项目提供。除文中另列的实际实验外,本页未执行目标引擎编译、GPU捕获或性能计时。
参考资料
- Gooch 等:技术插图光照模型:作者摘要中的色相、亮度与轮廓层次;本文 Ramp 与阴影合成是独立风格化方案。