场景 Shader · 庭院同机位逐层对照
在实际庭院中,从灰模受光逐步恢复基础色、法线、MAHS、区域调色、覆盖层和发光。每一步都由 Unity 渲染,保留同一机位与环境。
同机位滑动对比

00 灰模受光07 当前完整效果拖动画面或滑块对照 · 双击图片打开完整原图 · 左:00 灰模受光 / 右:07 当前完整效果
案例与阅读顺序
主体选用庭院中的桥边石栏及灯盒:同一张贴图包含石材、金属镶边和发光窗,适合观察不同表面响应。分区与覆盖层改用庭院原有的院墙实例,因为桥体材质本身没有开启这两项。
| 案例 | 来源材质 | 实际用途 |
|---|---|---|
| 桥边石栏 / 灯盒 | Assets/Art/Enviorment/Material/Scene/YuanyuanCourtyard/M_mod_map02_bridge+1_002_01_URP.mat | Base、Normal、MAHS、Emission;AO 强度为 0,分区/覆盖/细节/接地关闭。 |
| 院墙 | Assets/Art/Enviorment/Material/Scene/YuanyuanCourtyard/M_map02_house+1_003_01_URP.mat | 原 RGB 分区、遮罩覆盖、覆盖层法线;按实际配置恢复。 |
桥边近景中选取的具体实例为 P_mod_map02_side+1_004_03a_4e30332e。它有三个材质槽:桥栏主体,以及 Assets/Art/Enviorment/Material/Scene/YuanyuanCourtyard/M_prop_map02_light+1_003_02_URP.mat 和 Assets/Art/Enviorment/Material/Scene/YuanyuanCourtyard/M_prop_map02_light+1_003_01_URP.mat 两份灯具材质。截图逐步恢复的是镜头内全部目标材质,不把三个槽的整体变化都归因于一张桥栏贴图。
| 示例模型 | 完整路径 |
|---|---|
| 石栏 FBX | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Model\Scene\YuanyuanCourtyard\FBX\Architecture\P_mod_map02_side+1_004_03a_4e30332e.fbx |
| 石栏 Prefab | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Prefab\Scene\YuanyuanCourtyard\Architecture\P_mod_map02_side+1_004_03a_4e30332e.prefab |
| 院墙 FBX | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Model\Scene\YuanyuanCourtyard\FBX\Architecture\P_mod_map02_wall+1_009_02b_e49a2f1e.fbx |
| 院墙 Prefab | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Prefab\Scene\YuanyuanCourtyard\Architecture\P_mod_map02_wall+1_009_02b_e49a2f1e.prefab |
模型位置 / UV / 顶点色 / 法线切线
↓ EnvVert:世界空间、TBN、原 UV1 保留通道、光照 UV
↓ EnvSurface:Base → Normal → MAHS → Region → Cover / Detail → Ground / Wet → Emission
↓ EnvLighting:主光与阴影、附加光、Lightmap / SH
↓ EnvFrag:URP PBR → 时代反射补偿 → 薄叶透光 → 表面雾
↓ 相机后处理:曝光、色调映射、Bloom、抗锯齿
↓ 屏幕 PNG上图是阅读顺序。源代码中宏观法线在 Region 之前计算,MAHS 的最终系数在覆盖层之后赋值;这不等于把贴图简单一层层贴在屏幕上,而是逐步构造同一个 SurfaceData。
00 灰模受光
制作原因:先确认几何、光照与阴影
保留原模型、透明裁切与太阳/环境光,把场景表面替换为 18% 线性灰。金属度设为 0,光滑度设为 0.25,AO 设为 1,法线使用模型几何法线。这样能辨认台阶、墙面和灯柱形体,后面每一张输入图的贡献都有统一起点。

算法与原理
albedo = (0.18, 0.18, 0.18) metallic = 0; smoothness = 0.25; occlusion = 1 normalWS = normalize(geometryNormalWS) alpha 仍按原贴图裁切,避免叶片变成矩形。
01 基础色
制作原因:恢复表面的颜色与图案
加入原 _BaseMap 和 _BaseColor。桥栏上的石纹、金属饰面图案及灰色涂装开始出现。此时没有法线纹理和 MAHS 差异;画面上的明暗仍由固定光照计算,不能把高光和投影都画进基础色贴图。

00 灰模受光01 基础色拖动画面或滑块对照 · 双击图片打开完整原图 · 左:00 灰模受光 / 右:01 基础色

算法与原理
uv = baseUV × _BaseMap_ST.xy + _BaseMap_ST.zw base = SAMPLE_TEXTURE2D(_BaseMap, uv) albedo = lerp(base.rgb × _BaseColor.rgb, _BaseColor.rgb, _BaseTintCover) alpha = base.a × _BaseColor.a 若 _AlphaClip > 0.5:clip(alpha - _Cutoff)
颜色贴图按 sRGB 导入,GPU 采样时转换到线性空间再参与光照。转换遵循 sRGB 分段曲线:低值 c/12.92,其余 ((c+0.055)/1.055)^2.4。法线、MAHS 和分区遮罩是数据图,按线性方式读取,不能套这个颜色转换。
02 法线
制作原因:增加凹凸受光,保持原模型轮廓
加入原 _NormalMap。石材裂纹、接缝与金属边缘会改变受光方向,轮廓仍是原来的网格。桥体启用独立 PBR UV:基础色 ST 与法线/MAHS ST 分开计算,本材质的 _PbrUV=0,不能仅凭独立开关就称为 UV1。

01 基础色02 法线拖动画面或滑块对照 · 双击图片打开完整原图 · 左:01 基础色 / 右:02 法线

算法与原理
normalTS = UnpackNormalScale(normalTex, _NormalScale) T = normalize(tangentWS.xyz) B = cross(Ngeometry, T) × tangentWS.w normalWS = normalize(T × normalTS.x + B × normalTS.y + Ngeometry × normalTS.z)
法线图把每个纹素的小面朝向保存为编码值。Unity 按实际压缩格式解包,_NormalScale 调整扰动强度,再由 TBN 把切线空间方向变换到世界空间。光线 L 与新法线 N 的夹角改变 N·L 和镜面反射,因此能看到凹凸,而网格边缘不变。

模型几何法线最终含贴图的世界法线拖动画面或滑块对照 · 双击图片打开完整原图 · 左:模型几何法线 / 右:最终含贴图的世界法线
03 MAHS
制作原因:让石头与金属使用不同的反光规律
加入原 _MaskMap 的 R/G/B/A。R 决定金属度,G 为 AO,B 留给覆盖层高度混合,A 为光滑度。金属饰边和石材的高光范围、亮度开始分离。桥体的 _OcclusionStrength=0,所以这一步不能解释为“桥栏 AO 变强”。

02 法线03 MAHS拖动画面或滑块对照 · 双击图片打开完整原图 · 左:02 法线 / 右:03 MAHS





算法与原理
metallic = saturate(mask.r × _MetallicScale) smoothness = saturate(mask.a × _SmoothnessScale) occlusion = lerp(1, mask.g, _OcclusionStrength) height = mask.b // 供覆盖竞争读取,不会推移顶点 感知粗糙度 r = 1 - smoothness;微表面参数 α ≈ r²
PBR 将表面看作许多微小镜面。光滑度越高,微面朝向越集中,高光越窄。金属度改变漫反射/镜面能量分配与镜面颜色;它不是简单的“亮度开关”。以下是理解 GGX 的核心关系,URP 实现另有数值钳制和优化:
H = normalize(L + V) D_GGX = α² / [π × ((N·H)² × (α²-1) + 1)²] F0 = lerp(0.04, albedo, metallic) F_Schlick = F0 + (1-F0) × (1-V·H)^5 镜面项由 D、F、可见性/遮蔽项共同决定。
AO 主要抑制间接光的可达性,不是投射阴影。本桥体的有效 AO 通道是白色,即 1;贴图 G 中有数据也不会在强度为 0 时作用于画面。
04 分区调色
制作原因:用低频 RGB 遮罩控制局部风化与涂色
院墙使用 _RegionMask 给同一套平铺材质叠加局部色调。R、G、B 分别控制三个区域,不是 MAHS 的金属/AO/高度。遮罩与原保留 UV1 对齐;颜色参数的 A 是覆盖权重。此处只加入原资产的区域参数,未涂一张临时遮罩制造效果。

03 MAHS04 分区调色拖动画面或滑块对照 · 双击图片打开完整原图 · 左:03 MAHS / 右:04 分区调色

算法与原理
region = sample(_RegionMask, regionUV × ST + offset) k = _RegionScale.xyz w = saturate(k/2 + lerp(-k, 1, region + _RegionOffset.xyz)) × regionColorAlpha C = lerp(lerp(lerp(Cbase, Cb, wb), Cg, wg), Cr, wr) S = 同顺序混入 (1 - RegionRoughness) M = 同顺序混入 RegionMetallic
该墙的 RegionScale=0,因此 w 简化为 saturate(mask+offset) × 颜色 A。覆盖顺序为 B → G → R,R 的优先级最高。分区会同时修改颜色、粗糙度和金属度,因而差异不只体现在色相上。

05 覆盖层与细节
制作原因:用另一套表面输入替换局部颜色、遮罩和法线
院墙的 _LayerMode=2,覆盖率读取 _LayerBlendMap 的 R 通道;_LayerAmount=1,覆盖层法线强度倍率为 0.5。覆盖区不只改变颜色,还同步改变 MAHS 与法线。整个庭院中景观石的细节法线也在此阶段恢复。

04 分区调色05 覆盖与细节拖动画面或滑块对照 · 双击图片打开完整原图 · 左:04 分区调色 / 右:05 覆盖与细节




算法与原理
coverage = saturate(_LayerAmount × sample(_LayerBlendMap).r) layerUV = (worldPos.x, -worldPos.z) × _LayerBase_ST.xy + _LayerBase_ST.zw albedo = lerp(baseAlbedo, layerAlbedo, blend) MAHS = lerp(baseMAHS, layerMAHS, blend) normalWS = normalize(lerp(baseNormalWS, layerNormalWS, blend))
本墙 _HeightBlend=0,实际 blend=coverage。Shader 同时支持高度竞争,但它不是本墙这张图出现覆盖的原因。启用该可选分支时,用加权高度决定哪一层穿出:
hb = baseHeight × (1-coverage); hl = layerHeight × coverage threshold = max(hb, hl) - max(transition, 0.001) wb = max(0, hb-threshold) × (1-coverage) wl = max(0, hl-threshold) × coverage blend = lerp(coverage, wl/max(wb+wl,0.0001), HeightBlend)

细节法线的当前实现是切平面扰动:将小法线的 XY 经 T/B 变成世界扰动 g,再去掉沿已有法线的分量。宏观 NRO 与基础细法线的合成才使用 RNM;这两个分支不能混称。
g = T × detailNormal.x + B × detailNormal.y Ndetail = normalize(N + g - N × dot(N,g)) 宏观 NRO 分支:NcombinedTS = BlendNormalRNM(NmacroTS, NfineTS)
06 接地与湿润
制作原因:如实保留当前场景没有开启的功能
这组庭院案例的接地融合未启用,环境湿润值为 0,因此阶段 05 与 06 没有可归因于接地/湿润的可见增量。这里仍保留真实对比,避免把测试用绿色底图或“全湿”参数当成原场景的效果。

05 覆盖与细节06 接地与湿润拖动画面或滑块对照 · 双击图片打开完整原图 · 左:05 覆盖与细节 / 右:06 接地与湿润
算法与原理
groundUV = (positionWS.xz - GroundRect.xy) / GroundRect.zw groundY = colorHeight.a × heightRange.y + heightRange.x b = inside × GroundBlend × saturate(1 - max(y-groundY, 0)/GroundWidth) wetAlbedo = albedo × lerp(1, 1-WetDarkening, Wetness) wetSmoothness = lerp(smoothness, WetSmoothness, Wetness)
接地方案需要有效的 GroundRect 与地形缓存贴图。它依世界 XZ 查地面颜色和高度,越靠近地面越多采用地面的表面属性。湿润则是材质响应近似:降低漫反射底色、提高光滑度;它本身不构造水膜网格,也不求解液体流动。
07 自发光与完整表面
制作原因:恢复灯盒发光,回到原材质配置
最后加入原 _EmissionMap × _EmissionColor,并恢复有需要的薄叶透光。灯盒明亮区域由真实发光遮罩控制,Bloom 只是同一相机后处理对亮部的扩散。此时所有阶段开关都恢复到原材质实际启用的状态;未开启的功能仍然关闭。

06 接地与湿润07 当前完整效果拖动画面或滑块对照 · 双击图片打开完整原图 · 左:06 接地与湿润 / 右:07 当前完整效果

同一栏体另外两个灯具材质槽的自发光输入如下;完整槽位及源贴图路径列在文末。


算法与原理
emission = sample(_EmissionMap, baseUV).rgb × _EmissionColor.rgb color = UniversalFragmentPBR(InputData, SurfaceData) color += TTReflectionCorrection(...) leafTransmission = albedo × mainLightColor × saturate(-N·L) × transmission × shadow × attenuation output = TTApplyFog(max(color, 0), positionWS, era, fog)
自发光是额外的出射辐亮度,亮区不需要主光照到才显示。它是否照亮邻近墙体取决于 GI / 烘焙或额外灯光,发光贴图和 Bloom 本身不能自动替代这些照明机制。

分层通道 · 与全景相同机位
这些图片由分析副本直接输出计算结果,关闭曝光、色调映射和 Bloom 等后处理。金属度、AO、光滑度、权重是 0~1 数据;法线按 N×0.5+0.5 编码。天空和水体仍走自己的材质,只看场景物件表面即可。












表面之后:照明、反射与雾
上面的每一步保持相同照明,使材质输入的影响可比较。下图另作一次消融实验:只把目标场景材质的漫反射 GI 输入设为 0,实时光、镜面环境反射、阴影与后处理保持一致。

移除漫反射 GI 输入保留当前 GI 输入拖动画面或滑块对照 · 双击图片打开完整原图 · 左:移除漫反射 GI 输入 / 右:保留当前 GI 输入
surface = { albedo, normal, metallic, smoothness, AO, emission }
lighting = directLights + diffuseGI + specularEnvironment + emission
Cout = Csurface × transmittance + fogColor × (1 - transmittance)EnvLighting 根据 Lightmap 关键字读取光照贴图或 SH,再经过时代光照入口。GI 通道截图只证明当前输入确实参与渲染,不证明新一轮庭院局部烘焙已成功;已有 local-gi.json 中该次烘焙仍记录为失败。本篇不改烘焙资产。
反射来自当前环境与探针。雾在表面着色之后处理,随距离、高度和局部雾区改变透射率;它不应被混进 Base Color 贴图。水面上方的特效雾属于另一套表现,本篇不把它当作场景表面贴图。
查看仅移除场景表面雾的真实对照

移除 TTApplyFog保留 TTApplyFog拖动画面或滑块对照 · 双击图片打开完整原图 · 左:移除 TTApplyFog / 右:保留 TTApplyFog
只作用于场景 Shader;天空、水材质和独立后处理雾仍按原配置工作。因此不要把这张图当作全场景关闭全部雾。
文件路径与可复核记录
最终捕获:通过。原文件最终内容与截图前一致;原场景仍保持打开,其脏标记未改变。
| 核对 | 结果 |
|---|---|
| 生产文件 SHA256 | 338 个文件前后一致 |
| 实渲图片 | 62 张,1600×900 |
| 全副本场景 Shader 材质/槽位 | 232 / 1286(不是每个都在本机位可见) |
| 原 Shader vs 最终阶段,8 bit 平均绝对差 | 0.003040 / 255;差值大于 2 的像素 0.0329% |
| 原 Shader 重复捕获基准差 | 0.002950 / 255;差值大于 2 的像素 0.0310% |
已关闭相机抖色;仍列出重复截图基准来区分渲染细微变化与算法差异,不宣称原图和副本逐像素完全相等。图中的全景/近景均为直接输出,未对效果图做后期涂画或人工增强。
| 用途 | 完整路径 |
|---|---|
| 可交互图文 | F:\UnityAITest\GameTest\Docs\ShaderAnalysisCourtyard\index.html |
| 单文件内嵌图版 | F:\UnityAITest\GameTest\Docs\ShaderAnalysisCourtyard\庭院Shader逐步对比-内嵌图.html |
| Markdown 总文档 | F:\UnityAITest\GameTest\Docs\场景Shader庭院实景逐步分析-20261029.md |
| 生产 Shader(只读) | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Shader\Scene\URP\Scene_Shader.shader |
| 生产表面算法(只读) | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Shader\Scene\URP\SceneInput.hlsl |
| 独立分析 Shader | F:\UnityAITest\GameTest\Assets\Art\Enviorment\ShaderAnalysisCourtyard\Shader\Scene_Shader_Analysis.shader |
| 独立分析 HLSL | F:\UnityAITest\GameTest\Assets\Art\Enviorment\ShaderAnalysisCourtyard\Shader\SceneInput_Analysis.hlsl |
| 独立场景 | F:\UnityAITest\GameTest\Assets\Art\Enviorment\ShaderAnalysisCourtyard\CourtyardShaderSteps.unity |
| 截图工具 | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Scripts\Editor\CourtyardShaderAnalysisCapture.cs |
| Unity 捕获报告 | F:\UnityAITest\GameTest\Logs\ShaderAnalysisCourtyard\capture.json |
| 图像与文件校验 | F:\UnityAITest\GameTest\Logs\ShaderAnalysisCourtyard\validation.json |
| 贴图来源清单 | F:\UnityAITest\GameTest\Logs\ShaderAnalysisCourtyard\texture-inputs.json |
两份案例材质的全部贴图路径
| 案例 / 参数 | 源贴图路径 | 导入 |
|---|---|---|
| bridge / _BaseMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_mod_map02_bridge+1_002_01_D.png | Default / sRGB |
| bridge / _NormalMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_mod_map02_bridge+1_002_01_Normal.png | NormalMap / Linear |
| bridge / _MaskMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_mod_map02_bridge+1_002_01_MAHS.png | Default / Linear |
| bridge / _EmissionMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_mod_map02_bridge+1_002_01_EmissionMask.png | Default / Linear |
| wall / _BaseMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_trim_com_concrete+1_001_04a_D.png | Default / sRGB |
| wall / _NormalMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_map02_house+1_003_01_Normal.png | NormalMap / Linear |
| wall / _MaskMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_map02_house+1_003_01_MAHS.png | Default / Linear |
| wall / _RegionMask | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_mod_map01_building+1_019_14_rgb_M.png | Default / Linear |
| wall / _LayerBlendMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_mod_map01_building+1_019_14_M.png | Default / Linear |
| wall / _LayerBase | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_trim_com_concrete+1_001_04a_D.png | Default / sRGB |
| wall / _LayerNormal | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_map02_house+1_003_01_Cover_Normal.png | NormalMap / Linear |
| wall / _LayerMask | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_map02_house+1_003_01_Cover_MAHS.png | Default / Linear |
| lamp_shell / _BaseMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_prop_map02_light+1_003_01_D.png | Default / sRGB |
| lamp_shell / _NormalMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_02_Normal.png | NormalMap / Linear |
| lamp_shell / _MaskMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_02_MAHS.png | Default / Linear |
| lamp_shell / _EmissionMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_02_EmissionMask.png | Default / Linear |
| lamp_core / _BaseMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\Source\T_common_glass+1_001_07_D.png | Default / sRGB |
| lamp_core / _NormalMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_01_Normal.png | NormalMap / Linear |
| lamp_core / _MaskMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_01_MAHS.png | Default / Linear |
| lamp_core / _EmissionMap | F:\UnityAITest\GameTest\Assets\Art\Enviorment\Texture\Scene\YuanyuanCourtyard\URP\M_prop_map02_light+1_003_01_EmissionMask.png | Default / Linear |
在 Unity 的独立分析场景中,复制材质的“分析阶段 0-7”控制累积步骤,“分析通道 0=正常”选择上面的通道。原工程场景无需添加这些字段。需要重新捕获时使用 Time Traveler → Environment → Shader Analysis → 庭院实景逐步截图(独立副本);在原地编场景退出 Play 后运行。
此前的客栈单体文档保留作为测试输入示例,并已更正“PlayMode”和“完整效果”的措辞。本篇是按实际庭院、同机位滑动对照重新制作的阅读入口。