Untoon Engine

Material-Authoring-Guide

本页目录

材质编写指南

目标

这一页讲怎么在 untoon 分支里创建和组织 Untoon 材质。

基础原则

Untoon 材质基于原生 Toon Shading Model,不是在默认 Lit 材质上挂几个技巧节点。项目里最好统一这几样东西:

  • 一个 Untoon Master Material
  • 一组 Untoon Material Function
  • 一套参数命名规范

必备入口

Shading Model

材质先设为 Shading Model = Toon。

Toon Light

Toon Light 在 RenderToonLightPass 阶段提供风格化光照结果。常见用法是自定义明暗分界颜色、控制与原始 post-light 结果的混合比例,以及按材质特性调制或改写区域光照结果。

节点有两个输入:

  • ToonLighting:float4
    • rgb:材质希望在 Toon Light Pass 中提供的风格化颜色
    • a:与原始 post-light 场景色之间的混合权重
  • AreaLightLightingModulate:float3
    • 调制 PostLightSceneColor - PreLightSceneColor 得到的区域光照结果
    • 未连接时默认视为白色乘子 1,1,1

其中:

  • PreLightSceneColor:RenderLights 之前保存的 SceneColor 快照,主要表示间接光、底色、emissive 等灯光前底色
  • PostLightSceneColor:RenderLights / RenderMegaLights 之后、RenderToonLightPass 开始时保存的 SceneColor 快照
  • AreaLightLighting:max(PostLightSceneColor - PreLightSceneColor, 0),用于表达当前后置阶段看到的区域光照差分结果
  • SimpleMainDirectionalDiffuse:基于 BaseColor / PI、主方向光颜色、NdotL 与 1 - UntoonShadow.R 的简化漫反射补偿

当前 Toon Light Pass 的合成语义为:

AreaLightLighting = max(PostLightSceneColor - PreLightSceneColor, 0.0f);
AreaLightLighting *= AreaLightLightingModulate;
SimpleMainDirectionalDiffuse = (BaseColor / PI) * MainLightColor * saturate(dot(N, L)) * (1.0 - UntoonShadow.R);

FinalColor = lerp(PostLightSceneColor + SimpleMainDirectionalDiffuse, ToonLighting.rgb + AreaLightLighting, saturate(ToonLighting.a));

这意味着:

  • ToonLighting.a = 0:输出 PostLightSceneColor + SimpleMainDirectionalDiffuse
  • ToonLighting.a = 1:输出 ToonLighting.rgb + AreaLightLighting
  • AreaLightLightingModulate 只调制差分得到的区域光照结果,不改变 PreLightSceneColor 本身

Toon Priority 与 Untoon Render Group

为角色 Actor 添加 Untoon Render Group 组件。默认自动登记该 Actor 的 Mesh Component;跨组件成员可用 Members 或 Blueprint AddMember 显式登记。每个注册实例获得独立组标识,共享眼睛/头发材质不会让不同角色串组。

在眼睛、眼线或眉毛材质加入 Toon Priority,将 Role 设为 Target;在允许让位的头发材质中设为 YieldingOccluder。两个角色值都沿用 Shading Model = Toon、Opaque 或 Masked。一个网格通过材质槽区分眼睛和头发;一个材质只选择一种角色。

Mask 是单通道像素遮罩,默认 1;Mask >= 0.5 才参与,不做半透明边缘混合。未添加节点、Disabled、未登记渲染组或关闭 r.Untoon.Priority 时使用普通遮挡。

目标只穿过同组允许让位的像素。脸、墙和其他角色的头发照常遮挡;墙夹在头发与眼睛之间时,眼睛不可见,前方头发保留。运行时隐藏、距离和 LOD 规则仍有效。图集容量不足或组内需要的着色器未就绪时,该组使用普通遮挡。

运行时创建、删除或更换成员后调用 RefreshMembers。自动包含所属 Actor 网格时,RemoveMember 只删除显式登记;需要完全手动控制时先关闭 bIncludeOwnerMeshes。一个 Mesh Component 只能归属一个组,重复登记会保留先前归属并输出日志。

优先显示节点的输入应来自在相机各阶段一致的材质数据,例如 UV、纹理、顶点色、参数和表面法线。不要让 Priority Mask、WPO、Opacity Mask 或 PDO 依赖 ToonPassSwitch 的不同阶段结果、Toon 灯光结果或后置 SceneColor:深度阶段不能读取尚未产生的光照资源。

首版限定桌面 Deferred 普通静态/骨骼 Opaque、Masked Toon 网格。Nanite、Groom Strands、Forward、Mobile、Path Tracing、Scene Capture、Instanced Stereo 不参与。功能开关与排查方法见灯光与后处理指南,验收状态见优先显示验收。

ToonShadowMask

ToonShadowMask 在 post-light ShadowMask export 阶段输出风格化阴影遮罩,用来控制阴影区域的形状,也是后面晕染和模糊的输入。

执行顺序是:

  1. RenderLights 先把表面阴影写入 UntoonShadow.R
  2. ToonShadowMaskOutput 再读取这个结果并输出最终 Shadow Mask
  3. 输出写入 UntoonShadow.G
  4. 后续 blur 会消费 G 并写入 B

所以 ToonShadowMask 回答的是「怎么根据已有受光结果生成风格化阴影」,主光阴影本身不在这里重新定义。

ToonPassSwitch

ToonPassSwitch 用于让同一材质在不同 Untoon Pass 中输出不同结果。

推荐只在确实需要分离逻辑时使用,避免材质图过度复杂。

当前可稳定区分的语义上下文包括:

  • 默认路径
  • 保留的 Untoon Base Pass 语义入口
  • Untoon Light / post-light 阶段

普通 mesh 与 Nanite 的 post-light export 已对齐到同一套材质环境语义,因此 ToonPassSwitch 不应再因为是否启用 Nanite 而看到不同分支结果。

推荐材质组织方式

主材质只定义 Shading Model 和主输出,Toon Light 结果与 Shadow Mask 生成各交给一个 Material Function。分开封装之后,排查普通 mesh 与 Nanite 的差异时可以直接换掉单个函数做最小验证,不用动整张材质图。

调参建议

风格化光照

  • 风格化光照颜色与后置阶段区域光照调制交给 Toon Light。
  • 基于 UntoonShadow.R 的阴影形状交给 ToonShadowMask。
  • ToonPassSwitch 只在材质确实需要区分不同 Untoon Pass 时使用。

常见错误

  • 用默认 Lit 材质模拟 Untoon,而不使用 Toon Shading Model
  • 在材质图中直接硬编码大量 Untoon 参数
  • 过度依赖 ToonPassSwitch,导致材质难以维护
  • 在 ToonShadowMask 中假设某些 LightPass 变量只会出现在普通 mesh 路径
  • 把 ToonShadowMask 当作「主光阴影源」,而不是 post-light 风格化导出阶段

相关页面