Unity TMP_SDF 分析(四)字重阈值、裁剪阈值与顶点输出

发布时间:2026/10/8 13:40:54

Unity TMP_SDF 分析(四)字重阈值、裁剪阈值与顶点输出 Unity TMP_SDF 分析四字重阈值、裁剪阈值与顶点输出适用版本Unity2021.3.23f1c1TextMeshPro3.0.9UGUI1.0.0分析范围项目中的标准Assets/TextMesh Pro/Shaders/TMP_SDF.shader及其关联源码Mobile、SSD、Surface 等变体不直接套用。系列导航第 4/9 篇前置顶点着色器中的屏幕空间缩放下一篇基础片元着色器与GetColor第三篇得到了屏幕空间 SDF 缩放量scale。本篇继续分析顶点着色器后半部分weight、bias、alphaClip、装饰纹理 UV以及写入pixel_t的各项数据。一、相关源码以下为VertShader()后半部分节选Glow 和 Underlay 细节留到后文float weight lerp(_WeightNormal, _WeightBold, bold) / 4.0; weight (weight _FaceDilate) * _ScaleRatioA * 0.5; float bias (.5 - weight) (.5 / scale); float alphaClip 1.0 - _OutlineWidth * _ScaleRatioA - _OutlineSoftness * _ScaleRatioA; #if GLOW_ON alphaClip min( alphaClip, 1.0 - _GlowOffset * _ScaleRatioB - _GlowOuter * _ScaleRatioB); #endif alphaClip alphaClip / 2.0 - (.5 / scale) - weight; float4 clampedRect clamp(_ClipRect, -2e10, 2e10); float2 maskUV (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy); float2 textureUV UnpackUV(input.texcoord1.x); float2 faceUV TRANSFORM_TEX(textureUV, _FaceTex); float2 outlineUV TRANSFORM_TEX(textureUV, _OutlineTex); output.position vPosition; output.color input.color; output.atlas input.texcoord0; output.param float4(alphaClip, scale, bias, weight); output.mask half4( vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) pixelSize.xy)); output.viewDir mul( (float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz); output.textures float4(faceUV, outlineUV);二、选择 Normal 或 Bold 字重float weight lerp(_WeightNormal, _WeightBold, bold) / 4.0;其中bold来自float bold step(input.texcoord1.y, 0);因此bold 0 → weight _WeightNormal / 4 bold 1 → weight _WeightBold / 4_WeightNormal与_WeightBold是材质属性分别控制普通字符和由 Shader 模拟的 Bold 字符所使用的距离阈值偏移。为什么除以4CPU 端TMP_ShaderUtilities.UpdateShaderRatios()在计算可用距离范围时也使用相同的/ 4.0ffloatweightMathf.Max(mat.GetFloat(ID_WeightNormal),mat.GetFloat(ID_WeightBold))/4.0f;所以/4是 TMP 材质字重参数到内部距离场参数的统一尺度换算。三、叠加 Face Dilate 与_ScaleRatioAweight (weight _FaceDilate) * _ScaleRatioA * 0.5;_FaceDilate对普通和 Bold 字重统一施加额外的膨胀或收缩。_ScaleRatioA、_ScaleRatioB、_ScaleRatioC虽然是材质属性但通常由 TMP 的UpdateShaderRatios()计算用来把各类效果限制在字体 Atlas 的有效 SDF Padding 范围内。最后的* 0.5是 Shader 内部距离公式的一部分。与其猜测这是“再次优化精度”更可靠的理解是它把经 Ratio 修正后的字重与 Face Dilate 转换到后续以0.5为基准的 SDF 阈值域。四、_ScaleRatioA的真实作用CPU 端实现如下节选floatscalemat.GetFloat(ID_GradientScale);floatfaceDilatemat.GetFloat(ID_FaceDilate);floatoutlineThicknessmat.GetFloat(ID_OutlineWidth);floatoutlineSoftnessmat.GetFloat(ID_OutlineSoftness);floatweightMathf.Max(mat.GetFloat(ID_WeightNormal),mat.GetFloat(ID_WeightBold))/4.0f;floattMathf.Max(1,weightfaceDilateoutlineThicknessoutlineSoftness);boolisRatioEnabled!mat.shaderKeywords.Contains(RATIOS_OFF);floatratioAisRatioEnabled?(scale-1.0f)/(scale*t):1;mat.SetFloat(ID_ScaleRatio_A,ratioA);其中 C#scale是材质的_GradientScale通常与字体 Atlas Padding 相关。它与第三篇由屏幕尺寸计算的 HLSLscale含义不同名称所在位置含义C#scaleUpdateShaderRatios()材质_GradientScaleHLSLscaleVertShader()/PixShader()屏幕空间 SDF 缩放量ratioA的目的是给以下效果共同分配有限的 SDF 梯度范围Normal/Bold WeightFace DilateOutline WidthOutline Softness效果参数总和增大时t增大ratioA变小从而按比例压缩它们的实际作用范围避免越过 Atlas Padding 所能表达的距离范围。Ratio A 分配 Atlas 的有效距离范围屏幕空间缩放由顶点 Shader 的scale公式处理。若材质启用了RATIOS_OFF关键字则isRatioEnabled为false三个 Ratio 会回到1表示禁用这类安全范围补偿。五、biasAtlas 采样阈值与半过渡补偿float bias (.5 - weight) (.5 / scale);片元着色器随后会计算float sd (bias - c) * scale;其中c是 SDF Atlas 的 Alpha 值。TMP 的字形内部通常具有更高的c因此c bias → sd 0 → 更靠字形内部 c bias → sd 0 → 更靠字形外部1.0.5 - weight0.5是归一化 SDF 的基础边界。weight增大时bias降低较低的 Atlas 值也更容易进入可见过渡区因此字形向外扩张、视觉上变粗。2.0.5 / scale这一项把约半个标准过渡单位的补偿换算回 Atlas 值域使后续sd和GetColor()的抗锯齿过渡随屏幕缩放保持稳定。bias通常位于0.5附近同时包含字重和屏幕尺度补偿。六、alphaClip早期片元丢弃阈值基础计算为float alphaClip 1.0 - _OutlineWidth * _ScaleRatioA - _OutlineSoftness * _ScaleRatioA; alphaClip alphaClip / 2.0 - (.5 / scale) - weight;它通过output.param.x传入片元着色器并用于#ifndef UNDERLAY_ON clip(c - input.param.x); #endifclip(x)在x 0时丢弃当前片元。因此这里丢弃的是c alphaClipOutline 越宽、Softness 越大alphaClip越低Shader 就会保留 Atlas 中离字形更远的像素以便GetColor()计算描边和柔边。alphaClip是安全的早期片元丢弃阈值它最终由片元着色器的clip()执行。它不会修改文本四边形尺寸。最终颜色边界仍由sd、outline、softness和GetColor()决定。开启 Glow 时还会进一步降低阈值保留辉光需要的 Atlas 区域。UNDERLAY_ON会跳过这次 clip因为偏移后的外部 Underlay 可能依赖原字形阈值之外的片元。七、UI 裁剪数据的准备float4 clampedRect clamp(_ClipRect, -2e10, 2e10); float2 maskUV (vert.xy - clampedRect.xy) / (clampedRect.zw - clampedRect.xy); output.mask half4( vert.xy * 2 - clampedRect.xy - clampedRect.zw, 0.25 / (0.25 * half2(_MaskSoftnessX, _MaskSoftnessY) pixelSize.xy));当前版本中局部变量maskUV后续没有被读取编译器通常会将其消除。真正传给片元着色器的是output.mask.xy相对于裁剪矩形中心的对称坐标。.zw结合 RectMask2D Softness 与屏幕像素尺度得到的过渡斜率。完整推导将在第九篇说明。八、Face/Outline 装饰纹理 UVfloat2 textureUV UnpackUV(input.texcoord1.x); float2 faceUV TRANSFORM_TEX(textureUV, _FaceTex); float2 outlineUV TRANSFORM_TEX(textureUV, _OutlineTex);这条数据链已经在第二篇分析input.texcoord1.x保存PackUV(mappingU, mappingV)。UnpackUV()恢复有限精度的二维映射坐标。TRANSFORM_TEX应用_FaceTex_ST或_OutlineTex_ST的 Tiling/Offset。它们用于_FaceTex和_OutlineTex_MainTexSDF Atlas 使用input.texcoord0。Pack 使用511Unpack 使用1/512解包结果包含固定量化误差。九、pixel_t输出字段output.position vPosition; output.color input.color; output.atlas input.texcoord0; output.param float4(alphaClip, scale, bias, weight); output.mask ...; output.viewDir ...; output.textures float4(faceUV, outlineUV);输出字段内容片元阶段用途position裁剪空间顶点位置光栅化位置colorTMP 顶点颜色Face RGB 与最终 Alpha 调制atlasTEXCOORD0字体图集 UV采样_MainTexparam.xalphaClipAtlas 采样的早期clip()阈值param.yscale把 Atlas 距离换算到屏幕尺度param.zbias字重和半过渡补偿后的边界阈值param.wweightBevel 法线轮廓等后续计算mask矩形中心坐标与软裁剪斜率UNITY_UI_CLIP_RECTviewDir经_EnvMatrix旋转的顶点到摄像机向量Bevel 环境反射textures.xyFace Texture UV采样_FaceTextextures.zwOutline Texture UV采样_OutlineTexviewDir的计算为mul( (float3x3)_EnvMatrix, _WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, vert).xyz)它先得到世界空间中“顶点指向摄像机”的向量再通过环境矩阵旋转用于后续 Cubemap 反射。十、完整关系bold 标记 └─ 选择 _WeightNormal / _WeightBold └─ /4 _FaceDilate └─ × _ScaleRatioA × 0.5 └─ weight ├─ bias 0.5 - weight 0.5/scale └─ alphaClip ... - weight Atlas 与装饰纹理 input.texcoord0 → output.atlas → _MainTex input.texcoord1.x → UnpackUV → _FaceTex / _OutlineTex input.texcoord1.y → bold SDF scale十一、源码索引Assets/TextMesh Pro/Shaders/TMP_SDF.shader顶点输出、bias与alphaClip。Assets/TextMesh Pro/Shaders/TMPro.cgincUnpackUV()与片元颜色函数。Scripts/Runtime/TMP_ShaderUtilities.cs_ScaleRatioA/B/C的计算。Scripts/Editor/TMP_SDFShaderGUI.cs材质面板中的 Weight、Face Dilate 等属性。
延伸阅读

更多相关文章

2026/10/7 2:25:50

积少成多:实用开源项目

目录一、Free FS - 现代化文件管理网盘系统二、Umi-OCR 文字识别工具三、Type Words四、Paper Burner X五、LosslessCut — 无损视频/音频剪辑的瑞士军刀六、FlyingMouse Format / 飞鼠格式 — 离线万能文件格式转换工具一、Free FS - 现代化文件管理网盘系统 一个基于 Spring…

2026/10/8 13:40:52

构建团队内部的 AI 代码评审代理:从规则配置到效果打磨

在很多技术团队雄心勃勃地引入“AI 自动化代码审查(AI Code Reviewer)”之后,事情的走向往往会迅速演变成一场令人啼笑皆非的闹剧: 机器人一上线,无论开发者提交了什么代码,它都会在 PR 下面疯狂刷屏二三十…

2026/10/8 13:40:52

W1 性能压测收官指南:双 11 容量摸底全链路基线报告构建方法论

时值国庆长假收官日,为期一周的大促前全链路压力演练与系统容量摸底暂告一个段落。在这场跨越网络层、运行时、存储引擎与大模型推理调度器的高负荷实战中,工程团队积累了数以亿计的底层性能追踪样本。 然而在很多技术团队中,压测演练往往陷入…

2026/10/8 13:40:52

混合 AI 架构的战术协调器:行为树与 Utility AI 的互补型分层实现

在开放世界与复杂战术射击游戏中,纯粹依赖单一决策模型往往会遭遇架构瓶颈。行为树(Behavior Tree, BT)的层级化结构在处理确定性流程、序列执行与状态兜底时表现极其稳定,但在面对数十种动态交织的环境变量(如玩家威胁…

2026/10/8 13:35:52

ponytail前端调试插件实战指南:接口捕获与Mock数据全攻略

最近群里好几个前端同事在问 ponytail 这个插件到底怎么用,有人觉得它比 DevTools 顺手,有人装完打开却发现面板空白,完全不知道从哪里下手。说实话我刚开始接触 ponytail 的时候也差不多是这个状态,前后折腾了一下午才把它的脾气…

2026/10/8 10:03:18

Jev+Agent接管浏览器:browser-use实战与jev-ultrafast性能优化

1. 从“Jev”说起:为什么我要把Agent接进浏览器“Jev”这个词最近在圈子里出现的频率越来越高,很多人第一次听到会以为是某个新模型的名字,其实它更像是一种思路——把Jev模型的能力当作底座,通过Agent的方式去接管浏览器&#xf…

2026/10/8 10:03:20

多智能体集群实战:DeepAgents编排、MCP与A2A协议及Skills体系

1. 从"单兵作战"到"集群协同":多智能体编排到底在解决什么问题如果你最近在折腾 Agent 相关的东西,大概率会有一种感觉:单个 Agent 能做的事情,其实很快就摸到天花板了。你给它一个提示词,挂几个工…

2026/10/8 6:05:44

无源低通滤波器设计实战:从RC到LC,手把手教你避开那些坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/8 0:02:17

自然数立方等于连续奇数之和:从证明到编程验证

十几年来我一直游走在数学科普和编程教学这两块内容之间,对“看起来像魔法、拆开全是数学”的结论总是格外敏感。最近翻资料时又撞见一句话:任何一个自然数 m 的立方,都可以写成 m 个连续奇数之和。2 的立方等于 3 加 5,3 的立方等…

2026/10/8 0:02:17

C#上位机SSH连接实战:用SSH.NET补齐超时、批量与密钥认证

简介:这是一份基于 C# 开发的 SSH 连接功能半成品工程,原本作为另一个主项目的子功能模块,现独立打包分享。工程采用 WinForms 界面,包含源码、解决方案、安装部署工程、NuGet 依赖包及说明文档,适合正在做远程连接、网…

2026/10/8 0:02:17

Java SpringBoot一体化智能售后系统设计与实现全解析

毕业设计年年做,Java Web 方向的题目翻来覆去就那么几个,但“一体化智能售后系统”这个题,每次看到我都觉得值得认真聊一聊。它不是一个简单 curd 堆出来的管理系统,而是把客户、工单、派单、处理、回访、统计整条链路串起来的一套…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
☎咨询二维码 ☎ ↑