[docs][feat]: 补充微信小游戏渲染与卡牌 Shader 文档

This commit is contained in:
2026-08-13 13:55:05 +08:00
parent 6fa3ea4bf1
commit 8cc42b823a
4 changed files with 415 additions and 0 deletions

View File

@@ -35,6 +35,7 @@ UnityAI 是 StrayFog 框架的通用工作流与知识库。它面向 AI 工具
- [相机系统](modules/frameworks/相机系统/README.md)
- [资源管理](modules/frameworks/资源管理/README.md)
- [资源管理使用指南](modules/frameworks/资源管理/使用指南.md)
- [微信小游戏渲染规则](modules/frameworks/微信小游戏渲染规则.md)
- [场景系统](modules/frameworks/场景系统.md)
- [网络系统](modules/frameworks/网络系统.md)
- [数据配置框架](modules/frameworks/数据配置框架.md)
@@ -58,6 +59,7 @@ UnityAI 是 StrayFog 框架的通用工作流与知识库。它面向 AI 工具
## 业务参考
- [PlayBalloons 业务参考](business-reference/playballoons/):外部项目案例,不作为框架规范。
- [FFCard_WeChat 卡牌 Shader 框架](business-reference/ffcard-wechat/卡牌Shader框架.md):项目业务侧 URP 卡牌 Shader 与 Y 轴深度实现。
## 文档关联

View File

@@ -0,0 +1,193 @@
# FFCard_WeChat 卡牌 Shader 框架
## 一、定位
这套 Shader 是 `FFCard_WeChat` 的卡牌业务实现,面向 Unity `2022.3.29f1`、URP `14.0.11` 和微信小游戏。它不属于 `StrayFogCore`,也不作为其他项目必须遵循的通用框架。
实现位于:
```text
Assets/Game/GameHFResource/CN/Shaders/HFShader/
├── HLSL/HFMath.hlsl
├── HLSL/HFCardCommon.hlsl
├── HFCardUnlitOpaque.shader
├── HFCardUnlitCutout.shader
├── HFCardUnlitTransparent.shader
├── HFCardUnlitYDepthCutout.shader
└── Runtime/
```
`HFCardBack.shadergraph` 不在 v1 的迁移范围内。
### HFMath 通用数学库
`HLSL/HFMath.hlsl` 只封装无纹理、无材质属性和无卡牌资源依赖的纯算法。当前包含矩形遮罩与重映射、扩张 UV 还原、单位 Quad 按 UV 向外扩张、矩形外缘距离、RG 法线重建并转换到世界空间、自动扫光带、错峰正弦脉冲、交叉星芒、Rec.601 亮度计算、径向/定向进度遮罩和通用溶解遮罩。
每个函数在源码中必须说明功能、返回值或参数约束以及适用场景。卡牌窗口坐标、宝石位置、纹理采样、材质 CBUFFER 和最终颜色合成继续保留在业务 HLSL 中;不得把具体美术资源的 UV 常量写入 `HFMath.hlsl`。
## 二、Shader 选择
| Shader | Queue | ZWrite | Pass | 用途 |
|---|---:|---|---|---|
| `HF/Card/UnlitOpaque` | Geometry | On | Forward、DepthOnly、ShadowCaster | 完全不透明卡牌 |
| `HF/Card/UnlitCutout` | AlphaTest | On | Forward、DepthOnly、ShadowCaster | 有透明裁剪边缘的卡牌 |
| `HF/Card/UnlitTransparent` | Transparent | Off | Forward | 半透明卡牌,依赖透明排序 |
| `HF/Card/UnlitYDepthCutout` | AlphaTest | On | Forward、DepthOnly、ShadowCaster | 需要按 Y 轴产生真实深度遮挡的裁剪卡牌 |
| `HF/Card/StylizedPremiumYDepthCutout` | AlphaTest | On | Forward、DepthOnly、ShadowCaster | 带边框高光、视角闪面和边缘光的精品卡牌 |
| `HF/Card/StylizedSurfaceCutout` | Geometry | On | Forward、DepthOnly、ShadowCaster | 不透明背景、手绘浮雕卡框、糖果宝石与自动扫光;使用标准物体变换,不包含 Y 轴深度偏移 |
普通半透明卡牌优先使用透明排序;只有需要写入深度并产生真实遮挡时才使用 YDepthCutout。
## 三、材质属性契约
| 属性 | 含义 |
|---|---|
| `_BaseMap` | 卡牌主纹理,材质级配置,不建议逐帧通过 MPB 替换 |
| `_BaseColor` | 主颜色乘色 |
| `_Alpha` | 最终 Alpha 乘数 |
| `_Cutoff` | Cutout 裁剪阈值 |
| `_HFYDepthScale` | Pivot 世界 Y 到附加世界 Z 的缩放 |
| `_HFYDepthOffset` | 附加世界 Z 的偏移 |
| `_Cull` | 剔除模式 |
四个 Shader 使用同一套 `UnityPerMaterial` CBUFFER 布局。YDepthCutout 的顶点公式固定为:
```text
最终世界 Z = Transform 后的世界 Z + 物体 Pivot 世界 Y × _HFYDepthScale + _HFYDepthOffset
```
Y 深度项作为附加偏移,不覆盖 Transform 后的世界 Z,因此卡牌会完整跟随物体 XYZ 位移与旋转。`_HFYDepthScale` 可取正值或负值,用来匹配相机朝向和遮挡顺序。Forward、DepthOnly 和 ShadowCaster 复用同一顶点变换与 Alpha Clip。
精品卡牌 Shader 默认使用程序化边框与闪面,不需要额外贴图即可预览。启用 `_HF_EFFECT_MASK` 后,`_EffectMask` 的 R、G、B 通道分别控制边框、闪面和边缘光区域;这是一个 Local Keyword,只有确实需要美术遮罩的材质才启用。`_ShimmerProgress` 用于由业务动画控制闪面位置,不依赖全局时间。
参考贴图位于 `HFShader/ReferenceTextures/`:
- `HFCardPremium_BaseMap_Reference.png`:完整卡面构图和材质层次参考。
- `HFCardPremium_EffectMask_Reference.png`:与 BaseMap 对应的 RGB 分区示例,不作为量产卡面的最终精确遮罩。
EffectMask 必须按线性数据纹理导入,关闭 sRGB;为避免通道串色,参考遮罩关闭纹理压缩。BaseMap 与 EffectMask 都使用 Clamp,避免卡牌边缘发生 Repeat。
手绘卡框使用 `StylizedSurfaceCutout`。它保持 `Cull Off`,并在一个 Forward Pass 内根据面朝向选择正反面:
- 正面等价 `Cull Back`,只显示 `_BaseMap`,按 BaseMap Alpha 裁剪,并叠加浮雕、宝石和扫光。
- 背面等价 `Cull Front`,只显示 `_BackgroundMap`;背面 U 轴自动翻转,并以 BackgroundMap Alpha 定义卡背透明轮廓。外描边按该 Alpha 轮廓向外膨胀,不再固定使用完整矩形。
- `_RoleMap` 使用固定内容映射(卡面 UV 的 X `0.055~0.918`、Y `0.315~0.940`),角色显示窗口扩展到 Y `0.958`,以覆盖实际约为 `0.953` 的顶部弧形开口并预留抗锯齿余量。角色不再读取 HighlightMap 黑色区域作为 Mask,而是通过 BaseMap 自身 Alpha 透明开口显示;BaseMap 非透明卡框始终覆盖在角色上方。不提供角色位移属性或运行时 Property ID。
- `_HighlightMap`:R 为宽高光、G 为锐高光、B 为扫光与框体范围;Shader 从 RGB 邻域高度差重建卡框浮雕法线。
HighlightMap 必须关闭 sRGB、MipMap 和纹理压缩,并使用 Clamp。新版 `CardKuang_HighlightMap1.png` 与 `CardKuang_BaseMap.png` 均为 `792×1112`,用于新的金色边框、顶部铭牌、中央黑色宝石和右下角金属砝码。HighlightMap 必须从 BaseMap 同坐标像素确定性派生,不能使用重新绘制或生成式近似轮廓:R 由同位置材质亮度生成宽高光,G 由同位置亮度梯度生成锐高光,B 由 BaseMap Alpha 与材质色差生成框体和扫光范围。当前信息图有效像素全部位于 BaseMap 有效 Alpha 内。正面依次合成 RoleMap、BaseMap 和 HighlightMap,背面只采样 BackgroundMap;不再依赖已失效的 NormalMap 和 MaterialMask。`_ReliefDepth` 控制从 HighlightMap RGB 邻域重建的卡框法线、倒角明暗和凹槽压暗,但不修改顶点或任何采样 UV。基础高度明暗只读取当前 UV,重建法线限制为轻微坡面并仅影响局部高光,避免深度为 `1` 时因强侧光造成卡框视觉偏移。金属框继续使用宽窄双层高光。B 通道直接参与扫光范围,不再被其他缺失遮罩清零。中央宝石位置、强度、饱和度、亮度、呼吸速度和闪光频率均为 Shader 内固定美术预设,不再开放材质属性或运行时 Property ID。
外描边通过 UV 方向把单位 Quad 四角分别向外平移,再重映射卡面 UV。它不再围绕物体原点缩放顶点,因此改变 `_OutlineWidth` 只会增加卡牌边界外侧的几何空间,`_BaseMap` 的世界位置和尺寸保持不变;该实现要求使用宽高为 `1` 的四顶点 Quad。正面仅使用 BaseMap 的真实 Alpha 外缘,并把卡面 UV 的 X `0.045~0.955`、Y `0.275~0.988` 定义为禁止描边的内部角色开口及其最大膨胀保护区;该范围已覆盖 `Outline Width=12` 的采样距离,同时在 0~1 UV 最外侧保留真正外轮廓的描边空间。因此 BaseMap 中间可以使用透明 Alpha,而角色窗口和顶部铭牌两侧的弧形开口不会生成内描边。背面使用完整矩形卡背轮廓。正面轮廓在目标宽度的 `25%`、`50%`、`75%` 和 `100%` 四层分别进行八方向采样,形成从内到外的连续膨胀,避免只在最大半径复制卡框直边而产生孤立细线。正反面共用一个开关、一组 HDR 颜色和一个 `Outline Width`(范围 `1~12`),内部按约 3 倍 BaseMap 像素换算,保证缩小后的 UI 仍然可见。`_SweepEnabled` 打开后由 Shader 使用 `_Time` 自动循环,不需要业务代码逐帧写入进度。专用 ShaderGUI 仅在勾选 Auto Sweep 时显示颜色、速度、强度、宽度、柔边和方向参数。DepthOnly 与 ShadowCaster 复用扩张后的正反面轮廓。
卡牌溶解使用 `_DissolveMap`,并通过材质面板开放的 Tiling/Offset(`_DissolveMap_ST`)独立控制噪声密度和位置;变换顺序为先把卡牌 UV 限制到 `0~1`,再应用 Tiling/Offset,因此外描边区域与卡牌边缘使用一致的溶解采样。`_DissolveChannel` 在 R/A 通道中选择灰度;低值区域随 `_DissolveProgress` 从 `0` 到 `1` 先消失。`_DissolveEdgeColor` 为 Add HDR 消散边缘颜色,其 Alpha 作为附加强度,`_DissolveEdgeWidth` 控制噪声值空间中的边缘宽度。正面、背面及其外描边都应用相同的溶解裁剪和 HDR 边缘,DepthOnly 与 ShadowCaster 复用同一遮罩,避免画面已消失但深度或阴影仍残留。卡框示例材质为 `HF_CardKuang_StylizedSurface.mat`。
Canvas `Image` 使用独立 Shader `HF/Card/StylizedSurfaceUI`。它复用场景版卡框、角色、卡背、浮雕、高光、扫光、双面 Alpha 外描边和卡牌溶解算法,但不包含 DepthOnly 或 ShadowCaster,并改用 UI Transparent、Stencil、RectMask2D、CanvasGroup 顶点颜色和 `unity_GUIZTestMode`。UI 溶解沿用 `_DissolveMap` 的 R/A 通道、Tiling/Offset、进度、Add HDR 边缘颜色和边缘宽度;正面、背面及外描边共享同一裁剪结果,保留像素再继续接受 UI 压暗、分界线和粒子合成。UI Image 应使用中心 Pivot 和完整 `0~1` UV 的未打包 Sprite,卡牌内容仍由材质的 BaseMap、BackgroundMap 与 RoleMap 提供。
UI 版最终输出在进度线上方叠加压暗锁定层,不增加网格,也不把画面转换成灰度;`_UIRevealDarkness` 在 `0~1` 控制未恢复区域的压暗程度,0 保持原亮度,1 为全黑,默认 0.55。进度从 `0` 到 `1` 时蒙板由下向上解除,分界效果仅在进度严格位于 `0~1` 中间时显示。分界线不再包含电弧、Noise、脉冲、程序亮芯或横向滚动;`_UIRevealLineMap` 使用 R 通道直接决定分界显示形状,其基础贴图 Y=0.5 固定对应当前中线。分界线高度参数已移除,贴图 Tiling 围绕 UV 0.5 缩放并控制显示大小,Offset 只提供额外偏移;`_UIRevealLineColor` 提供 Add HDR 着色。`_UIRevealParticleMap` 使用 R 通道提供向卡牌本地 UV 下方流动的粒子形状,其 Tiling/Offset 控制粒子重复密度和排列。`_UIRevealParticleMaskMap` 使用 R 通道完全控制粒子的显示范围与拖尾,遮罩局部 Y=0.5 始终跟随中线,并独立开放 Tiling/Offset。公开参数收敛为压暗强度、分界线 HDR 颜色、粒子 HDR 颜色/速度;动画时间固定读取 `HFTimeManager` 写入的全局 `_UnscaledTime`。粒子属于 Canvas Image 内部像素效果,随 Image 旋转并受卡牌轮廓、RectMask2D 和 Stencil 裁剪。
该材质不提供 Y 轴深度缩放或偏移,顶点只使用标准物体 Transform。SurfaceMap、艺术光、Cull 和内部高光参数隐藏并使用固定预设。
### 闪卡范围材质
“扇卡类型”中的压扇、合扇和劈扇范围提示使用三份职责独立的 Shader,便于后续按类型单独修改美术效果;纹理采样、预乘透明合成和进度算法由公共 HLSL 复用:
```text
Assets/Game/GameHFResource/CN/Shaders/HFShader/
├── HFFanRangePress.shader
├── HFFanRangeMerge.shader
├── HFFanRangeCleave.shader
└── HLSL/HFFanRangeCommon.hlsl
Assets/Game/GameHFResource/CN/Material/FanRange/
├── HF_FanRange_Press.mat
├── HF_FanRange_Merge.mat
└── HF_FanRange_Cleave.mat
```
| Shader | 展开方式 | 主体/底图 | 亮芯/填充 |
|---|---|---|---|
| `HF/FanRange/Press` | 从纹理中心径向扩张 | `yashan2.png` | `yashan1.png` |
| `HF/FanRange/Merge` | 程序化扇区,开角可调,沿固定最大半径向外延伸 | BaseMap(`01.png`) | 程序化 Mask × HDR 填充颜色 |
| `HF/FanRange/Cleave` | 程序化窄扇,宽度可调,从下方向上延伸 | BaseMap(`pishan1.png`) | 程序化 Mask × HDR 填充颜色 |
公共材质属性契约如下:
| 属性 | 传入内容 |
|---|---|
| `_BodyMap` / `_GlowMap` | 仅旧版压扇使用的主体与独立亮芯贴图;合扇和新版劈扇不使用这两个属性 |
| `_BaseMap` | 合扇使用角度×半径极坐标 UV 的完整扇面底图;劈扇使用宽度重映射后的纵向 UV 底图;两者的 Alpha 都只控制底图自身,不参与程序化延伸 |
| `_BaseColor` | 合扇和劈扇使用;BaseMap 的 HDR 乘色,RGB 控制染色与增亮,Alpha 只控制底图自身透明度,不影响填充、运动层和描边 |
| `_FillColor` | 合扇和劈扇使用;不依赖填充贴图,程序化当前范围直接使用该 HDR 颜色和 Alpha |
| `_FillSideShrink` | 合扇和劈扇使用;只让 HDR 填充从左右等量向中心缩减,`0` 保持完整,`0.2` 左右各缩减 10%,`1` 完全隐藏;运动层和填充描边同步跟随,BaseMap 不变 |
| `_FillOutlineColor` / `_FillOutlineWidth` | 合扇在当前填充半径内侧绘制 HDR 弧面描边;劈扇在当前向上延伸终点内侧绘制 HDR 端面描边 |
| `_MotionMap` | 独立动态贴图;压扇围绕中心缩放,合扇沿极坐标半径向外滚动,劈扇沿宽度修正后的 UV Y 向上滚动;合扇和劈扇均覆盖在实际可见的 HDR 填充层上 |
| `_Fill01` | 压扇控制旧版主体显现;合扇控制 HDR 填充向外延伸;劈扇控制 HDR 填充从下向上延伸;均为 `0` 隐藏、`1` 完整显示 |
| `_BottomUpCull01` | 仅合扇使用;`0` 完整显示,`1` 完全剔除,沿扇柄到外圆弧方向推进并保持扇形弧面边界;统一裁切底图、填充、运动层和描边 |
| `_FillSoftness` | 合扇只柔化 BaseMap 最终外圆边缘,劈扇只柔化 BaseMap 最终顶边;不影响两者的 HDR 填充、运动层或填充描边;压扇保持旧版语义 |
| `_SectorAngle` | 仅合扇使用,控制完整开角(度);最大边长固定为归一化 `1`,不作为材质参数开放 |
| `_Width` | 仅劈扇使用;以 UV X=`0.5` 为中心控制完整显示宽度,`0` 完全隐藏,`1` 使用底图完整宽度 |
| `_NineSliceBorder` | 仅劈扇使用;BaseMap 九宫格左、右、下、上边距,单位为源纹理像素;默认 `(30,30,30,30)`,四角和边线保持比例,中间区域承担 Transform 非等比拉伸 |
| `_MotionChannel` / `_MotionColor` | 合扇和劈扇使用;选择 MotionMap 的 R 或 A 作为强度,并用 HDR 颜色控制最终运动层颜色与 Alpha |
| `_MotionSpeed` | 压扇为每秒缩放循环次数;合扇为每秒沿半径向外滚动的极坐标 UV 单位;劈扇为向上滚动速度 |
| `_MotionScaleMin` / `_MotionScaleMax` | 仅压扇使用,控制动态贴图单向循环缩放的最小值与最大值 |
三份 Shader 固定使用 `Transparent+200`、`ZTest Always`、`ZWrite Off`、`Cull Off` 和预乘 Alpha 混合 `Blend One OneMinusSrcAlpha`,因此只用于始终显示在卡牌上方的范围贴片,不参与深度、阴影或卡牌本体渲染。每份 Shader 只有一个 Forward Pass;合扇和劈扇分别使用独立业务 HLSL,便于后续按类型单独调整轮廓算法。合扇与劈扇都只采样 BaseMap 和 MotionMap 两张纹理,HDR 填充不增加纹理采样。
压扇与劈扇的亮芯层不参与进度裁切。合扇扇柄中心固定为 UV `(0.5, 0)`,最大半径固定为 `1`,当前半径直接等于 `_Fill01`。由于合扇渲染几何只使用原 Quad 上半区,实际几何宽高比为 `2:1`,极坐标内部固定将 X 距离乘 `2` 进行角度补偿;因此 `_SectorAngle=90` 对应完整圆的 `1/4`,左右各偏离中心线 `45` 度。合扇按“程序化 HDR 填充 → 运动层 → 完整 BaseMap 底图 → 当前弧面 HDR 描边 → 从下往上弧形剔除”的顺序合成,使底图边框和纹理细节覆盖在填充之上。`_FillSoftness` 只向内柔化 BaseMap 的最终外圆边缘;HDR 填充推进边缘、运动层和弧面描边仅使用屏幕导数进行最低限度的抗锯齿,不读取该材质参数。弧面描边只向当前半径内部占宽,不越过进度边界。`_BottomUpCull01` 对最终预乘颜色和 Alpha 统一裁切,因此所有合成层同步消失。
动态层使用 `HFTimeManager` 每帧写入的全局 `_UnscaledTime`,动画不受 Unity `Time.timeScale` 影响。压扇动态贴图从最小值单向放大到最大值后直接重播;合扇在极坐标图的半径轴上向外滚动;劈扇沿原始 UV Y 向上滚动。合扇动态层在 HDR 填充之后合成,其最终 Alpha 乘以填充层的实际 Alpha,因此只覆盖 `_Fill01` 已经延伸且 `_FillColor.a` 可见的区域;不再依赖 BaseMap Alpha,也不会继承 `_FillSoftness` 的底图外缘柔化。
新版劈扇按“从下向上的程序化 HDR 填充 → 向上运动层 → 完整 BaseMap 底图 → 当前端面 HDR 描边”的顺序合成,使底图斜边、边框和内部纹理覆盖在填充之上;不提供 `_BottomUpCull01`。`_Width` 以中心线收窄程序化范围,同时重映射底图和运动纹理 UV;`_FillSideShrink` 则只在该范围内部继续对 HDR 填充左右收拢,不改变 BaseMap。与合扇一致,劈扇的向上延伸、运动层和端面描边只读取程序化宽度与进度,不读取 BaseMap 的颜色或 Alpha;BaseMap Alpha 仅控制底图自身轮廓。BaseMap 在采样前使用 `_NineSliceBorder` 和物体 X/Y 缩放执行九宫格重映射,四角及四边保持参考尺寸,只有中间区域吸收非等比拉伸;该逻辑不改变程序化填充或 MotionMap UV。运动层最终 Alpha 乘以填充层实际 Alpha,因此不会越过 `_Fill01`、`_FillSideShrink`,或在 `_FillColor.a` 为零时单独显示。
合扇与劈扇固定使用 Unity 内置单位 Quad,并在顶点阶段把局部 Y 从 `-0.5~0.5` 映射到原 Quad 上半区 `0~0.5`。因此 UV 底边中心、扇柄底点与 GameObject Transform 原点重合,`_Fill01=1` 只到达原 Quad 上边界,对应原 Quad 总高度的 `1/2`,不会向上超出原网格范围;旋转、缩放和位移仍完整跟随物体。该操作只改变渲染顶点,不修改原始 Mesh 或 MeshCollider,若业务依赖碰撞范围必须单独同步 Collider。
v1 只提供 Shader 和示例材质,不接入手势、战斗流程或表现组件。后续表现组件应复用 `MaterialPropertyBlock` 写入 `_Fill01`,不要为每次动画实例化 Material。
## 四、运行时 API
运行时代码位于独立业务程序集 `GameHF.CardShaders`,命名空间为 `GameHF.Rendering`。
```csharp
var block = new MaterialPropertyBlock();
var state = new HFCardShaderState
{
BaseColor = Color.white,
Alpha = 1f,
Cutoff = 0.5f,
YDepthScale = 1f,
YDepthOffset = 0f
};
HFCardShaderUtility.Apply(renderer, block, state);
```
调用方必须长期复用 `MaterialPropertyBlock`,并显式初始化 `HFCardShaderState` 的全部字段。`Apply` 会先读取 Renderer 已有 PropertyBlock,再覆盖框架管理的字段,因此不会清除其他业务属性,也不会实例化 Material。使用 MPB 会影响对应 Renderer 参与 SRP Batcher 的方式,是否划算必须以微信真机测量为准。
`HFCardShaderPropertyIds` 额外提供 `BackgroundMap`、`SweepEnabled`、`ReliefDepth`、`GemStrength`、`GemPulseSpeed` 和 `GemSparkleFrequency`。现有 `HFCardShaderState` 与 `Apply` 接口保持不变;单卡效果差异由调用方在复用的 MPB 中写入对应 Property ID。
UGUI 使用该立体材质时,所属 Canvas 必须在 Additional Shader Channels 中启用 `Normal` 和 `Tangent`。否则 CanvasRenderer 不会生成卡面切线基,NormalMap、浮雕与宝石高光会和场景 Quad 不一致。`GameStart` 示例 Canvas 已配置为 Normal + Tangent(序列化标志值 `24`)。
## 五、扩展规则
- 新增纹理、额外 Pass 等结构性功能时使用 Local Keyword,并控制真实材质组合数量。
- 灰化、闪白、亮度和动画进度等高频状态使用普通属性与 MPB。
- 不把所有功能合并为一个 Uber Shader;新渲染状态优先拆分成职责明确的 Shader。
- 新增 Pass 时必须保证顶点位移和 Alpha Clip 与 Forward Pass 一致。
- BaseMap、Render Queue、Blend 和 ZWrite 属于材质结构配置,不通过每帧 API 动态切换。
## 六、验证
1. 在 Unity 中确认四个 Shader 无编译错误,并检查材质 Inspector 的 SRP Batcher 兼容性。
2. 用不同 Pivot Y 的多个 Quad 验证统一深度平面、Scale 正负方向和 Offset。
3. 使用 Frame Debugger 检查 Forward 正面两次、背面一次纹理采样,以及 DepthOnly、ShadowCaster 对完整 Quad 的深度写入。
4. 验证正面只显示 BaseMap、背面只显示 BackgroundMap,扫光不影响背面,并检查三颗宝石错峰呼吸和星芒。
5. 构建 WebGL 2,检查 Shader 编译日志、粉色材质和首帧卡顿。
6. 在微信开发者工具以及 Android、iOS 真机复核遮挡、内存和 GPU 帧耗。
平台通用约束和完整验收基线见[微信小游戏渲染规则](../../modules/frameworks/微信小游戏渲染规则.md)。
## 七、文档关联
| 文档 | 关联原因 |
|---|---|
| [微信小游戏渲染规则](../../modules/frameworks/微信小游戏渲染规则.md) | WebGL 2、URP、变体、透明深度和真机验证规则 |
| [工作流清单](../../rules/40-工作流清单.md) | Unity 工程与文档仓库的验证流程 |

View File

@@ -0,0 +1,218 @@
# 微信小游戏渲染规则
## 一、定位与适用范围
本文档定义 StrayFog 接入微信小游戏时需要长期遵守的渲染与 Shader 规则,适用于 Unity URP、手写 Shader、Shader Graph、材质、Shader 变体和 AssetBundle 资源。
微信小游戏 Unity 快适配以 WebGL/WebAssembly 构建为基础,再由微信转换插件接入小游戏运行环境。Editor、普通 WebGL 和桌面平台表现不能替代微信开发者工具与真机验证。
本文档不规定具体画面风格,也不替代:
- [微信小游戏资源接入方案](资源管理/微信小游戏资源接入方案.md)中的 CDN、AssetBundle、缓存和卸载规则。
- [相机系统](相机系统/README.md)中的 Base/Overlay Camera Stack 生命周期。
- 具体业务项目的 URP Asset、Renderer Feature、质量等级和材质参数。
## 二、能力基线
### 2.1 图形 API
- Unity 2022.3 WebGL 2 对应 OpenGL ES 3.0 级别的渲染能力。
- 微信转换配置启用 WebGL 2 时,导出流程必须使用 `OpenGLES3`;关闭时才退回 `OpenGLES2`。
- 新项目默认以 WebGL 2 / OpenGLES3 为目标,不为 WebGL 1 主动增加复杂兼容分支。
- 若业务必须支持 WebGL 1,应单独确认设备范围、色彩空间、Shader 循环与索引限制,并建立独立真机验收。
- 运行时可用 `SystemInfo.graphicsDeviceType`、`SystemInfo.graphicsDeviceName` 和 `SystemInfo.graphicsMemorySize` 记录真实设备能力,不能只读取构建配置。
### 2.2 平台宏
Shader 中优先使用 Unity 已定义的图形平台宏:
```hlsl
#if defined(SHADER_API_GLES3)
// WebGL 2 / OpenGL ES 3 路径
#endif
#if defined(SHADER_API_MOBILE)
// 移动 GPU 低成本路径
#endif
```
C# 中微信 SDK 调用继续使用项目约定的 `WeChat` 宏。不要为了 Shader 再建立 `HF_WECHAT_SHADER`、`isMiniGameShader` 等第二套平台开关。
Shader 平台能力和微信业务 API 是两个边界:
- Shader 使用图形 API 宏决定可编译能力和质量分支。
- C# 使用 `WeChat` 宏隔离微信 SDK。
- 不在 Shader 中依赖业务项目名、渠道或微信登录状态。
## 三、Shader 编写规则
### 3.1 通用规则
- URP Shader 必须声明正确的 `RenderPipeline`、`RenderType`、`Queue` 和 `LightMode`。
- 手写 URP Shader 的材质属性放入 `UnityPerMaterial` CBUFFER,并保持所有 Pass 布局一致,以兼容 SRP Batcher。
- 颜色、法线和普通权重优先使用 `half`;世界坐标、裁剪坐标、深度重建和大范围 UV 计算使用 `float`。
- 限制纹理采样、透明叠加、屏幕空间读取、插值变量、动态分支和高次数循环。
- 循环次数可确定时使用常量上限;不要依赖无法在 GLES 路径稳定展开的复杂动态循环。
- 不使用 Geometry Shader、Tessellation Shader 或桌面平台专属 Shader Model 作为微信小游戏核心表现。
- 需要计算深度时必须处理 Reversed-Z、透视除法和线性深度转换,优先使用 URP 提供的深度函数。
- 自定义 HLSL 和 Shader Graph Custom Function 必须同时在目标 Unity 版本、WebGL 构建和真机验证,不能只看节点预览。
### 3.2 Shader Graph
- Shader Graph 目标必须选择 Universal,并明确 Surface Type、Depth Write、Depth Test、Render Face 和 Cast/Receive Shadows。
- Blackboard Keyword 默认选择能剥离未使用组合的定义方式;只有运行时确实会切换所有组合时才保留对应变体。
- 控制 Custom Interpolator 数量,避免为少量计算增加大量顶点到片元数据。
- 节点预览成功不代表生成的 GLES Shader 一定可用;最终以 WebGL/小游戏构建日志和真机画面为准。
- Shader Graph 生成的 Pass 和关键字较多时,应检查生成变体数,不以“图里节点不多”判断成本。
### 3.3 透明、深度与排序
- 透明物体默认 `ZWrite Off`,通过 Sorting Layer、Order、Render Queue 或透明排序轴控制顺序。
- 只有确实需要参与深度遮挡的透明对象才启用 `ZWrite On`,并验证半透明重叠、Alpha 边缘和粒子遮挡。
- 镂空卡片、植被和围栏优先使用 Alpha Clipping 配合深度写入,避免大面积半透明 Overdraw。
- 修改 Vertex Position 的 Z 会真实改变几何位置、裁剪和阴影,不等同于只修改透明排序。
- 2D/2.5D 场景按 Y 排序时,优先使用 Graphics Settings 的 Custom Axis 或 Renderer 排序字段;只有要与不透明物体进行真实深度测试时才映射顶点深度。
- Camera Depth Texture 和 Opaque Texture 会增加带宽或拷贝成本,只在确有深度、水体、折射或屏幕颜色需求时启用。
- 使用 Scene Depth 的透明 Shader 必须检查目标相机是否生成深度纹理,以及 Overlay Camera 是否继承了正确结果。
## 四、URP 与 Renderer 配置
- 使用单一、明确的 URP Asset 作为微信构建基线,质量等级不得无意覆盖为另一套 Render Pipeline Asset。
- 关闭未使用的主光阴影、附加光阴影、软阴影、Light Cookies、HDR、Opaque Texture、Depth Texture 和 Renderer Feature。
- Renderer Feature 必须有明确的 LayerMask、Render Queue、Pass Event 和目标 Camera;`LayerMask = 0` 的 Feature 应删除或说明保留原因。
- 开启 SRP Batcher,并用 Frame Debugger 或真机渲染分析确认材质布局兼容。
- GPU Instancing 只在相同 Mesh/Material 的重复渲染确实受益时启用;必须验证微信目标设备,不把 Editor 批次数直接当作结论。
- 移动端默认避免 Deferred;使用 Forward 或项目验证过的 Forward+ 配置。
- Render Scale、MSAA、HDR 和后处理一起评估。降低 Render Scale 往往比继续压缩单个片元 Shader 更直接,但必须验证 UI 清晰度。
## 五、Shader 变体与加载
### 5.1 变体控制
- 优先使用 `shader_feature`,只在运行时必须覆盖全部组合时使用 `multi_compile`。
- 关键字尽量限制到实际影响的 Shader Stage。
- URP Asset 中关闭未使用能力,让 URP 构建预过滤和 Shader Stripping 生效。
- 不把 URP Lit、Standard 或其他高变体 Shader 整体加入 Always Included Shaders。
- 动态启用关键字时,构建中必须存在对应材质组合或精确的 ShaderVariantCollection。
- 调试剥离问题时可以临时开启 Strict Shader Variant Matching;确认完成后再决定正式配置。
### 5.2 AssetBundle
- AssetBundle 材质使用的 Shader 必须由主包材质、精确的 ShaderVariantCollection 或经过验证的包含策略保留。
- Bundle 中的 Shader、材质和主包 URP 版本必须一致,不能混用不同 Unity/URP 版本生成的结果。
- ShaderVariantCollection 只记录真实使用组合,不把所有可能关键字做笛卡尔积。
- Bundle 卸载遵循资源管理生命周期;仍有材质或 Renderer 引用时不能假设 Shader GPU 资源已经释放。
### 5.3 首次使用卡顿
Unity 第一次渲染某个 Shader 变体时,图形驱动可能创建并上传 GPU 程序,产生可见卡顿。处理顺序:
1. 先减少无用 Shader 和变体。
2. 收集首屏、战斗、场景切换实际使用的变体。
3. 在可控加载阶段分批预热必要集合。
4. 避免一次性 WarmUp 全项目变体导致启动峰值。
5. 在微信真机记录首帧、场景首开和特效首次播放时间。
## 六、纹理、色彩与 RenderTexture
- WebGL 2 才支持可靠的 Linear 色彩空间路径;项目选择 Linear 后不得保留未经验证的 WebGL 1 回退。
- 纹理的 sRGB、Normal Map、Alpha 和压缩格式必须按用途配置,不能只靠文件名约定。
- 微信转换插件的压缩纹理功能和 Unity Texture Importer 是两层配置,启用前应确认产物格式、设备支持、下载体积和运行时内存。
- 不把 DXT 当作移动设备通用格式;ASTC、ETC2 和无压缩回退需要按微信设备覆盖范围验证。
- 压缩纹理不可用时可能发生解压或回退,导致显存和加载峰值显著增加。
- RenderTexture 的格式、MSAA、Depth Buffer 和 sRGB 必须使用目标设备支持的组合。
- 临时 RenderTexture 使用 `GetTemporary`/`ReleaseTemporary` 成对管理;长期 RenderTexture 在生命周期结束时显式释放。
- 全屏 Blit、Grab、Opaque Texture 和多层后处理按像素数消耗带宽,小游戏上应优先减少次数和分辨率。
## 七、性能预算与诊断
性能判断以真机 GPU/CPU 帧时间、内存和发热为准,不只看 Draw Call。
重点指标:
- SetPass Calls、Draw Calls、Batches。
- Triangles、Vertices。
- 透明 Overdraw 和全屏 Pass 数量。
- RenderTexture 数量、尺寸和格式。
- Shader/Material 数量和首次使用卡顿。
- URP 阴影、附加光、后处理和 Renderer Feature 成本。
- 纹理下载体积、解码峰值、CPU 内存和 GPU 估算内存。
微信转换配置提供 Render Analysis、性能分析和 Profile Stats 时,可以在诊断构建中启用;正式包是否保留以开销实测为准。
推荐定位顺序:
1. 确认设备、基础库、WebGL 版本和分辨率。
2. 确认问题属于 CPU、GPU、内存、下载还是 Shader 首次编译。
3. 用 Unity Frame Debugger/Profiler 找到 Pass、材质和 RenderTexture。
4. 用微信开发者工具和真机复现。
5. 每次只改变一类 URP 或 Shader 配置,记录前后数据。
## 八、构建与验收流程
### 8.1 构建前
- 确认 Unity、URP、Shader Graph 和微信转换插件版本。
- 确认微信转换配置启用 WebGL 2。
- 检查目标 Quality Level 和实际 URP Asset。
- 检查 Graphics Settings 的 Always Included Shaders、Preloaded Shaders、透明排序轴和剥离配置。
- 检查 URP Asset、Renderer Feature、Depth/Opaque Texture、HDR、MSAA 和 Render Scale。
- 构建日志开启 Shader Variant 统计时,记录编译前后变体数量。
### 8.2 最低验证场景
- 冷启动首屏。
- 首次打开主要 UI 和 Spine 动画。
- 首次进入每个使用独立 Shader 的场景。
- 透明 Quad、粒子、Alpha Clipping 和 UI 遮挡。
- 水体、Scene Depth、Opaque Texture 和反射效果。
- Base/Overlay Camera Stack。
- AssetBundle 首次加载、缓存命中和卸载后重新加载。
- Android 与 iOS 微信真机至少各一台中低端设备。
- 前后台切换、分辨率变化和长时间运行。
### 8.3 通过标准
- 没有粉色材质、缺失 Pass、错误关键字或不支持的 RenderTexture 格式。
- Editor、普通 WebGL 和微信小游戏的核心画面含义一致。
- 首次使用 Shader 不产生不可接受的主线程或 GPU 卡顿。
- 透明排序、深度遮挡和 Camera Stack 符合设计。
- 变体、纹理和 RenderTexture 不造成不可接受的启动或场景内存峰值。
- Unity Console、WebGL 浏览器日志、微信开发者工具和真机日志无本次新增 Shader Error。
## 九、FFCard_WeChat 当前基线
以下是项目基线,不是所有 StrayFog 项目的强制配置:
- Unity:`2022.3.29f1`。
- URP:`14.0.11`。
- Shader Graph:`14.0.11`。
- 微信转换配置:WebGL 2 开启。
- URP:SRP Batcher、Depth Texture、Opaque Texture、HDR 和 2x MSAA 开启。
- WebGL Quality Level:High。
- WebGL Static Batching、Dynamic Batching 和 Graphics Jobs 关闭。
- Graphics Settings 使用 Custom Transparency Sort Axis `(0, 0, 1)`。
- Preloaded Shaders 为空,Strict Shader Variant Matching 关闭。
- 微信压缩纹理、Render Analysis、Emscripten GLX 和 Render Thread 当前关闭。
这些配置只是后续优化的测量起点。调整前必须保存目标设备、测试场景和基线数据,调整后在微信真机对比。
## 十、外部依据
- [微信小游戏 Unity/团结引擎快适配文档](https://wechat-miniprogram.github.io/minigame-unity-webgl-transform/)
- [Unity 2022.3 WebGL](https://docs.unity3d.com/cn/2022.3/Manual/webgl.html)
- [Unity 2022.3 WebGL Graphics](https://docs.unity3d.com/2022.3/Documentation/Manual/webgl-graphics.html)
- [Unity Shader Variant Stripping](https://docs.unity3d.com/cn/2022.3/Manual/shader-variant-stripping.html)
- [Unity Shader Loading](https://docs.unity3d.com/cn/2022.3/Manual/shader-loading.html)
- [Unity Graphics Settings](https://docs.unity3d.com/cn/2022.3/Manual/class-GraphicsSettings.html)
## 十一、文档关联
| 文档 | 关联原因 |
|---|---|
| [FFCard_WeChat 卡牌 Shader 框架](../../business-reference/ffcard-wechat/卡牌Shader框架.md) | 本规则在 FFCard_WeChat 中的卡牌业务实现 |
| [微信小游戏资源接入方案](资源管理/微信小游戏资源接入方案.md) | Shader、材质和 AssetBundle 的加载与卸载边界 |
| [相机系统](相机系统/README.md) | URP Camera Stack 和渲染顺序 |
| [第三方插件](第三方插件.md) | Spine、UI 特效和微信转换插件的 Shader 风险 |
| [工作流清单](../../rules/40-工作流清单.md) | Unity、平台与真机验证流程 |
| [开发规则](../../rules/20-开发规则.md) | 平台、资源和生成代码约束 |

View File

@@ -11,6 +11,7 @@
| [事件系统](frameworks/事件系统/README.md) | `CopyToHF/Event/` | CTC/STC 事件派发 | 已完成 |
| [资源管理](frameworks/资源管理/README.md) | `CopyToHF/AssetBundle/`、`StrayFog/Core/SFSubPackageV2.cs` | AssetBundle、图集、卸载、分包 V2 和下载 | 已完成 |
| [微信小游戏资源接入方案](frameworks/资源管理/微信小游戏资源接入方案.md) | `CopyToGeneral/MinGameModel/`、`SFSubPackageV2.cs` | FFCard_WeChat 微信 CDN、缓存、加载、卸载和资源生命周期接入方案 | 待实施 |
| [微信小游戏渲染规则](frameworks/微信小游戏渲染规则.md) | `ProjectSettings/`、URP Asset、Shader、Shader Graph、材质 | WebGL 2、URP、Shader 变体、透明深度、纹理与微信真机渲染规范 | 待验证 |
| [音效系统重构方案](frameworks/音效系统重构方案.md) | `StrayFog/Editor/CopyToX/CopyToHF/Audio/`、`CopyToGeneral/Audio/` | 原生音频播放、混音、缓存、并发与资源生命周期设计 | 待验证 |
| [相机系统](frameworks/相机系统/README.md) | `GameHFScripte/` | 多相机、堆栈和渲染顺序 | 已完成 |
| [实体系统](frameworks/实体系统/README.md) | `GameFunction/GameEntity/` | 实体、System、Model 和实体池 | 已完成 |
@@ -39,6 +40,7 @@
## 业务参考
- [PlayBalloons](../business-reference/playballoons/业务参考总入口.md):外部项目业务实现示例,不作为框架规范。
- [FFCard_WeChat 卡牌 Shader 框架](../business-reference/ffcard-wechat/卡牌Shader框架.md):GameHFResource 内的 URP 卡牌 Shader 与 Y 轴深度业务实现,不作为通用核心框架。
## 文档拆分