ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Unity TMP_SDF 分析(五)基础片元着色器与 GetColor

Unity TMP_SDF 分析(五)基础片元着色器与 GetColor Unity TMP_SDF 分析五基础片元着色器与GetColor适用版本Unity2021.3.23f1c1TextMeshPro3.0.9UGUI1.0.0分析范围项目中的标准Assets/TextMesh Pro/Shaders/TMP_SDF.shader及其关联源码Mobile、SSD、Surface 等变体不直接套用。系列导航第 5/9 篇前置字重阈值、裁剪阈值与顶点输出下一篇GLOW_ON辉光路径片元着色器的核心任务是采样 SDF Atlas把归一化距离换算为屏幕尺度距离再生成 Face、Outline 和抗锯齿覆盖率。一、基础源码以下代码保留基础颜色路径并标出与宏相关的入口节选fixed4 PixShader(pixel_t input) : SV_Target { UNITY_SETUP_INSTANCE_ID(input); float c tex2D(_MainTex, input.atlas).a; #ifndef UNDERLAY_ON clip(c - input.param.x); #endif float scale input.param.y; float bias input.param.z; float weight input.param.w; float sd (bias - c) * scale; float outline (_OutlineWidth * _ScaleRatioA) * scale; float softness (_OutlineSoftness * _ScaleRatioA) * scale; half4 faceColor _FaceColor; half4 outlineColor _OutlineColor; faceColor.rgb * input.color.rgb; faceColor * tex2D( _FaceTex, input.textures.xy float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y); outlineColor * tex2D( _OutlineTex, input.textures.zw float2(_OutlineUVSpeedX, _OutlineUVSpeedY) * _Time.y); faceColor GetColor( sd, faceColor, outlineColor, outline, softness); // Bevel / Underlay / Glow / UI Clip 在此后执行。 return faceColor * input.color.a; }weight在基础颜色路径中没有直接参与后续表达式因为它已经被顶点着色器折算进bias开启BEVEL_ON后它还会用于程序化表面法线计算。二、输入字段片元着色器接收的pixel_t关键字段如下字段数据来源用途input.atlas顶点TEXCOORD0采样_MainTexSDF Atlasinput.param.xalphaClipAtlas 采样的早期丢弃阈值input.param.yscale屏幕空间 SDF 缩放量input.param.zbias带字重和半过渡补偿的 Atlas 阈值input.param.wweightBevel 等后续路径使用input.textures.xy解包映射 UV _FaceTex_STFace Texture UVinput.textures.zw解包映射 UV _OutlineTex_STOutline Texture UVinput.colorTMP 顶点颜色Face RGB 和最终整体 Alpha 调制标准 Shader 在UNDERLAY_ON未启用时使用alphaClipclip(c - input.param.x);三、采样 SDF Atlasfloat c tex2D(_MainTex, input.atlas).a;_MainTex是字体 AtlasSDF 数据位于 Alpha 通道。对于本项目使用的 TMP SDF Atlas可按以下方向理解c 较大 → 更靠字形内部 c 较小 → 更靠字形外部 c ≈ 0.5 → 未施加字重偏移时的基础边缘附近SDF Alpha 是经过纹理量化和采样过滤后的归一化距离表示。测试时可以直接输出采样值float c tex2D(_MainTex, input.atlas).a; return fixed4(c, c, c, 1);对应的 SDF 字体图集图集中的字形示例四、alphaClip在完整着色前丢弃无关片元#ifndef UNDERLAY_ON clip(c - input.param.x); #endifHLSLclip(x)在x 0时丢弃片元因此实际条件为c alphaClip → discard c alphaClip → 继续执行alphaClip已在顶点着色器中考虑 Weight、Face Dilate、Outline Width、Outline Softness以及开启 Glow 时的额外范围。它用于排除确定不会对结果产生贡献的 Atlas 外围区域最终颜色边界由GetColor()计算。正常 Underlay 使用偏移 UV 再采样 Atlas可能需要当前c阈值之外的片元所以UNDERLAY_ON变体跳过这次 clip。五、sd的正负方向float scale input.param.y; float bias input.param.z; float sd (bias - c) * scale;由于字形内部的c较大c bias → sd 0 → 更靠字形内部 c bias → sd 0 → 当前字重阈值附近 c bias → sd 0 → 更靠字形外部这是 Face、Outline、Glow 和 Inner Underlay 距离方向的共同基础。scale把 Atlas 值差转换到近似屏幕像素尺度文本在屏幕上较大时scale通常较大同样的 Atlas 值差会形成更陡的sd变化。文本较小时scale较小过渡会更宽以维持抗锯齿稳定性。六、描边宽度与柔化参数float outline (_OutlineWidth * _ScaleRatioA) * scale; float softness (_OutlineSoftness * _ScaleRatioA) * scale;两项材质参数先由_ScaleRatioA限制在 Atlas 的有效距离范围内再乘屏幕空间scale转换到GetColor()使用的sd尺度。这里的“分辨率无关”是近似的屏幕尺度补偿不意味着描边宽度在任何透视角、Atlas 分辨率和极端缩放下都严格保持同一像素数。七、Face、Outline 与顶点颜色half4 faceColor _FaceColor; half4 outlineColor _OutlineColor; faceColor.rgb * input.color.rgb;顶点颜色 RGB 只在这里调制 Face RGB。因此 TMP 顶点渐变主要作用于字面颜色不会直接乘到_OutlineColor.rgb。最终返回时return faceColor * input.color.a;顶点 Alpha 会乘到整个合成结果包括 Face、Outline以及它们之后叠加的 Bevel、Underlay、Glow 和裁剪结果。八、Face/Outline 装饰纹理与 UV 滚动faceColor * tex2D( _FaceTex, input.textures.xy float2(_FaceUVSpeedX, _FaceUVSpeedY) * _Time.y); outlineColor * tex2D( _OutlineTex, input.textures.zw float2(_OutlineUVSpeedX, _OutlineUVSpeedY) * _Time.y);_FaceTex和_OutlineTex是额外的颜色/图案纹理SDF Atlas 位于_MainTex。input.textures来自打包映射 UV 的解包结果。_Time.y × UVSpeed使纹理随时间滚动。纹理采样结果与各自基础颜色按 RGBA 相乘纹理 Alpha 也会影响后续覆盖率。九、GetColor()源码TMPro.cginc中的实现如下fixed4 GetColor( half d, fixed4 faceColor, fixed4 outlineColor, half outline, half softness) { half faceAlpha 1 - saturate( (d - outline * 0.5 softness * 0.5) / (1.0 softness)); half outlineAlpha saturate(d outline * 0.5) * sqrt(min(1.0, outline)); faceColor.rgb * faceColor.a; outlineColor.rgb * outlineColor.a; faceColor lerp( faceColor, outlineColor, outlineAlpha); faceColor * faceAlpha; return faceColor; }参数d就是片元着色器中的sd其方向为“负值在内、正值在外”。十、faceAlpha整个 Face/Outline 合成结果的覆盖率half faceAlpha 1 - saturate( (d - outline * 0.5 softness * 0.5) / (1.0 softness));faceAlpha会在颜色混合之后乘到 Face/Outline 的整体结果上。结合d的方向字形内部d较负括号内值较小faceAlpha接近1。向字形外部移动时d增大faceAlpha逐渐降到0。outline * 0.5把整体可见范围向外推。softness同时移动并加宽过渡区。分母中的常量1.0是基础抗锯齿距离跨度Softness 在此基础上继续加宽。本 Shader 的d正方向朝外因此d增大时faceAlpha逐渐降低。十一、outlineAlphaFace 与 Outline 的颜色选择权重half outlineAlpha saturate(d outline * 0.5) * sqrt(min(1.0, outline));outlineAlpha更准确的含义是“向 Outline Color 插值的权重”内部的d较负权重接近0颜色偏向 Face。接近边缘并向外时d增大权重提高颜色逐渐转向 Outline。outline * 0.5控制转换开始的位置。sqrt(min(1, outline))令窄描边的最大颜色权重低于1并随宽度平滑增加。例如outline 0.25时该乘数为0.5这一项最多贡献一半的 Outline 插值权重。十二、预乘 Alpha 与颜色混合faceColor.rgb * faceColor.a; outlineColor.rgb * outlineColor.a; faceColor lerp(faceColor, outlineColor, outlineAlpha); faceColor * faceAlpha;Shader Pass 使用Blend One OneMinusSrcAlpha因此颜色在输出前采用预乘 Alpha 形式Face 和 Outline 的 RGB 先分别乘自身 Alpha。outlineAlpha在两种预乘颜色之间插值。faceAlpha作为 SDF 几何覆盖率乘到最终 RGBA。片元函数末尾再乘 TMP 顶点 Alpha。这种顺序能让半透明 Face、Outline Texture 和抗锯齿边缘在预乘混合模式下保持一致。十三、基础颜色流程TEXCOORD0 └─ 采样 _MainTex.a → c ├─ 与 alphaClip 比较丢弃确定无贡献的片元 └─ sd (bias - c) × scale ├─ outline / softness ├─ Face 与 Outline 装饰纹理 └─ GetColor() ├─ faceAlpha整体覆盖率 ├─ outlineAlpha颜色插值权重 ├─ 预乘 Face/Outline RGB └─ 输出预乘颜色 └─ × input.color.a最重要的符号结论为在标准TMP_SDF.shader中sd (bias - c) * scale所以sd 0更靠字形内部sd 0更靠字形外部。十四、源码索引Assets/TextMesh Pro/Shaders/TMP_SDF.shaderPixShader()与预乘混合设置。Assets/TextMesh Pro/Shaders/TMPro.cgincGetColor()。Assets/TextMesh Pro/Shaders/TMP_Properties.cginc片元阶段使用的材质 Uniform。Scripts/Runtime/TMP_ShaderUtilities.cs_ScaleRatioA的 CPU 端计算。
返回列表