祀 · 角色 Shader 实景逐层对照

从同一个庭院机位,把祀的材质由灰模受光逐步恢复到正确头骨方向输入下的完整效果。每一层都有实际模型截图、输入贴图、算法原理和制作建议。

2026-10-30 初版 / 2026-11-02 采集抗锯齿与 Overlay 误绑勘误,302 张正常分析图重拍 · Unity 2022.3.58f1 / URP 14.0.11 / DX11 · 1600×900 · 非 Play:4×MSAA + SMAA

2026-11-02 采集勘误:运行时 TAA 已有;旧文档采集另行指定 SMAA,漏掉了编辑 4×MSAA。本版沿用场景编辑预览,逐帧核对真实颜色附件,并修复重复采集将眼部 Overlay 误换成普通表面的错误。下方提供同机位抗锯齿对照和青绿色块的实渲定位。

本版已替换全部正常分析截图:在教学副本中修正被材质槽属性块遮蔽的头骨方向,重拍逐层、光角和多视角图。旧错误效果只保留在下方勘误对照的左侧。正式运行场景尚未应用此修正。

2026-11-02 可见性补充:阶段 02 法线、06 阴影 LUT、09 眼睛 Matcap、10 边缘光已改用重新渲染的局部特写。可整幅切换,也可点击“自动对比”每 0.9 秒交替查看同一部位,点击“停止对比”即可停下。原参数贡献很小的结果如实保留,辅助侧/背光另行标明。

Mask 多视角验证:9 个相机角度 × 3 种光照,原 Mask、忽略 Mask、关闭 SDF 与 Matcap 已在正确方向下重新对照。

祀 · 正确头骨方向下的完整效果,真实庭院固定姿态,2026-11-02 重拍
祀 · 正确头骨方向下的完整效果,真实庭院固定姿态,2026-11-02 重拍

截图勘误:已经有 TAA,为什么文档还有锯齿?

工程已实现运行时 URP TAA High。旧角色文档采用非 Play 的独立采集相机,并把环境 AA 选择硬改为 SMAA;这还绕过了场景原有的编辑多重采样分支。问题出在采集路径,旧截图不能用来判断运行时 TAA 是否有效。

路径实际处理证据与边界
庭院 Play / 独立运行URP TAA High此前庭院验证跨 64 个真实 Play 帧更新历史;见庭院抗锯齿落地说明.md。
旧文档采集单样本 + SMAA High独立诊断实际颜色附件为 1 样本;没有 TAA 历史积累。
本次重拍最终颜色4×MSAA + SMAA High沿用原场景编辑预览逻辑;每帧记录颜色附件实际 4 样本。
中间量 / 数据通道关闭后处理用于解释数值,不当作最终抗锯齿画面。

TAA 需要不同真实帧的子像素抖动和历史重投影。这里每次切换材质阶段都会改变当前画面,因此保留不依赖时间历史的编辑采集。连续调用两次 Camera.Render 只用于预热渲染资源,不能称为两帧 TAA 收敛。

同机位描边:旧采集 / 恢复场景编辑抗锯齿

修正采集:4×MSAA + SMAA旧采集:1 样本 + SMAA
旧采集:1 样本 + SMAA修正采集:4×MSAA + SMAA

拖动画面或滑块 · 双击放大原图 · 左:旧采集:1 样本 + SMAA / 右:修正采集:4×MSAA + SMAA

两侧均为阶段 11,机位、1600×900 分辨率和描边宽度一致。观察发梢、脸轮廓及背景几何斜边;不能把全部背景叶片裁切和材质高频细节都归因于描边。MSAA 也不能自动消除所有透明裁切或着色产生的锯齿。

“屏幕宽度”只表示外扩量按像素计算。当前脸部约 0.35 像素、头发等约 0.65 像素,均小于 1 像素;单帧单样本容易把细线采成断续阶梯。OutlineMask 还使用 clip(R−0.1) 硬裁切。稳定线宽、平滑覆盖率和时间抗锯齿是三个不同环节。

眼睛青绿色平面:覆盖层误绑的实渲复现

恢复正确 Overlay 类型:阶段 09 尚未启用覆盖层诊断中复现旧错误:Overlay 被当成普通表面
诊断中复现旧错误:Overlay 被当成普通表面恢复正确 Overlay 类型:阶段 09 尚未启用覆盖层

拖动画面或滑块 · 双击放大原图 · 左:诊断中复现旧错误:Overlay 被当成普通表面 / 右:恢复正确 Overlay 类型:阶段 09 尚未启用覆盖层

这片区域来自眼部阴影覆盖网格,正常用途是通过遮罩和模板值对已有眼部颜色作乘法调暗。它不是虹膜 Matcap,也不是 TAA 残影。旧工具重复执行时保留了内存中的 OverlaySteps,却只识别源 Shader 的 OverlayShadow 后缀,第二次切换误用了普通角色 Shader;头发阴影薄片也会受到同样影响。

本次从源材质显式恢复副本的 Shader 与属性,再同时识别源覆盖层和教学覆盖层,消除重复运行的状态残留。受控误绑复现与归档旧图逐像素一致;恢复后与正确基线的最大通道差为 0/255。正常正文中的旧色块图已经替换;这里的左图只作为错误示例。

核对内容本次结果
重拍范围302 张:94 张逐层/专项、153 张多视角、55 张特写
最终颜色实际 AA213 张逐帧核对为 4×MSAA + SMAA;其余为关闭后处理的数据通道
源文件各采集报告的逐文件指纹全部保持一致
验证报告F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\capture-correction-validation.json
诊断采集F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\eye-outline-inspect.json
运行时 TAA 说明F:\UnityAITest\GameTest\Docs\庭院抗锯齿落地说明.md

头身受光勘误:原因、修正对照与重拍范围

2026-11-02:正文的 94 张逐层/专项图及 153 张多视角图已全部在正确的头骨输入下重新采集,封面、滑块、SDF 光角、Mask、Matcap、GPU 通道及等价对照均已替换。仅本节左侧保留明确标注的旧错误效果,供解释原因。

此前脸部把正面来光判成背光,大片使用偏棕的阴影 LUT,因而比肩部明显更暗。问题出在方向参数传递:PerlicaHeadBasis 将方向写到 Renderer 级属性块;SourceShadowActor 创建材质槽级属性块写阴影索引。存在槽级块时,Renderer 级块不再生效,缺少的头骨方向回退到材质默认值。

输入/分支旧错误输入本次副本正确输入
头部前向 _HeadForward材质默认 (0,0,−1)头骨实际约 (0.0032,−0.0842,+0.9964)
本次 GPU 主光方向(−0.1330,+0.6428,+0.7544)同一方向,不旋转灯光
L·headForward约 −0.754,判为背光约 +0.697,判为前方来光
SDF 阈值 t约 0.99240约 0.00931
脸 Kind=2 / 身体 Kind=1脸用错误基底驱动 SDF;身体按法线受光脸用正确基底驱动 SDF;身体仍按法线受光

脸与身体共用 T_actor_common_face_01_RD 和 T_actor_common_femaleskincolor01_lut_D,环境光强度均为 0.8、基础色乘色为白色。因此此次落差不能解释为“脸用了另一张肤色 LUT”。两者阴影强度等仍有差别,且脸部本来还有 SDF、Overlay 等艺术化处理,修正并不意味着所有部位应完全同色。

头骨世界前/右轴 → Renderer 属性块
                         × 被槽级属性块遮蔽 → 回退到材质默认 −Z
修正副本:读取每材质槽已有属性块
        → 保留 _TTShadowActorIndex
        → 合入真实 _HeadForward / _HeadRight
        → 渲染后逐槽核对方向仍正确
        → 在相同方向输入下比较原 Shader 与教学完整阶段

三机位:旧错误效果 / 同条件同步方向后的效果

以下六张来自独立诊断采集:相同姿态、材质参数、贴图、相机及灯光,右侧只修正副本中的头骨方向传递。脸部恢复合理的正面受光;头发高光也可能变化,因为它同样读取头骨基底。此处左图是故障示例,不能用作正常 SDF 目标效果。

左斜侧 40°

正确输入:副本同步头骨方向旧错误:头部方向被遮蔽
旧错误:头部方向被遮蔽正确输入:副本同步头骨方向

拖动画面或滑块 · 双击放大原图 · 左:旧错误:头部方向被遮蔽 / 右:正确输入:副本同步头骨方向

正面 0°

正确输入:副本同步头骨方向旧错误:头部方向被遮蔽
旧错误:头部方向被遮蔽正确输入:副本同步头骨方向

拖动画面或滑块 · 双击放大原图 · 左:旧错误:头部方向被遮蔽 / 右:正确输入:副本同步头骨方向

右斜侧 40°

正确输入:副本同步头骨方向旧错误:头部方向被遮蔽
旧错误:头部方向被遮蔽正确输入:副本同步头骨方向

拖动画面或滑块 · 双击放大原图 · 左:旧错误:头部方向被遮蔽 / 右:正确输入:副本同步头骨方向

同机位局部像素与验收边界

下表只采样左斜侧 40° 图中的 9×9 像素块,坐标从原图左上角计数。数值为最终 PNG 的显示 RGB,不是线性辐亮度;肩膀和背景的选定小块未变化,不能外推为整个身体都逐像素相同。

区域 / 中心坐标旧错误 RGB 均值同步方向后 RGB 均值最大通道差 / 255
脸颊 [765, 554]195.00, 151.20, 125.89221.65, 202.73, 180.4956
肩膀 [997, 806]221.06, 204.06, 182.89221.06, 204.06, 182.890
背景 [1423, 390]182.35, 156.72, 193.59182.35, 156.72, 193.590

此前“原 Shader 与教学副本逐像素一致”只证明二者等价,两个版本可以同时继承相同的输入错误。本次额外核对每张图的槽级头骨方向、六组光角的 GPU 主光,以及正面/侧面实渲。SDF 仍保持启用,没有用提亮底色、改变曝光或永久关闭 SDF 来掩盖问题。

修改范围:截图工具、独立教学副本与本文档。正式角色脚本、原 Shader、源材质和原场景未改动;这些新图不能表示正式运行场景已完成修复。保存的教学场景把当前固定姿态方向写入复制材质,重新摆姿势后应重新采集。

证据/工具完整路径
方向写入脚本F:\UnityAITest\GameTest\Assets\Art\Shader\Perlica\URP\PerlicaHeadBasis.cs
槽级阴影索引写入F:\UnityAITest\GameTest\Assets\Art\Enviorment\Scripts\Runtime\SourceShadowActor.cs
六张诊断截图F:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\HeadBodyDiagnosis
诊断原始报告F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\headbody-diagnose.json
诊断图及像素核对F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\headbody-diagnosis-validation.json
247 张重拍、逐图哈希与主光检查F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\recapture-validation.json
旧图归档(不用于正常分析)F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\BeforeHeadBasisCorrection-20261102\manifest.json

先看整体,再拖动比较

13 自发光与当前完整效果00 灰模受光
00 灰模受光13 自发光与当前完整效果

拖动画面或滑块 · 双击放大原图 · 左:00 灰模受光 / 右:13 自发光与当前完整效果

本篇拆解的是当前工程的 Charter_Shader URP 适配,在教学副本中统一正确的头骨方向输入。祀的原贴图与绑定提供输入证据;不能把本地适配公式称为终末地客户端完整实现。阶段开关与方向同步仅用于副本,原场景、原 Shader、源材质和导入配置均以文件指纹核对。

天空、庭院和祀的独立 VFX 材质保持原路径,未纳入本篇逐层门控;画面中的装饰特效不是 SDF、法线或自发光贴图新增的贡献。祀携带的武器若使用 Charter_Shader,也随主表面一起分层。

案例、固定条件与实际绑定

项目本次记录
角色祀 / chr_0036_jsspsi
模型F:\UnityAITest\GameTest\Assets\Art\Model\Si\Si_CombatModel.prefab
动作F:\UnityAITest\GameTest\Assets\Art\Animator\Si\Si_Combat.asset:idle + idleAdditive,固定 0.75 秒
环境EnvironmentArtSandbox 的独立副本;云、水、粒子、GPU 落叶固定
相机三个固定机位;最终颜色实际 4×MSAA + SMAA High;关闭 dithering;两次 Camera.Render 用于预热,不是 TAA 收敛
光照逐层过程保持太阳和曝光不变;SDF 专项只旋转副本太阳,每角关闭/开启成对
截图边界GPU 直接输出 PNG,未修图、未人工增强;通道图关闭后处理,输入图拆通道另标注
祀实例中的材质ShaderKind实际开启输入渲染队列
M_wpn_claym_0010_01.matCharter_Shader0BumpMap, MetallicGlossMap2450
M_eyewhiteshadow_common_01.matCharter_Shader/OverlayShadow-1Overlay:单独的乘法材质2900
M_eyeshadow_common_03.matCharter_Shader/OverlayShadow-1Overlay:单独的乘法材质2900
M_actor_jsspsi_body_01.matCharter_Shader1BumpMap, DiffRampMap, ShadowLutTex2000
M_actor_jsspsi_cloth_02.matCharter_Shader0BumpMap, MetallicGlossMap, DiffRampMap, SpecRampMap, Emission, OutlineMask2000
M_actor_jsspsi_cloth_03.matCharter_Shader0BumpMap, MetallicGlossMap, DiffRampMap, SpecRampMap3000
M_actor_jsspsi_hair_01.matCharter_Shader3BumpMap, MetallicGlossMap, DiffRampMap, SpecRampMap, LineMap, OutlineMask2000
M_actor_jsspsi_iris_01.matCharter_Shader4DiffRampMap, Matcap2000
M_actor_jsspsi_brow_01.matCharter_Shader5DiffRampMap, ShadowLutTex2000
M_actor_jsspsi_face_01.matCharter_Shader2BumpMap, DiffRampMap, ShadowLutTex, SDFLightmap, OutlineMask2000
M_hairshadow_common_01.matCharter_Shader/OverlayShadow-1Overlay:单独的乘法材质2900
M_actor_jsspsi_cloth_04.matCharter_Shader0BumpMap, MetallicGlossMap, DiffRampMap, SpecRampMap2000
M_actor_jsspsi_cloth_01.matCharter_Shader0BumpMap, MetallicGlossMap, DiffRampMap, SpecRampMap, Emission, OutlineMask2000

Kind:0 衣料,1 皮肤,2 脸,3 头发,4 眼睛,5 眉;-1 表示 Overlay 无此参数。这里列的是当前模型真正引用的材质,不把目录里所有备用材质都说成已使用。

主案例的脸部 SDF 是 T_actor_common_female_face_03_SDF,Mask 是 face_03_cm_M;这里读取祀自己的绑定,没有沿用此前佩丽卡 face_01 的贴图作为案例。

骨骼姿势 / 顶点位置 / UV / 法线切线
  ↓ CharacterVertex:世界坐标、TBN、BaseMap UV
  ↓ Base → N/HN → P → 半兰伯特或脸部 SDF → RD
  ↓ 阴影可见度与 P.B 上限 → LUT/备用阴影色 → 漫反射
  ↓ 普通高光或头发条带 → RS / Line → Matcap / Rim / Emission
  ↓ 表面雾;独立反壳描边与 Overlay Pass(内部 4×MSAA)
  ↓ 相机曝光、色调映射、Bloom、SMAA → 实渲 PNG

上面是依赖关系和阅读顺序,不是新增 14 个运行时渲染 Pass。教学阶段在同一个原算法中开启对应分支;原 Forward、Outline、Depth、Shadow 等 Pass 结构保持。

输入贴图的彩色预览显示源文件 RGB,不把打包的 Alpha 当作网页透明度。P.A、HN.A、RD.A、SDF.A 等另用灰度通道图展示;这与模型最终透明混合是两回事。祀的独立特效材质在副本中固定到 4 秒,保留其 Shader 与原参数。

00 灰模受光

制作原因:先建立几何和受光基准

将祀的主表面 RGB 换成 18% 线性灰,保留原网格、骨骼姿势、透明度、主光、阴影和环境光。高光、法线图和艺术化明暗暂不加入。灰模已经受光,不是无光照的纯色剪影;薄衣料仍使用原 Alpha 和透明队列。

阶段 00:灰模仍保留原透明度和场景受光
阶段 00:灰模仍保留原透明度和场景受光

当前算法

albedo.rgb = 0.18
N = normalize(模型法线)
P = (0, 0.5, 1, 0.25)
lightValue = saturate(N·L × 0.5 + 0.5)
高光、SDF、Ramp、LUT、Rim、描边、Overlay、自发光 = 关闭

算法原理

灰模把颜色与纹理干扰移开,可以先观察模型体块和环境亮度。这里的 0.18 是线性数值,经曝光和显示变换后不会是 PNG 中的 18% 灰。各阶段保留同一套相机后处理。

贴图制作与导入思路(建议流程)

  1. 先检查低模轮廓、最终三角化、UV、顶点法线和切线。脸颊凹陷、发片穿插属于几何问题,不能用贴图掩盖。
  2. 固定动作、相机、分辨率、光源和曝光,再逐项恢复输入。实际 HDR 曝光、色调映射和抗锯齿设置随报告记录。

01 基础色 D

制作原因:确定角色固有色和服饰图案

恢复祀原来的 _BaseMap × _BaseColor。此时可以看到肤色、白发、衣服印花及装饰色,但还没有贴图法线、P 属性和发丝条带高光。阴影仍来自统一的基础受光。

01 基础色 D00 灰模受光
00 灰模受光01 基础色 D

拖动画面或滑块 · 双击放大原图 · 左:00 灰模受光 / 右:01 基础色 D

实际输入贴图

脸部 · 基础色 / Overlay 遮罩 · 源图 1024×1024 / GPU 1024×1024 · sRGB · RGB 数据预览
脸部 · 基础色 / Overlay 遮罩 · 源图 1024×1024 / GPU 1024×1024 · sRGB · RGB 数据预览
头发 · 基础色 / Overlay 遮罩 · 源图 2048×2048 / GPU 2048×2048 · sRGB · RGB 数据预览
头发 · 基础色 / Overlay 遮罩 · 源图 2048×2048 / GPU 2048×2048 · sRGB · RGB 数据预览
衣料 01 · 基础色 / Overlay 遮罩 · 源图 2048×2048 / GPU 2048×2048 · sRGB · RGB 数据预览
衣料 01 · 基础色 / Overlay 遮罩 · 源图 2048×2048 / GPU 2048×2048 · sRGB · RGB 数据预览

当前算法

uv = uv0 × _BaseMap_ST.xy + _BaseMap_ST.zw
D = sample(_BaseMap, uv)
albedo = D × _BaseColor
若 _AlphaClip > 0.5:clip(albedo.a - _Cutoff)

算法原理

基础色描述表面本身的颜色,而最终像素还要经过光照。颜色图开启 sRGB 后,GPU 采样会把 RGB 解码到线性空间参与计算;Alpha 仍是线性数据。源 D 中可以包含美术绘制的局部层次,但不应把固定方向的太阳投影烘死在底色里。

贴图制作与导入思路(建议流程)

  1. 用材质 ID、曲率等辅助分区,绘制肤色、妆容、衣料和金属饰面的固有色。为 UV 边缘扩边,避免缩小后漏出底色。
  2. 透明发片、薄衣料把覆盖率存入 A,先确认它们用透明混合还是 AlphaClip。本次祀实例引用的 cloth_03 使用透明分支;目录中的备用 hairt 材质未被此实例引用。
  3. 导出颜色 PNG,RGB 按 sRGB 导入;在固定白光及庭院环境各检查一次,不用曝光补偿错误的颜色。

02 普通法线 N 与头发 HN

制作原因:增加受光细节,并让发束明暗与高光分别可控

恢复普通部位的法线图,以及祀头发 RG/BA 打包的双法线。普通法线同时影响漫反射和高光;头发 RG 影响漫反射,BA 影响高光方向。这个阶段高光带尚未加入,因此 BA 的视觉贡献要结合后面的“高光法线单项关闭”对照判断。

上轮总览把衣料的细小法线变化缩得太小。这里分别看衣料 N 和头发 HN.RG;阶段 02 还没有头发高光,不能把尚未出现的亮带作为 HN.BA 的效果。

衣料 N:原光照下的表面细节

观察衣料的纹理、缝边和表面微小明暗;用“只看加入前/后”切换整幅,避免将画面两边不同部位互相比较。

庭院原光照;同一观察点缩窄视野至原来的 1/5.00,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
02 加入贴图法线01 只有基础色和模型法线
01 只有基础色和模型法线02 加入贴图法线

拖动画面或滑块 · 双击放大原图 · 左:01 只有基础色和模型法线 / 右:02 加入贴图法线

本特写未增强差异:最大通道差 72/255,超过 2/255 的像素占 20.60%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 38/255,超过2/255的像素占 0.0018%,最差40×40局部平均差 0.0971/255。微弱效果需结合这项误差与通道图判断。

补充侧光:让法线方向的变化更容易辨认

此组单独把主光转到头部右侧 70°。两张使用相同侧光,只开关法线;侧光本身造成的变化不能算成 N 的贡献。

受控侧/背光:相对头骨 70°,原主光强度和曝光;同一观察点缩窄视野至原来的 1/5.00,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
02 模型法线 + N01 模型法线
01 模型法线02 模型法线 + N

拖动画面或滑块 · 双击放大原图 · 左:01 模型法线 / 右:02 模型法线 + N

本特写未增强差异:最大通道差 68/255,超过 2/255 的像素占 22.97%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 49/255,超过2/255的像素占 0.0008%,最差40×40局部平均差 0.0498/255。微弱效果需结合这项误差与通道图判断。

同一表面的世界法线 N×0.5+0.5

加入后的 GPU 中间量加入前的 GPU 中间量
加入前的 GPU 中间量加入后的 GPU 中间量

拖动画面或滑块 · 双击放大原图 · 左:加入前的 GPU 中间量 / 右:加入后的 GPU 中间量

法线通道的色彩表示方向,不是额外涂上的颜色。细节方向已经改变,但最终亮度还会经过 N·L、环境光和后处理;因此通道清晰并不保证远景里差异强烈。

头发 HN.RG:发束近景

只比较发束上已有的暗纹和明暗起伏。HN.BA 的条带方向要到阶段 07 的高光开启后再看。

庭院原光照;同一观察点缩窄视野至原来的 1/5.00,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
02 HN 漫反射法线01 模型法线
01 模型法线02 HN 漫反射法线

拖动画面或滑块 · 双击放大原图 · 左:01 模型法线 / 右:02 HN 漫反射法线

本特写未增强差异:最大通道差 74/255,超过 2/255 的像素占 9.90%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 16/255,超过2/255的像素占 0.0195%,最差40×40局部平均差 0.1300/255。微弱效果需结合这项误差与通道图判断。

原逐层机位总览(保留参考,细节以特写为主)
02 普通法线 N 与头发 HN01 基础色 D
01 基础色 D02 普通法线 N 与头发 HN

拖动画面或滑块 · 双击放大原图 · 左:01 基础色 D / 右:02 普通法线 N 与头发 HN

实际输入贴图

衣料 01 · 普通法线 N · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
衣料 01 · 普通法线 N · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
脸部 · 普通法线 N · 源图 1024×1024 / GPU 1024×1024 · Linear · RGB 数据预览
脸部 · 普通法线 N · 源图 1024×1024 / GPU 1024×1024 · Linear · RGB 数据预览
头发 · 双法线 HN · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
头发 · 双法线 HN · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览

以下 HN 四通道是源图数据预览,不能把彩色原图直接当作一张 RGB 法线。

HN.R · 漫反射 X
HN.R · 漫反射 X
HN.G · 漫反射 Y
HN.G · 漫反射 Y
HN.B · 高光 X
HN.B · 高光 X
HN.A · 高光 Y
HN.A · 高光 Y

当前算法

普通图:q = (Ntex.r × Ntex.a, Ntex.g)
头发:qDiffuse = HN.rg;qSpec = HN.ba
xy = q × 2 - 1
z = sqrt(max(1 - dot(xy, xy), 0.0001))
nTS = normalize(float3(xy × scale, z))
B = normalize(cross(Ngeometry, T)) × tangent.w
nWS = normalize(T×nTS.x + B×nTS.y + Ngeometry×nTS.z)

算法原理

法线图改变每个像素参与 N·L 和高光计算的方向,不会改变网格外轮廓。TBN 将切线空间方向变换到世界空间。头发单独设计高光法线,可以在发束体块基本不变时调整高光带走势。当前实现先重建 Z,再缩放 XY 并归一化;不能直接换成任意 NormalMap 解码而认为结果相同。

贴图制作与导入思路(建议流程)

  1. 在最终三角化低模上,用匹配的切线基烘焙高模法线;检查 cage、接缝和 G 方向。
  2. 普通 N 按当前解码打包:RG 存 XY 时 A 必须为 1;AG 存 XY 时 R 必须为 1。使用 Default、关闭 sRGB,避免 Unity NormalMap 导入再次改写通道。
  3. 头发分别制作漫反射与高光的切线空间法线,取两套 XY 打包进 RG、BA。中性 HN 四通道均为 0.5,不是普通 RGB 蓝色法线。
  4. 转动主光检查鼻翼、衣褶、发束接缝;增加高光后再检查 BA 是否把亮带拉歪。

03 属性图 P 与普通高光

制作原因:区分金属饰件、布料与手绘受光限制

恢复祀衣料与头发的 P 图,同时开启普通部位的高光。P 是本角色 Shader 的定制数据格式,不可直接用场景 MAHS、ORM 或 RMA 替换。脸、皮肤、眼睛等类型会把金属度清零;脸和皮肤还可能使用常量而非 P 图。

03 属性图 P 与普通高光02 普通法线 N 与头发 HN
02 普通法线 N 与头发 HN03 属性图 P 与普通高光

拖动画面或滑块 · 双击放大原图 · 左:02 普通法线 N 与头发 HN / 右:03 属性图 P 与普通高光

实际输入贴图

衣料 01 · 属性图 P · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
衣料 01 · 属性图 P · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
头发 · 属性图 P · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
头发 · 属性图 P · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
P.R · 金属度
P.R · 金属度
P.G · 高光幅度
P.G · 高光幅度
P.B · 亮面上限,不是 AO
P.B · 亮面上限,不是 AO
P.A · 光滑度
P.A · 光滑度

当前算法

P.R = metallic;P.G = specularAmplitude
P.B = paintedLitCeiling;P.A = smoothness
lit = min(saturate(ramp.a × visibility), saturate(P.b))
metal = Kind == Cloth ? saturate(P.r) : 0
s = clamp(P.a, 0.02, 0.92)
H = normalize(L + V)
gloss = max(Nspec·H,0) ^ (2 ^ (3 + 7×s))
F0 = lerp(0.055, albedo, metal)
spec = gloss × F0 × P.g
脸部 spec 再乘 0.12

算法原理

光滑度提高时,幂次变大,高光更集中;P.G 控制幅度,P.R 改变高光颜色并削弱漫反射。这里是风格化的近似 Blinn-Phong,不是场景 URP PBR 的完整 GGX。P.B 对亮面插值取上限,暗色会让局部即使正对主光也保留暗部;它不是 AO,也不是高度。

贴图制作与导入思路(建议流程)

  1. 分别保存金属、高光幅度、受光上限、粗糙度四个可编辑源层。金属分区按表面材质制作,不按“哪个地方亮”来涂。
  2. 若制作工具输出粗糙度 r,转换 P.A=1-r;本 Shader 会把有效光滑度限制到 0.02~0.92。
  3. P.B 默认白色,需要固定暗部时才压暗。AO 可供绘画参考,但不能无判断地复制为 P.B。
  4. 以 Default、线性数据导入 RGBA,保留 Alpha。分别查看四通道再合成,避免 A 在压缩或导出时丢失。

04 漫反射 Ramp RD

制作原因:把连续受光变成可设计的明暗层次

恢复各材质实际绑定的 RD。RD 的 A 决定明暗混合,RGB 只按彩色程度参与染色;仅查看 RGB 缩略图不能判断它如何分出亮面和暗面。祀的脸、头发与衣服使用各自实际绑定。

04 漫反射 Ramp RD03 属性图 P 与普通高光
03 属性图 P 与普通高光04 漫反射 Ramp RD

拖动画面或滑块 · 双击放大原图 · 左:03 属性图 P 与普通高光 / 右:04 漫反射 Ramp RD

实际输入贴图

脸部 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
脸部 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
头发 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
头发 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
衣料 01 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
衣料 01 · 漫反射 Ramp RD · 源图 256×1 / GPU 256×1 · sRGB · RGB 数据预览
脸部 RD.A 的真实灰度输入:明暗混合曲线
脸部 RD.A 的真实灰度输入:明暗混合曲线

当前算法

lightValue = saturate(N·L×0.5+0.5)
ramp = sampleLOD0(RD, float2(lightValue,0.5))
visibility = lerp(1, mainShadowAttenuation, _ShadowStrength)
lit = min(saturate(ramp.a × visibility), P.b)
sat = max(ramp.rgb) - min(ramp.rgb)
diffuse = lerp(shadowColor, albedo, lit)
diffuse *= lerp(1, ramp.rgb, saturate(sat))

算法原理

Ramp 是把一个标量重新映射到颜色和权重的查找表。A 的陡坡产生分段受光,缓坡产生柔和过渡。灰度 RGB 的最大最小值相同,当前公式不会再用它重复压暗底色。主光阴影和 P.B 仍会在查表后限制结果,因此“Ramp 上白色”不代表最终像素一定亮。

贴图制作与导入思路(建议流程)

  1. 先独立画 A 的明暗过渡位置,再设计 RGB 的染色;不要用 RGB 黑白渐变且 A 全白来期待明暗分段。
  2. 建议 Clamp、Bilinear,禁止图集边框污染;当前采样显式 LOD 0。RGB 作为颜色按 sRGB,A 保持数据含义。
  3. 转动灯光观察脸、头发和衣料的过渡,同时检查对应的 lightValue、RD.A 和最终 lit,定位问题在哪一级。

05 脸部 SDF 与 Mask

制作原因:让脸颊和鼻侧明暗服从美术设计

仅对 Kind=Face 恢复 SDF 分支。它用相对头骨的光向查预制图,替代普通半兰伯特,再经过 RD 和阴影限制。Mask.G=0 使用 SDF,G=1 回到几何受光。原图 R/G 一起参与,不能写成“左光读 R、右光读 G”。本组已用正确头骨输入重拍;此前脸部异常变暗来自方向传递冲突,不是 SDF 本身应有的效果。

05 脸部 SDF 与 Mask04 漫反射 Ramp RD
04 漫反射 Ramp RD05 脸部 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:04 漫反射 Ramp RD / 右:05 脸部 SDF 与 Mask

实际输入贴图

脸部 · 脸部 SDF · 源图 1024×1024 / GPU 1024×1024 · Linear · RGB 数据预览
脸部 · 脸部 SDF · 源图 1024×1024 / GPU 1024×1024 · Linear · RGB 数据预览
脸部 · SDF Mask · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
脸部 · SDF Mask · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
SDF.R · 当前读取
SDF.R · 当前读取
SDF.G · 当前读取
SDF.G · 当前读取
SDF.B · 保留的源数据,本项目未读取
SDF.B · 保留的源数据,本项目未读取
SDF.A · 保留的源数据,本项目未读取
SDF.A · 保留的源数据,本项目未读取
Mask.G:0 选 SDF,1 选半兰伯特
Mask.G:0 选 SDF,1 选半兰伯特

当前算法

q = normalize(float2(dot(L,headRight), dot(L,headForward)) + (0,0.0001))
maskUV = uv;若 q.x <= 0:uv.x = 1 - uv.x
s = (SDF(uv).r + SDF(uv).g) / 2
a = q.y × 0.5
t = clamp(0.5-a, 0.001, 0.999)
v = smoothstep(max(2t-1,0), min(2t,1), s)
face = saturate(abs(-v - a×ceil(a)))
lightValue = lerp(face, halfLambert, saturate(Mask(maskUV).g))

算法原理

几何法线会使低模鼻翼、脸颊的小面片产生不理想的阴影。SDF 受光场在 UV 上预先设计过渡,运行时由头骨和光的相对方向驱动。镜像只作用于 SDF UV,Mask 使用原 UV。这里没有独立的俯仰角维度;当前解码也不是简单 step(s,angle)。原资源叫 SDF,但不能仅凭名称认定它是单张黑白图的欧氏距离场。

贴图制作与导入思路(建议流程)

  1. 冻结脸部 UV 和头骨前/右轴。当前镜像 U→1-U 要求左右脸能在这个映射下对应;测试转头,防止阴影固定在世界方向。
  2. 建议准备一侧 0°、45°、90°、135°、180° 的目标明暗图,先烘焙初稿,再修鼻侧、脸颊和下巴;这些角度是制作建议,不是原版已证实的采样方案。
  3. 可用有符号距离 d=EDT(M)-EDT(1-M) 在相邻轮廓间估计切换角;再针对上面的实际解码拟合 s,使所有测试角度下的输出接近目标。不能直接把任意 SDF 工具输出塞进来。
  4. 新贴图可先令 R=G=s 做解码验证,但这不重建原版两通道各自语义。保留源 B/A:本项目没读不等于原版没用。
  5. SDF 与 Mask 按 Default、线性导入;遮罩 G 单独绘制。检查全亮/全暗、UV 接缝、左右镜像及逆光。详见下方六个光角的真实开关对照。

06 皮肤阴影 LUT

制作原因:为不同底色设计不同的阴影色

恢复祀脸部、皮肤等启用材质的阴影 LUT。它改变阴影的颜色映射,SDF/Ramp 决定哪里用这个颜色。它是角色表面着色中的查表,不是相机后处理色调映射。

原正面光下,脸中央的 lit 接近 1:lerp(shadowColor,albedo,lit) 几乎只选基础色,因此换阴影 LUT 的效果本来就很小。单纯放大这个亮区不能解决展示问题。

LUT 主对照:背侧光下的脸部阴影

观察脸颊、鼻侧和下巴的阴影色相与亮度。此处把主光设为头骨右后方 135°,让同一张皮肤进入阴影;SDF、Mask、Ramp、曝光和 LUT 贴图保持原值。

受控侧/背光:相对头骨 135°,原主光强度和曝光;同一观察点缩窄视野至原来的 1/3.57,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
06 启用原皮肤 LUT05 备用亮度/饱和度阴影色
05 备用亮度/饱和度阴影色06 启用原皮肤 LUT

拖动画面或滑块 · 双击放大原图 · 左:05 备用亮度/饱和度阴影色 / 右:06 启用原皮肤 LUT

本特写未增强差异:最大通道差 75/255,超过 2/255 的像素占 20.97%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 30/255,超过2/255的像素占 0.0038%,最差40×40局部平均差 0.1006/255。微弱效果需结合这项误差与通道图判断。

阴影颜色本身:备用公式 / LUT

加入后的 GPU 中间量加入前的 GPU 中间量
加入前的 GPU 中间量加入后的 GPU 中间量

拖动画面或滑块 · 双击放大原图 · 左:加入前的 GPU 中间量 / 右:加入后的 GPU 中间量

上方通道直接输出 shadowColor,便于看清查表改变了什么;它未乘 lit,也未经过最终照明,不能当成脸部最终效果。祀的眉毛材质也启用了 LUT,因此眼睑装饰附近可能比脸颊有更明显的颜色变化;解读肤色时应单独看脸颊,不把这一大块变化都算成皮肤。

对照:原庭院正面光下为什么不明显

原光照的同部位特写

脸中央依然主要处于亮面。保留这组小差异结果,用来解释 LUT 只在阴影权重实际参与时才明显。

庭院原光照;同一观察点缩窄视野至原来的 1/3.57,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
06 原皮肤 LUT05 备用阴影色
05 备用阴影色06 原皮肤 LUT

拖动画面或滑块 · 双击放大原图 · 左:05 备用阴影色 / 右:06 原皮肤 LUT

本特写未增强差异:最大通道差 84/255,超过 2/255 的像素占 2.16%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 56/255,超过2/255的像素占 0.0494%,最差40×40局部平均差 0.1994/255。微弱效果需结合这项误差与通道图判断。

原逐层机位总览(保留参考,细节以特写为主)
06 皮肤阴影 LUT05 脸部 SDF 与 Mask
05 脸部 SDF 与 Mask06 皮肤阴影 LUT

拖动画面或滑块 · 双击放大原图 · 左:05 脸部 SDF 与 Mask / 右:06 皮肤阴影 LUT

实际输入贴图

脸部 · 阴影 LUT · 源图 1024×32 / GPU 1024×32 · sRGB · RGB 数据预览
脸部 · 阴影 LUT · 源图 1024×32 / GPU 1024×32 · sRGB · RGB 数据预览

当前算法

c = saturate(LinearToSRGB(albedo))
z = c.b × 31;k = floor(z)
uv = ((c.r×31+0.5)/1024 + k/32, (c.g×31+0.5)/32)
A = sampleLOD0(LUT,uv).rgb
B = sampleLOD0(LUT,uv+(1/32,0)).rgb
shadowColor = lerp(A,B,frac(z))
diffuse = lerp(shadowColor,albedo,lit)

算法原理

32³ 色表把三维 RGB 输入映射为阴影 RGB。32 个蓝色切片横排成 1024×32,R/G 在切片内定位,蓝色在相邻切片间插值。因此同一张图能让肤色偏暖、灰白区域偏冷,而不只是给所有像素乘统一暗色。最终效果还取决于 lit,正面全亮时关闭 LUT 可能只有小差别。

贴图制作与导入思路(建议流程)

  1. 先生成中性 32³ 色表确认轴和切片布局,再对色表设计阴影色相、亮度和饱和度。尺寸必须与当前 1024×32 公式一致。
  2. 保持 sRGB 颜色导入、Clamp,避免额外显示色彩变换烘入两遍;当前采样 LOD 0。
  3. 用肤色、白发、饱和衣物和极暗颜色做测试。把 LUT 当颜色映射,不把眼眶阴影轮廓画进这张色表。

07 头发条带高光

制作原因:让发束出现连续的方向性亮带

开启头发专用高光形状;它替代普通高光。此阶段先使用默认暖色和均匀发丝强度,下一步再恢复祀自己的 RS 和 Line。头发 HN.BA 已在阶段 02 载入,实际亮带现在开始体现这套法线的作用。头发同样读取头骨基底,本节已随方向修正重拍。

07 头发条带高光06 皮肤阴影 LUT
06 皮肤阴影 LUT07 头发条带高光

拖动画面或滑块 · 双击放大原图 · 左:06 皮肤阴影 LUT / 右:07 头发条带高光

实际输入贴图

头发 · 双法线 HN · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览
头发 · 双法线 HN · 源图 2048×2048 / GPU 2048×2048 · Linear · RGB 数据预览

当前算法

headUp = normalize(cross(headForward,headRight))
Tstrand = normalize(headUp - Nspec×dot(headUp,Nspec))
H = normalize(L+V)
x = dot(Tstrand,H)
band = sqrt(saturate(1-x²)) ^ 72
band *= smoothstep(-0.1,0.6,dot(Nspec,H))
spec = (1,0.86,0.79) × band × _HairSpecStrength

算法原理

普通表面高光围绕法线与半程向量的对齐形成;这里的头发亮带在发丝方向与 H 接近垂直时增强。投影头部上方向可得到局部发丝走向,HN.BA 调整切平面,从而塑造亮带。它是风格化各向异性近似,不是完整多重散射毛发 BSDF,也没有读取独立 Flowmap。

贴图制作与导入思路(建议流程)

  1. 先保证发束几何和漫反射法线成立,再调整高光法线,避免亮带在发片接缝突然中断。
  2. 固定光源扫一圈验证前发、侧发和后发,确认不是只在单个视角好看。
  3. 当前指数 72 固定,强度由 _HairSpecStrength 控制;文档保留祀原值,不为了截图夸大强度。

08 高光 Ramp RS 与发丝 Line

制作原因:给高光设计颜色变化和细碎层次

恢复祀头发和衣料的高光色表,以及头发 Line.R。头发和普通部位查表横轴不同:头发使用 band,普通部位使用 gloss;纵轴均与 N·V 有关。

08 高光 Ramp RS 与发丝 Line07 头发条带高光
07 头发条带高光08 高光 Ramp RS 与发丝 Line

拖动画面或滑块 · 双击放大原图 · 左:07 头发条带高光 / 右:08 高光 Ramp RS 与发丝 Line

实际输入贴图

头发 · 高光 Ramp RS · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
头发 · 高光 Ramp RS · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
头发 · 发丝 Line · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
头发 · 发丝 Line · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
衣料 01 · 高光 Ramp RS · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
衣料 01 · 高光 Ramp RS · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
Line.R 源数据:进入 lerp(0.65,1,R),不是透明度
Line.R 源数据:进入 lerp(0.65,1,R),不是透明度

当前算法

lineUV = uv × _LineMap_ST.xy + _LineMap_ST.zw
detail = lerp(0.65,1,Line(lineUV).r)
头发:tint = RS(saturate(band),saturate(Ndiffuse·V)).rgb
specHair = tint × band × detail × _HairSpecStrength
普通部位:spec *= RS(saturate(gloss),saturate(N·V)).rgb

算法原理

RS 把强度和视角映射为高光颜色,Line 在已有高光上调制细节。Line 黑色仍保留 65% 高光,因此它不是把发片裁掉的透明遮罩。当前头发 Line 的 UV 还叠加独立缩放偏移;原参数会列在贴图绑定表。

贴图制作与导入思路(建议流程)

  1. 先用测试色表核对 X/Y,再绘制高光核心、过渡和掠射角的色彩;RS 按 sRGB 导入。
  2. 顺发束走向画或程序生成 Line,R 存强度,线性导入;避免随机噪声破坏发束方向。
  3. 同时检查 Line 的平铺和 UV 接缝。先单独关闭 RS、再单独关闭 Line,避免把两个输入的贡献混为一谈。

09 眼睛 Matcap

制作原因:用视空间查表补充眼睛颜色

恢复祀虹膜材质启用的 Matcap。它以视空间法线生成坐标,不跟随眼睛 UV 贴死,也不采样庭院实时反射。本材质在已测角度主要采到暗色区域,实际贡献是轻微提亮或染色,并没有明显新增的独立亮斑。下面使用单眼特写展示;白色菱形等关闭前已有的图案不能算成 Matcap。

逐层正面总览的 Matcap 贡献很小,不适合只靠小图解释。下面使用俯视 20° 和左斜侧 40° 的单眼特写,强度仍为当前算法的 0.3,没有为了截图加亮。旧特写中覆盖虹膜的青绿色平面来自采集时误绑的 Overlay,已修正并重拍;定位见“截图勘误”。

Matcap:单眼特写(俯视 20°)

重点比较虹膜暗处和色彩的轻微提亮;点击“自动对比”或“只看加入前/后”,观察同一只眼睛。白色菱形亮斑在关闭 Matcap 时已经存在,不能算成它的效果。

庭院原光照;同一观察点缩窄视野至原来的 1/12.50,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
09 Matcap 开启08 Matcap 关闭
08 Matcap 关闭09 Matcap 开启

拖动画面或滑块 · 双击放大原图 · 左:08 Matcap 关闭 / 右:09 Matcap 开启

本特写未增强差异:最大通道差 100/255,超过 2/255 的像素占 7.41%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 0/255,超过2/255的像素占 0.0000%,最差40×40局部平均差 0.0000/255。微弱效果需结合这项误差与通道图判断。

第二视角:左斜侧 40° 单眼特写

这组取样主要是较暗的颜色,整体贡献轻微;眼睛已充满画面仍看不到强烈新高光,说明当前材质的 Matcap 就不是那种强效果。

庭院原光照;同一观察点缩窄视野至原来的 1/12.50,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
09 Matcap 开启08 Matcap 关闭
08 Matcap 关闭09 Matcap 开启

拖动画面或滑块 · 双击放大原图 · 左:08 Matcap 关闭 / 右:09 Matcap 开启

本特写未增强差异:最大通道差 94/255,超过 2/255 的像素占 9.87%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 0/255,超过2/255的像素占 0.0000%,最差40×40局部平均差 0.0000/255。微弱效果需结合这项误差与通道图判断。

俯视 20° 同机位 Matcap 颜色项

俯视 20° 同机位 Matcap 颜色项 · GPU 直接输出,关闭后处理;不是最终颜色
俯视 20° 同机位 Matcap 颜色项 · GPU 直接输出,关闭后处理;不是最终颜色

Matcap 单项图只显示查表颜色×0.3,其他表面为黑。本例的单项图几乎是低亮度的整片虹膜,说明当前采样没有产生明显独立亮斑;它与纹理中的白色菱形不是一回事。原小图的最大差值还包含抗锯齿边缘,不能凭最大差一个数字断言高光很强。

对照:同样放大的原正面眼睛

原正面机位:相同强度下贡献很小

保留不明显的正面结果,并与上面的斜侧机位区别标注。相机改变了视空间法线和 Matcap UV;不能把两个机位整体不同的外观都归因于开关。

庭院原光照;同一观察点缩窄视野至原来的 1/10.00,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
09 Matcap 开启08 Matcap 关闭
08 Matcap 关闭09 Matcap 开启

拖动画面或滑块 · 双击放大原图 · 左:08 Matcap 关闭 / 右:09 Matcap 开启

本特写未增强差异:最大通道差 6/255,超过 2/255 的像素占 0.00%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 2/255,超过2/255的像素占 0.0000%,最差40×40局部平均差 0.0031/255。微弱效果需结合这项误差与通道图判断。

原逐层机位总览(保留参考,细节以特写为主)
09 眼睛 Matcap08 高光 Ramp RS 与发丝 Line
08 高光 Ramp RS 与发丝 Line09 眼睛 Matcap

拖动画面或滑块 · 双击放大原图 · 左:08 高光 Ramp RS 与发丝 Line / 右:09 眼睛 Matcap

实际输入贴图

虹膜 · Matcap · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
虹膜 · Matcap · 源图 256×256 / GPU 256×256 · sRGB · RGB 数据预览
虹膜 · 基础色 / Overlay 遮罩 · 源图 512×512 / GPU 512×512 · sRGB · RGB 数据预览
虹膜 · 基础色 / Overlay 遮罩 · 源图 512×512 / GPU 512×512 · sRGB · RGB 数据预览

原逐层正面总览(非上方特写)的 08→09 对照:整图最大通道差 43/255,超过 2/255 的像素比例为 0.020347%。多视角章节另以可见虹膜区域统计;最大差不能代替对实际高光形态的观察。

当前算法

Nview = TransformWorldToViewDir(N,true)
uvMatcap = Nview.xy×0.5 + 0.5
color += sample(Matcap,uvMatcap).rgb × 0.3

算法原理

Matcap 预存“某个法线朝向看起来是什么颜色”。相机变动时视空间法线改变,取样位置也改变,因此能以很低成本表现稳定的风格化亮斑。但它不包含真实场景遮挡、反射视差或动态倒影。

贴图制作与导入思路(建议流程)

  1. 在球面法线布局上绘制或渲染亮斑、暗部与边缘,使用彩色 sRGB 图。
  2. 把球面外圈处理干净,避免取样到画布边框;在眼球模型上转相机检查拉伸和亮斑方向。
  3. 与 D 中的虹膜和瞳孔分工:D 管固有图案,Matcap 管视空间外观。

10 边缘光 Rim

制作原因:增强轮廓附近的亮部可读性

恢复各部位原来的 _RimStrength。该项没有专用贴图,由法线、观察方向和主光方向计算。最终是否明显还取决于底色与曝光。

Rim 只加在掠射角附近,且还乘主光方向权重。当前头发强度约 0.16,多数其他材质约 0.07;原全脸图里细窄的提亮容易被底色、发丝高光与曝光掩盖。以下保持这些原强度。

Rim 主对照:最外侧发片轮廓特写

只看发片贴近背景的窄边与弧面转折。点击“自动对比”,让同一条轮廓交替显示开关前后;变化应是薄薄的提亮,不是凭空增加一圈宽白边。

庭院原光照;同一观察点缩窄视野至原来的 1/8.33,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
10 原强度 Rim09 Rim 关闭
09 Rim 关闭10 原强度 Rim

拖动画面或滑块 · 双击放大原图 · 左:09 Rim 关闭 / 右:10 原强度 Rim

本特写未增强差异:最大通道差 56/255,超过 2/255 的像素占 5.93%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 29/255,超过2/255的像素占 0.0034%,最差40×40局部平均差 0.0960/255。微弱效果需结合这项误差与通道图判断。

第二部位:发束掠射角局部

看发束边缘和弧面掠射处的细微提亮,不把阶段 07 已存在的亮带当作新增 Rim。整幅前后切换更容易定位这条窄变化。

庭院原光照;同一观察点缩窄视野至原来的 1/5.00,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
10 原强度 Rim09 Rim 关闭
09 Rim 关闭10 原强度 Rim

拖动画面或滑块 · 双击放大原图 · 左:09 Rim 关闭 / 右:10 原强度 Rim

本特写未增强差异:最大通道差 98/255,超过 2/255 的像素占 12.50%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 0/255,超过2/255的像素占 0.0000%,最差40×40局部平均差 0.0000/255。微弱效果需结合这项误差与通道图判断。

Rim:侧面轮廓局部

比较同一侧面机位的轮廓和发束转折。本轮只更换取景,不提高 Rim 强度;若原参数效果弱,就以弱效果说明,不称为强轮廓灯。

庭院原光照;同一观察点缩窄视野至原来的 1/4.17,重新渲染 1600×900。相机曝光、贴图与材质强度保持原值。

整幅切换:左右滑块
10 原强度 Rim09 Rim 关闭
09 Rim 关闭10 原强度 Rim

拖动画面或滑块 · 双击放大原图 · 左:09 Rim 关闭 / 右:10 原强度 Rim

本特写未增强差异:最大通道差 67/255,超过 2/255 的像素占 2.97%。数值只用于核对开关有无实际贡献,不能替代看图。

同状态复拍误差:最大差 18/255,超过2/255的像素占 0.0242%,最差40×40局部平均差 0.1713/255。微弱效果需结合这项误差与通道图判断。

Rim 几何/光向权重(未乘材质强度)

Rim 几何/光向权重(未乘材质强度) · GPU 直接输出,关闭后处理;不是最终颜色
Rim 几何/光向权重(未乘材质强度) · GPU 直接输出,关闭后处理;不是最终颜色

权重图显示 pow(1−saturate(N·V),4)×saturate(N·L×0.5+0.5)。最终还要乘基础色、主光颜色和 0.07/0.16 的强度,所以权重图的白色不等于最终出现同样亮的白边。

原逐层机位总览(保留参考,细节以特写为主)
10 边缘光 Rim09 眼睛 Matcap
09 眼睛 Matcap10 边缘光 Rim

拖动画面或滑块 · 双击放大原图 · 左:09 眼睛 Matcap / 右:10 边缘光 Rim

当前算法

rim = (1-saturate(N·V)) ^ 4
rim *= saturate(N·L×0.5+0.5)
color += albedo × mainLightColor × rim × _RimStrength

算法原理

N·V 接近 0 时处于掠射轮廓,四次幂把亮部压缩到边缘;再乘主光方向权重,防止所有背光轮廓无条件发亮。这是风格化加色项,不能称为真实轮廓灯或完整物理菲涅耳。

贴图制作与导入思路(建议流程)

  1. 先在原光照下检查脸颊、头发外缘和衣袖,调整每材质强度,避免纯白边圈。
  2. 没有额外 Rim 贴图要制作;其输入来自法线图和几何,因此法线接缝也会影响轮廓光。

11 屏幕宽度描边

制作原因:用稳定轮廓帮助角色从背景中分离

恢复原描边 Pass。反壳只渲染外扩后的背面,并按材质 _OutlineWidth 控制近似像素宽度。祀部分材质的 OutlineMask 会裁掉局部描边。

11 屏幕宽度描边10 边缘光 Rim
10 边缘光 Rim11 屏幕宽度描边

拖动画面或滑块 · 双击放大原图 · 左:10 边缘光 Rim / 右:11 屏幕宽度描边

实际输入贴图

脸部 · 描边遮罩 · 源图 128×128 / GPU 128×128 · Linear · RGB 数据预览
脸部 · 描边遮罩 · 源图 128×128 / GPU 128×128 · Linear · RGB 数据预览
头发 · 描边遮罩 · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
头发 · 描边遮罩 · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
衣料 01 · 描边遮罩 · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览
衣料 01 · 描边遮罩 · 源图 512×512 / GPU 512×512 · Linear · RGB 数据预览

当前算法

Nview = TransformWorldToViewDir(Nworld,true)
dir = normalize(Project2D(Nview.xy))
clipPos.xy += dir × (OutlineWidth×2 / screenSize.xy) × clipPos.w
Cull Front;ZWrite On;Offset 1,1
若 UseOutlineMask:clip(OutlineMask(uv).r - 0.1)
outlineColor = lerp(_OutlineColor.rgb,albedo.rgb×0.17,0.3)

算法原理

裁剪空间的偏移乘 clip.w 补偿透视除法,所以线宽近似按屏幕像素保持。反壳不是后处理边缘检测;模型法线与网格结构会影响轮廓连续性。Mask.R 在当前实现中是阈值裁切,不是连续乘到线宽上。

本节已使用 4×MSAA + SMAA 重拍。游戏运行时已有 TAA High;本篇非 Play 采集采用原场景编辑预览路径。屏幕宽度不等于抗锯齿:0.35/0.65 像素细线仍需覆盖率采样;旧图漏用多采样的原因与同机位对比见“截图勘误”。

贴图制作与导入思路(建议流程)

  1. 保留轮廓法线连续性,检查发片边缘与脸内面片,避免内线变成黑斑。
  2. OutlineMask 的 R 以白保留、黑去掉,需要稳定阈值区域;按线性数据导入。
  3. 以最终游戏分辨率和远近距离检查宽度。不要只在放大贴图预览中验收。

12 Overlay 艺术阴影

制作原因:用独立网格和遮罩补充发压脸、眼部阴影

恢复祀原有的 OverlayShadow 材质。它在已有颜色上做乘法调暗,并通过模板值约束接收表面。它与主光投射阴影、SDF 明暗和 LUT 颜色分别负责不同部分。

12 Overlay 艺术阴影11 屏幕宽度描边
11 屏幕宽度描边12 Overlay 艺术阴影

拖动画面或滑块 · 双击放大原图 · 左:11 屏幕宽度描边 / 右:12 Overlay 艺术阴影

实际输入贴图

发压脸 Overlay · 基础色 / Overlay 遮罩 · 源图 32×32 / GPU 32×32 · sRGB · RGB 数据预览
发压脸 Overlay · 基础色 / Overlay 遮罩 · 源图 32×32 / GPU 32×32 · sRGB · RGB 数据预览

当前算法

mask = sample(_BaseMap,uv).r
factor = lerp(1,_BaseColor.rgb,mask×_Strength)
Blend DstColor Zero:Cnew = factor × Cold
Stencil:比较 (buffer & ReadMask) 与 (Ref & ReadMask)
ZWrite Off;Cull Off;Offset -1,-1

算法原理

白色乘数 1 不改变画面,较暗乘数压低现有颜色。独立覆盖网格提供位置,贴图提供软硬形状,Stencil 避免阴影落到错误材质区域。这种艺术化阴影不会自动根据光线与发束距离计算投影。

贴图制作与导入思路(建议流程)

  1. 按接收区域准备覆盖网格,绘制 R 通道阴影遮罩并柔化边界。
  2. 配置接收材质 Stencil 和 Overlay 的 Ref/ReadMask,使发压脸与眼部各落到预期区域。
  3. 检查近景穿插、侧面漏影与转头表现,独立调节 Strength 和乘法颜色;不要把过黑归因于 SDF 一项。

13 自发光与当前完整效果

制作原因:恢复祀原本开启的发光,回到原材质结果

恢复衣料上原有的 Emission,并解除教学副本对湿润分支的临时门控。近景可观察头部饰件与肩部装饰的亮度变化:材质文件叫 cloth,不代表它只覆盖躯干布料。此次角色没有涉水湿润输入,因此没有新增湿润效果。至此所有教学阶段门控放开,未在原材质开启的功能仍不启用。

13 自发光与当前完整效果12 Overlay 艺术阴影
12 Overlay 艺术阴影13 自发光与当前完整效果

拖动画面或滑块 · 双击放大原图 · 左:12 Overlay 艺术阴影 / 右:13 自发光与当前完整效果

实际输入贴图

衣料 01 · 自发光 E · 源图 1024×1024 / GPU 1024×1024 · sRGB · RGB 数据预览
衣料 01 · 自发光 E · 源图 1024×1024 / GPU 1024×1024 · sRGB · RGB 数据预览
衣料 02 · 自发光 E · 源图 512×512 / GPU 512×512 · sRGB · RGB 数据预览
衣料 02 · 自发光 E · 源图 512×512 / GPU 512×512 · sRGB · RGB 数据预览

当前算法

emission = sample(_EmissionMap,uv).rgb × _EmissionColor.rgb × _EmissionBrightness
color += emission
wet = saturate(WetAmount) × (1-smoothstep(waterY-0.15,waterY+max(0.02,fadeHeight),worldY))
albedo *= 1 - wet×0.22
smoothness = lerp(smoothness,0.88,wet×0.7)
output = TTApplyFog(color,worldPos,era,fog)

算法原理

自发光向最终颜色额外加辐亮度。Bloom 是相机后处理对亮区的扩散,并不会自动让衣服照亮附近墙面。湿润仅改变已有表面的底色和光滑度,不会生成水膜网格。最后还有统一雾、曝光、色调映射和抗锯齿,所以截图并不等于直接显示贴图 RGB。

贴图制作与导入思路(建议流程)

  1. 为发光区域绘制独立 E 图,不要把发光亮度烘死在 D 里;颜色 E 按实际 sRGB 约定导入。
  2. 先关 Bloom 看真实图案,再开 Bloom 判断光晕;保持 HDR 强度与曝光匹配。
  3. 用下文“原 Shader / 教学完整阶段 / 原 Shader 复拍”验证最终结果,避免把教学重写产生的差异当成贴图作用。

SDF 专项:同一张脸,六个头部光角

为了看清 SDF 的贡献,此组只改变副本太阳方向。0° 表示光大致来自头部正面,正/负角沿头部右方向区分;每一对使用同一光向、姿态、机位和完整其他材质项。太阳略高于头部平面。这些是 Unity 中真实渲染的脸,不是 UV 平面算法示意。

本节六组光角已全部重拍,并同时核对材质槽头骨方向与渲染后的 GPU 主光。此前方向冲突下的旧光角图已撤出正文;不能用旧图解释正常正面/背面受光。

-90° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

-45° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

0° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

45° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

90° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

135° 光向

启用原 SDF 与 Mask仅关闭 SDF,使用半兰伯特
仅关闭 SDF,使用半兰伯特启用原 SDF 与 Mask

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 SDF,使用半兰伯特 / 右:启用原 SDF 与 Mask

读图时先比较同一角度的开关,再观察不同角度:前方 0° 下脸部大范围受光,侧向 90° 出现沿鼻侧分开的明暗,135° 背侧光下暗面增多。这组变化来自真实头骨相对光向;侧光或背光时脸与肩部仍可能因 SDF 和法线受光不同而有合理差异,不应强行调成相同颜色。

SDF 并不取代主光阴影、发压脸 Overlay 和 LUT。比如脸进入真实阴影时,即使 SDF 给出较高权重,最终 lit 仍受阴影可见度限制。原版参考中 SDF.B 还参与其他计算;当前只用 RG 的边界见后文证据。

Mask 多视角验证:相机角度与光照角度分开检查

2026-11-02 全部重拍:已同步正确头骨方向,替换本节原有 153 张截图并重新计算表格。祀固定姿态,从正面、左右 20°/40°/70°、俯视/仰视 20° 共 9 个相机角度拍摄;每角再用庭院原光照、头部左右 70° 光向拍摄 3 种状态。成对图片均来自本轮同条件的真实渲染。

“左/右相机角度”指相机位于头部前向的哪一侧,沿头骨右轴定义正角;不代表画面中鼻子朝向的左右。俯仰角相对于头部平面。每个成对对照只改变一个 Shader 开关,相机与灯光保持相同。

原 Mask:lightValue = lerp(faceSDF,halfLambert,Mask.G)
忽略 Mask:Mask.G 强制 0 → 全部使用 faceSDF
关闭 SDF:直接使用 halfLambert,Mask 此时不参与
Mask.G=0 → 使用 SDF;Mask.G=1 → 使用普通法线受光
这不是透明度 Mask,也不是轮廓宽度 Mask。

制作原因:脸中央需要保留手绘的 SDF 明暗形状,耳部及部分边缘则需要按表面法线过渡。Mask.G 为这两种结果分配权重;白色并不是“挖空”,而是选择半兰伯特结果。判断是否合适,要看这些边缘区域在斜侧光下能否与脸中央衔接。

算法原理:Mask.G 按模型原 UV 取样,当前实现不随相机角度旋转,也不跟随 SDF 的左右镜像。换相机是在查看同一遮罩的不同可见部分;换光照才会同时改变 SDF 判定和半兰伯特值。各角度的高光、Rim 等也可能随视线改变,因此应比较同一机位内的左右两张,不能把不同机位之间的所有色差都归因于 Mask。

原 Mask + SDF忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)原 Mask + SDF

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:原 Mask + SDF

放大同一原图,不增强色差;双击仍可打开完整图。

九个相机角度:同一庭院光照下逐个查看

下列缩略显示保留同一尺寸,点击可打开 1600×900 原图。对照可以看鼻翼、颧颊、脸缘和下巴;发束遮住的部分在当前角度不可见,不能据此判断该区域 Mask 失效。

左侧 70°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

左斜侧 40°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

左斜侧 20°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

正面 0°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

右斜侧 20°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

右斜侧 40°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

右侧 70°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

俯视 20°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

仰视 20°

启用原 Mask忽略 Mask(全部 SDF)
忽略 Mask(全部 SDF)启用原 Mask

拖动画面或滑块 · 双击放大原图 · 左:忽略 Mask(全部 SDF) / 右:启用原 Mask

Mask.G 实际落在模型哪里

以下仅为 GPU 诊断图:白色区域混回半兰伯特,黑色脸部区域保留 SDF,灰色为过渡。为了辨认覆盖范围,背景与非脸材质显示黑色;彩色效果对照仍保留实际庭院。标注为“可见脸部区域”的白色图是统计范围,不代表 Mask 本身。

Mask.G 真实采样可见脸材质区域
可见脸材质区域Mask.G 真实采样

拖动画面或滑块 · 双击放大原图 · 左:可见脸材质区域 / 右:Mask.G 真实采样

放大同一原图,不增强色差;双击仍可打开完整图。
正面 0° · Mask.G 真实采样
正面 0° · Mask.G 真实采样
左侧 70° · Mask.G 真实采样
左侧 70° · Mask.G 真实采样
右侧 70° · Mask.G 真实采样
右侧 70° · Mask.G 真实采样
俯视 20° · Mask.G 真实采样
俯视 20° · Mask.G 真实采样
仰视 20° · Mask.G 真实采样
仰视 20° · Mask.G 真实采样
正面 · 可见脸部区域,用于局部统计
正面 · 可见脸部区域,用于局部统计

实际差异统计:只看可见脸部

统计阈值是任一 RGB 通道相差超过 2/255,分母为这个角度真正可见的脸材质像素,包含该材质的耳部;头发、眼睛、衣物与背景不进入分母。这个指标用于定位差异明显的机位,不是美术质量分数;最终仍需看相应截图。

相机庭院原光:变化比例 / 最大差左侧光:变化比例 / 最大差右侧光:变化比例 / 最大差
左侧 70°13.96% / 85/25510.74% / 70/25525.11% / 98/255
左斜侧 40°4.14% / 79/2553.42% / 68/2559.19% / 70/255
左斜侧 20°2.25% / 69/2551.38% / 62/2554.70% / 69/255
正面 0°1.75% / 71/2552.23% / 61/2552.20% / 66/255
右斜侧 20°2.65% / 86/2554.50% / 69/2551.25% / 67/255
右斜侧 40°5.38% / 68/2558.10% / 69/2552.76% / 69/255
右侧 70°13.86% / 78/25521.86% / 70/2557.72% / 72/255
俯视 20°1.84% / 68/2552.35% / 63/2552.56% / 62/255
仰视 20°3.44% / 77/2553.42% / 71/2553.51% / 72/255

本次“忽略 Mask / 原 Mask”中,脸部变化比例最大的组合是 左侧 70° + 头部右侧光 +70°,约 25.11% 的可见脸部像素超过阈值。可在上方选择对应机位与光照查看。

看图结论:正面中央大部分 Mask.G 为黑,仍使用 SDF;白色主要落在耳部和少量脸缘,仰视还能看到下巴下缘的灰白过渡。忽略 Mask 后,耳内和耳廓失去部分按法线形成的明暗,侧面比正面更容易看出。下巴的影响只占很窄的一带,不能把整张脸的变化都算作这张 Mask 的贡献。

补充:眼睛 Matcap 的多视角验证

Matcap 与 SDF Mask 是不同输入:前者按视空间法线取颜色,后者按原 UV 混合脸部受光。此前仅凭正面机位观察到 Matcap 增量很小,本轮在同一组 9 个相机角度验证,保留原图、原强度与原相机后处理。

原完整配置仅关闭 Matcap
仅关闭 Matcap原完整配置

拖动画面或滑块 · 双击放大原图 · 左:仅关闭 Matcap / 右:原完整配置

放大同一原图,不增强色差;双击仍可打开完整图。
相机可见虹膜像素变化像素 (>2/255)区域平均差 / 255最大差 / 255
左侧 70°3131111.54313155
左斜侧 40°165312303.24662239
左斜侧 20°2087190.05494320
正面 0°2243110.03700427
右斜侧 20°2088220.13729228
右斜侧 40°1622190.15330925
右侧 70°17320.19460511
俯视 20°161213624.23780038
仰视 20°2530140.10843270

本轮 Matcap 最大差值机位为 仰视 20°,可见虹膜区域最大通道差 70/255。最大差可能只占很少的边界像素,包含抗锯齿影响,不能据此称其形成强烈的新高光。阶段 09 新增单眼特写和真实颜色项,确认主要是轻微提亮/染色;表格保留零差异,不把已有白色菱形和虹膜色归因于 Matcap。

多视角记录:正面 0° 的可见虹膜最大差为 27/255,左斜侧 40° 与俯视 20° 则分别为 39/255 和 38/255。最大差还包含少量采集边界变化;真实 Matcap 贡献需要结合单眼特写、独立颜色项和采样坐标判断,不能只凭这些最大值推断高光强度。

仰视 20° · Matcap 颜色项,关闭后处理,其余表面黑色
仰视 20° · Matcap 颜色项,关闭后处理,其余表面黑色
仰视 20° · Matcap UV,R=U,G=V,仅虹膜输出
仰视 20° · Matcap UV,R=U,G=V,仅虹膜输出

补拍完整性与路径

本轮重新采集并替换 153 张实渲 PNG,每张渲染后均核对材质槽的头骨方向;3868 项原资产/导入配置指纹一致,原场景脏标记未改变。9 个机位中,原 Shader 与完整教学副本在相同正确方向输入下存在稀疏边界差异,超过2/255的像素比例最高为 0.0423%,不宣称逐像素等价。原 Shader 自身复拍最大差为 107/255;完整局部/全图统计在下方验证报告。主篇 94 张逐层图也已重拍。

用途完整路径
多视角 PNGF:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\MaskMultiview
相机/光向/材质与文件指纹F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\mask-multiview.json
每对截图的脸部/虹膜像素统计F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\mask-multiview-validation.json
本次补充生成器F:\UnityAITest\GameTest\Tools\character_mask_multiview_document.py

完整效果中逐项关闭

以下单项开关均在正确头骨输入下重新拍摄。逐层加入会包含上游依赖,这里从完整效果中单独移除一项,查看该输入改变了什么。没有增强或后期染色;影响很小时以局部通道帮助解释。

脸部 SDF

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

SDF Mask.G(左侧全部使用 SDF)

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

独立高光法线(左侧改用漫反射法线)

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

阴影 LUT

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

发丝 Line

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

高光 RS

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

眼睛 Matcap

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

Rim

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

描边

原完整配置移除 / 替换该项
移除 / 替换该项原完整配置

拖动画面或滑块 · 双击放大原图 · 左:移除 / 替换该项 / 右:原完整配置

GPU 中间量:输入如何变成明暗

这些图在同一真实模型上直接输出中间计算量,并关闭后处理、描边和 Overlay。数据通道先进行 sRGB 反编码,使最终 PNG 的灰度近似表示 0~1 数据。颜色项保留线性到显示的正常编码。只看角色目标表面:庭院仍使用原材质。

全部中间量图已随本轮重拍。此前 SDF 后脸部大面积变暗反映的是错误方向输入,不是 SDF 的预期目标;本组使用修正输入后的真实计算结果。

基础色 Albedo
基础色 Albedo
最终漫反射世界法线 N×0.5+0.5
最终漫反射世界法线 N×0.5+0.5
高光世界法线 Nspec×0.5+0.5
高光世界法线 Nspec×0.5+0.5
纯几何/法线受光 Half Lambert
纯几何/法线受光 Half Lambert
脸部 SDF 后的 lightValue
脸部 SDF 后的 lightValue
查表得到的 RD.A
查表得到的 RD.A
乘阴影并受 P.B 限制的最终 lit
乘阴影并受 P.B 限制的最终 lit
LUT 或备用算法给出的阴影色
LUT 或备用算法给出的阴影色
加入主光和可见度后的高光
加入主光和可见度后的高光
Rim 标量权重
Rim 标量权重
P.R 原数据;有效金属还受 Kind 限制
P.R 原数据;有效金属还受 Kind 限制
P.G 高光幅度
P.G 高光幅度
P.B 手绘亮面上限
P.B 手绘亮面上限
P.A 原始光滑度输入
P.A 原始光滑度输入

材质之后:照明、阴影、雾与相机

ambient = TTScenarioProbe(position,N,era,max(0,SampleSH(N))) × AmbientStrength
colorDiffuse = diffuse × (ambient + mainColor×distanceAttenuation×0.82) × (1-metal×0.65)
color += spec × mainColor × visibility
附加光:albedo × otherColor × (halfLambert×distanceAttenuation×shadowAttenuation×0.35)
再加入 Matcap、Rim、Emission,最后 TTApplyFog

0.82、0.65、0.35 等是当前工程适配中的调校系数。角色通过 SH / 场景探针获取环境漫反射,本 Shader 没有在此处实现角色完整 PBR 环境镜面反射或皮肤体积次表面散射。模型没有 Lightmap UV 的采样路径,不应把庭院烘焙光照图直接说成角色材质贴图。

DepthOnly 和 DepthNormals 维持时代/Alpha 裁切;DepthNormals 当前输出几何法线,不读取 N/HN。ShadowCaster 向光空间投影,并加偏移;它与 SDF 艺术明暗不同。透明衣料的混合、ZWrite 和队列会影响遮挡与排序,不能只看片元函数判断最终结果。

证据层级、文件路径与完整性

层级可确认内容不能由此推出
当前工程源码与实渲本篇所有公式、参数和逐层视觉差异不等于客户端当前版本逐式完整移植
祀的源材质参数与贴图SourceParameters JSON、实际材质绑定、纹理通道与导入配置无法唯一反推原作者的 PSD、Substance 工程或 SDF 生成工具
归档 HLSL 参考Perlica/Reference 中原版片元参考;皮肤 SDF 有额外 B 通道用途第三方 AllShader_1.6.3.7z 不保证与本机客户端同版本
本篇制作流程针对当前解码设计的可执行工作步骤不是已证实的终末地内部制作流程

下面两侧均使用正确头骨输入。4×MSAA 采集中,原 Shader 自身复拍也存在少量边界像素变化,因此本版不宣称严格逐像素等价。表中保留最大差与局部误差;没有成片的材质变化,还需结合公式、逐帧输入和实渲判断。旧版在错误头骨输入下逐像素一致也不能证明受光正确。

全身:原 Shader 与教学完整阶段

教学阶段 13 + 同一正确输入原 Shader + 正确方向输入
原 Shader + 正确方向输入教学阶段 13 + 同一正确输入

拖动画面或滑块 · 双击放大原图 · 左:原 Shader + 正确方向输入 / 右:教学阶段 13 + 同一正确输入

脸部:原 Shader 与教学完整阶段

教学阶段 13 + 同一正确输入原 Shader + 正确方向输入
原 Shader + 正确方向输入教学阶段 13 + 同一正确输入

拖动画面或滑块 · 双击放大原图 · 左:原 Shader + 正确方向输入 / 右:教学阶段 13 + 同一正确输入

衣料:原 Shader 与教学完整阶段

教学阶段 13 + 同一正确输入原 Shader + 正确方向输入
原 Shader + 正确方向输入教学阶段 13 + 同一正确输入

拖动画面或滑块 · 双击放大原图 · 左:原 Shader + 正确方向输入 / 右:教学阶段 13 + 同一正确输入

核对项目结果
原文件 SHA2563868 项全部一致,包含依赖资产及 .meta
原场景脏标记False → False
GPU 截图94 张,1600×900
源 Shader 编译捕获前后无错误
比较平均差 / 255最大差 / 255差值大于 2 的像素比例最差 40×40 局部均差 / 255
full_native.png / full_full.png0.0017655590.021181%0.10938
full_native.png / full_native_repeat.png0.0006012570.005000%0.13687
face_native.png / face_full.png0.0020306720.027153%0.19062
face_native.png / face_native_repeat.png0.0008586340.011389%0.08625
cloth_native.png / cloth_full.png0.0012685600.012083%0.14021
cloth_native.png / cloth_native_repeat.png0.0029560600.027778%0.47146

采集稳定性门槛:全图平均差 <0.01/255,变化超过2/255的像素 <0.1%,每个40×40局部平均差 <1/255。最大单像素差不被抹除;特别弱的 Matcap/Rim 必须结合复拍误差和独立通道判断,不能把全部前后差值都当成效果贡献。

数值表按整张图片计算,含固定背景;另检查脸部、衣料、全身及光角,不以全图平均数替代视觉判断。原 Shader 复拍给出重复采集误差基准。正式运行代码未改动。

祀的源材质与当前适配入口

部位源参数记录的 Shader当前 Shader源绑定中当前主表面无对应入口的贴图源证据完整路径
脸部characternpr_skinCharter_Shader_EmotionMap, _HighlightMapF:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_actor_jsspsi_face_01.json
头发characternpr_hairCharter_Shader_StrokeMapF:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_actor_jsspsi_hair_01.json
衣料 01characternprCharter_Shader_ClearCoatMaskF:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_actor_jsspsi_cloth_01.json
衣料 02characternprCharter_Shader_ParallaxTexF:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_actor_jsspsi_cloth_02.json
虹膜characternpr_eyeCharter_Shader本表关注的贴图入口均有对应F:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_actor_jsspsi_iris_01.json
发压脸 Overlaycharacternpr_overlayshadowCharter_Shader/OverlayShadow本表关注的贴图入口均有对应F:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters\M_hairshadow_common_01.json

例如脸部源数据还绑定 _EmotionMap、_HighlightMap,当前 Charter_Shader 没有对应采样入口。本表只能确认源绑定存在,不能据此断言原版在所有状态均开启这些功能;也不能把本篇 14 阶段当作已覆盖原版全部分支。Overlay 则使用自己的独立 Shader,按前述乘法公式解释。

用途完整路径
交互网页F:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\index.html
单文件内嵌图版F:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\祀角色Shader逐层对比-内嵌图.html
完整 MarkdownF:\UnityAITest\GameTest\Docs\角色Shader祀实景逐层分析-20261030.md
图片路径清单F:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\图片路径清单.csv
原 ShaderF:\UnityAITest\GameTest\Assets\Art\Shader\Charter_Shader\Charter_Shader.shader
原算法F:\UnityAITest\GameTest\Assets\Art\Shader\Charter_Shader\Charter_Shader.hlsl
原 OverlayF:\UnityAITest\GameTest\Assets\Art\Shader\Charter_Shader\Charter_OverlayShadow.shader
祀材质F:\UnityAITest\GameTest\Assets\Art\Material\Si\URP
源材质参数F:\UnityAITest\GameTest\Assets\Art\Material\Si\SourceParameters
原版参考来源F:\UnityAITest\GameTest\Assets\Art\Shader\Perlica\source-provenance.json
独立教学场景F:\UnityAITest\GameTest\Assets\Art\Shader\ShaderAnalysisCharacter\SiShaderSteps.unity
独立教学 ShaderF:\UnityAITest\GameTest\Assets\Art\Shader\ShaderAnalysisCharacter\Shader\Charter_Steps.shader
Unity 截图工具F:\UnityAITest\GameTest\Assets\Art\Shader\ShaderAnalysisCharacter\Editor\CharacterShaderAnalysisCapture.cs
采集报告与全部哈希F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\capture.json
图像核对F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\validation.json
输入来源与导入配置F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\texture-inputs.json
浏览器检查F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\browser-check.json
贴图制作与 SDF 长文F:\UnityAITest\GameTest\Docs\角色贴图制作流程与SDF详解.md

局部特写的拍摄与核对

本次 11 组共 55 张特写由缩窄后的相机视锥重新渲染,不是把旧 PNG 数码放大。每组包含加入前、加入后、复拍和 GPU 中间量;逐帧头骨方向与实际主光均核对。材质强度保持原值,侧/背光另行标注。

用途完整路径
特写 PNGF:\UnityAITest\GameTest\Docs\ShaderAnalysisCharacter\DetailCloseups
机位、取景范围、阶段与光向记录F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\detail-closeups.json
逐组差异、复拍和文件核对F:\UnityAITest\GameTest\Logs\ShaderAnalysisCharacter\detail-closeups-validation.json
特写文档工具F:\UnityAITest\GameTest\Tools\character_detail_document.py

全部目标材质的贴图来源与导入

材质 / 槽位源贴图完整路径尺寸与导入采样ST 缩放 / 偏移
M_wpn_claym_0010_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Claym0010\T_wpn_claym_0010_01_D.png1024×1024,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_wpn_claym_0010_01.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Claym0010\T_wpn_claym_0010_01_N.png1024×1024,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_wpn_claym_0010_01.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Claym0010\T_wpn_claym_0010_01_P.png1024×1024,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_eyewhiteshadow_common_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_eyeshadow_01_M.png32×32,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_eyeshadow_common_03.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_eyeshadow_02_M.png32×32,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_body_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_body_01_D.png1024×1024,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_body_01.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_body_01_N.png1024×1024,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_body_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_face_01_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_body_01.mat / _ShadowLutTexF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_femaleskincolor01_lut_D.png1024×32,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_02_D.png512×512,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_02_N.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_02_P.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _EmissionMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_02_E.png512×512,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _SpecRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RS.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_02.mat / _OutlineMaskF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_lizhiyan_cloth_01_ST.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_03.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_D.png2048×2048,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_03.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_N.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_03.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_P.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_03.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_03.mat / _SpecRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_02_RS.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_hair_01_D.png2048×2048,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_hair_01_P.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_hair_03_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _SpecRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_hair_01_RS.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _SplitNormalMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_hair_01_HN.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _LineMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_hairline_03_M.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 8.0, 'y': 1.0} / {'x': -0.44999998807907104, 'y': 0.0}
M_actor_jsspsi_hair_01.mat / _OutlineMaskF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_hair_01_ST.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_iris_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_iris_01_D.png512×512,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_iris_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_body_01_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_iris_01.mat / _MatcapTexF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_matcap_08_D.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_brow_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_face_01_D.png1024×1024,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_brow_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_body_01_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_brow_01.mat / _ShadowLutTexF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_femaleskincolor03_lut_D.png1024×32,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_face_01_D.png1024×1024,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_face_01_N.png1024×1024,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _EmissionMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\common_deco_white.png68×68,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_face_01_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _ShadowLutTexF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_femaleskincolor01_lut_D.png1024×32,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _SDFLightmapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_female_face_03_SDF.png1024×1024,Default,LinearClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _SDFMaskF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_female_face_03_cm_M.png512×512,Default,LinearClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_face_01.mat / _OutlineMaskF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_face_01_ST.png128×128,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_hairshadow_common_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_hairshadow_01_M.png32×32,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_04.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_04_D.png512×512,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_04.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_04_N.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_04.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_04_P.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_04.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_04.mat / _SpecRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RS.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _BaseMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_D.png2048×2048,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _BumpMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_N.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _MetallicGlossMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_P.png2048×2048,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _EmissionMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_E.png1024×1024,Default,sRGBRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _DiffRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RD.png256×1,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _SpecRampMapF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_common_cloth_04_RS.png256×256,Default,sRGBClamp/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}
M_actor_jsspsi_cloth_01.mat / _OutlineMaskF:\UnityAITest\GameTest\Assets\Art\Texture\Si\T_actor_jsspsi_cloth_01_ST.png512×512,Default,LinearRepeat/Bilinear, mip=True{'x': 1.0, 'y': 1.0} / {'x': 0.0, 'y': 0.0}

表中有绑定不等于一定参与当前分支,需同时看前面的 Use 开关及 Kind。导入设置记录实际值;正文给出制作建议时不会偷偷改写原资源。完整参数和颜色常量保存在 capture.json。

未纳入分层的祀特效材质

实际材质 → Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_04_01.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_04_02.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_04_03.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_06_01.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_actor_jsspsi_cloth_08_01.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_06_02.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_cloth_05.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_actor_jsspsi_cloth_05_01.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_9.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_10.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_12.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_11.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_6.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_7.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_8.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_5.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_4.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_1.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_2.mat → Game/Effect/Effect_Shader
Assets/Art/Material/Si/URP/M_fx_jsspsi_toppotential_01_3.mat → Game/Effect/Effect_Shader

在 Unity 中查看与重新采集

查看独立场景 SiShaderSteps.unity,祀的复制材质里 _CAStage 为 0~13,_CADebug=0 是正常颜色。副本材质保留本次固定姿态的正确头骨方向;改变动作或旋转头骨后应重新采集,不将这个固定快照当成正式运行时修复。三机位和输入记录均在 capture.json。

需要重拍时,在 EnvironmentArtSandbox 退出 Play 后使用菜单 Tools / Time Traveler / Shader 分析 / 祀:生成庭院逐层截图。该操作重建教学副本和文档截图,不保存原场景。改写文档正文后运行 Tools/build_character_shader_document.py 可重新生成两种网页与 Markdown。

放大原图