资讯动态

Unity TMP_SDF 分析(七)UNDERLAY_ON 与 UNDERLAY_INNER

发布时间:2026/8/15 19:54:56 来源:尧图企业网站定制
Unity TMP_SDF 分析七UNDERLAY_ON与UNDERLAY_INNER适用版本Unity2021.3.23f1c1TextMeshPro3.0.9UGUI1.0.0分析范围项目中的标准Assets/TextMesh Pro/Shaders/TMP_SDF.shader及其关联源码Mobile、SSD、Surface 等变体不直接套用。系列导航第 7/9 篇前置GLOW_ON辉光路径下一篇BEVEL_ON法线、光照与反射Underlay 使用一套偏移、膨胀和柔化参数再次采样_MainTexSDF Atlas再把结果合成到 Face/Outline 下方。一、关键字是互斥变体Shader 声明为#pragma shader_feature __ UNDERLAY_ON UNDERLAY_INNER材质面板对应三种互斥状态None → 两个关键字都不启用 Normal → UNDERLAY_ON Inner → UNDERLAY_INNERNormal Underlay 的效果示例Inner 模式选择UNDERLAY_INNER变体Normal 与 Inner 是互斥选项。Inner Underlay 会乘(1 - faceColor.a)所以 Face/Outline 完全不透明时效果会被遮住。调低 Face Alpha 后更容易观察效果示例二、完整相关代码顶点着色器准备一套额外参数节选#if (UNDERLAY_ON || UNDERLAY_INNER) float4 underlayColor _UnderlayColor; underlayColor.rgb * underlayColor.a; float bScale scale; bScale / 1 ((_UnderlaySoftness * _ScaleRatioC) * bScale); float bBias (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale); float x -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth; float y -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight; float2 bOffset float2(x, y); output.texcoord2 float4( input.texcoord0 bOffset, bScale, bBias); output.underlayColor underlayColor; #endif片元着色器根据互斥关键字选择一种合成方式#ifndef UNDERLAY_ON clip(c - input.param.x); #endif #if UNDERLAY_ON float d tex2D( _MainTex, input.texcoord2.xy).a * input.texcoord2.z; faceColor input.underlayColor * saturate(d - input.texcoord2.w) * (1 - faceColor.a); #endif #if UNDERLAY_INNER float d tex2D( _MainTex, input.texcoord2.xy).a * input.texcoord2.z; faceColor input.underlayColor * (1 - saturate(d - input.texcoord2.w)) * saturate(1 - sd) * (1 - faceColor.a); #endif三、CPU 端_ScaleRatioCTMP_ShaderUtilities.UpdateShaderRatios()的计算如下节选floatscalemat.GetFloat(ID_GradientScale);floatfaceDilatemat.GetFloat(ID_FaceDilate);floatweightMathf.Max(mat.GetFloat(ID_WeightNormal),mat.GetFloat(ID_WeightBold))/4.0f;floatoffsetXmat.GetFloat(ID_UnderlayOffsetX);floatoffsetYmat.GetFloat(ID_UnderlayOffsetY);floatdilatemat.GetFloat(ID_UnderlayDilate);floatsoftnessmat.GetFloat(ID_UnderlaySoftness);floatrange(weightfaceDilate)*(scale-1.0f);floattMathf.Max(1,Mathf.Max(Mathf.Abs(offsetX),Mathf.Abs(offsetY))dilatesoftness);boolisRatioEnabled!mat.shaderKeywords.Contains(RATIOS_OFF);floatratioCisRatioEnabled?Mathf.Max(0,scale-1.0f-range)/(scale*t):1;mat.SetFloat(ID_ScaleRatio_C,ratioC);Ratio C 先扣除 Weight 与 Face Dilate 占用的 Atlas 梯度范围再根据以下 Underlay 参数压缩剩余预算X/Y Offset 中绝对值较大的一个DilateSoftnessRatio C 限制 SDF Padding 使用范围顶点 Shader 的scale负责屏幕尺度换算。四、Underlay 颜色使用预乘 Alphafloat4 underlayColor _UnderlayColor; underlayColor.rgb * underlayColor.a;Shader Pass 使用Blend One OneMinusSrcAlpha所以 Underlay RGB 先乘自身 Alpha后续可以通过加法形式合成underlay base × (1 - underlayAlpha)当前 Shader 的实际表达式还额外乘了(1 - faceColor.a)用来保证 Underlay 位于已生成的 Face/Outline 之下。五、bScaleUnderlay Softnessfloat bScale scale; bScale / 1 ((_UnderlaySoftness * _ScaleRatioC) * bScale);可写为bScale scale / (1 softness × ratioC × scale)Softness 为0时bScale scale。Softness 增大时bScale减小。片元阶段使用atlasAlpha × bScale - bBias生成覆盖率较小的bScale令 Atlas 距离变化斜率更缓Underlay 边缘更柔。这套 Softness 只作用于第二次偏移采样不会直接修改主 Face/Outline 的scale。六、bBiasWeight、Dilate 与柔化后的阈值float bBias (0.5 - weight) * bScale - 0.5 - ((_UnderlayDilate * _ScaleRatioC) * 0.5 * bScale);片元阶段组合为float d shiftedAtlasAlpha * bScale; float underlayCoverage saturate(d - bBias);因此bBias是已经转换到d所在尺度的覆盖阈值weight让 Underlay 与主字形的字重基准保持联系。正_UnderlayDilate会降低bBias使d - bBias增大覆盖区域扩张。Softness 已经通过bScale同时影响斜率和阈值。主路径计算sd (bias - c) * scaleUnderlay 路径计算c × bScale - bBias两者使用各自的符号和代数形式。七、Offset 如何变成 Atlas UV 偏移float x -(_UnderlayOffsetX * _ScaleRatioC) * _GradientScale / _TextureWidth; float y -(_UnderlayOffsetY * _ScaleRatioC) * _GradientScale / _TextureHeight;_GradientScale / _TextureWidth与_GradientScale / _TextureHeight把材质 Offset 换算为 Atlas UV 尺度Ratio C 负责限制可用范围。代码中的负号表示采样坐标向设置方向的反方向移动视觉上得到的 Underlay 图形会相对原字形向 Offset 指定方向偏移。这与“移动图形”和“反向移动采样点”之间的关系一致。最终传递的数据为input.texcoord2.xy → 偏移后的 Atlas UV input.texcoord2.z → bScale input.texcoord2.w → bBias八、为什么 Normal Underlay 跳过主clip()#ifndef UNDERLAY_ON clip(c - input.param.x); #endif只有UNDERLAY_ON会跳过主 Atlas 的早期 clipNormal Underlay 需要在原字形外侧显示偏移后的形状。当前片元的原始 Atlas 值c可能低于主 Face/Outline 的阈值但偏移 UV 采样却可能命中 Underlay 字形。若先按原始c丢弃这些外部阴影片元就永远无法执行第二次采样。UNDERLAY_INNER只需要原字形内部附近的片元因此它与无 Underlay 情况一样保留这次主clip()。九、Normal Underlay 合成float d tex2D( _MainTex, input.texcoord2.xy).a * input.texcoord2.z; faceColor input.underlayColor * saturate(d - input.texcoord2.w) * (1 - faceColor.a);拆解为shiftedCoverage saturate(shiftedAtlasAlpha × bScale - bBias) underlayContribution premultipliedUnderlayColor × shiftedCoverage × (1 - baseFaceOutlineAlpha)(1 - faceColor.a)使 Underlay 只填充 Face/Outline 尚未覆盖的部分形成位于文字下方的阴影或外扩底层。十、Inner Underlay 合成faceColor input.underlayColor * (1 - saturate(d - input.texcoord2.w)) * saturate(1 - sd) * (1 - faceColor.a);它由三个遮罩共同决定。1. 偏移字形的反向覆盖1 - saturate(d - bBias)Normal 模式使用偏移字形“被覆盖”的区域Inner 模式反过来选择偏移字形“未覆盖或覆盖较弱”的区域。2. 原始字形内部遮罩saturate(1 - sd)第五篇已经确认sd 0更靠原字形内部内部sd 0时该遮罩通常为1。向外移动、sd增大后逐渐降为0。这一步把反向偏移覆盖限制在原字形内部附近从而形成内部阴影/内凹效果。3. 基础颜色剩余透明度1 - faceColor.a即使前两个遮罩有贡献只要 Face/Outline 已经完全不透明最终 Underlay 仍会被乘为零。这正是降低 Face Alpha 后 Inner Underlay 才明显的原因。十一、两种模式对照项目UNDERLAY_ONUNDERLAY_INNER材质面板NormalInner是否与另一关键字同时启用否否是否跳过主clip(c-alphaClip)是否偏移覆盖saturate(d-bBias)1-saturate(d-bBias)原字形内部遮罩无saturate(1-sd)与 Face 的层级都乘(1-faceColor.a)位于其下常见效果投影、外部底层内阴影、内凹遮罩十二、源码索引Assets/TextMesh Pro/Shaders/TMP_SDF.shaderUnderlay 顶点参数与两种片元合成。Scripts/Runtime/TMP_ShaderUtilities.cs_ScaleRatioC。Scripts/Editor/TMP_SDFShaderGUI.csNone / Normal / Inner 互斥关键字设置。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价