Files
UnityAI/business-reference/ffcard-wechat/卡牌Shader框架.md

21 KiB
Raw Permalink Blame History

FFCard_WeChat 卡牌 Shader 框架

一、定位

这套 Shader 是 FFCard_WeChat 的卡牌业务实现,面向 Unity 2022.3.29f1、URP 14.0.11 和微信小游戏。它不属于 StrayFogCore,也不作为其他项目必须遵循的通用框架。

实现位于:

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 的顶点公式固定为:

最终世界 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 复用:

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。

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 帧耗。

平台通用约束和完整验收基线见微信小游戏渲染规则。

七、文档关联

文档 关联原因
微信小游戏渲染规则 WebGL 2、URP、变体、透明深度和真机验证规则
工作流清单 Unity 工程与文档仓库的验证流程