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/8 14:13:12

积少成多:实用开源项目

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

2026/10/9 11:51:36

Xcode Cloud、Python协程与HarmonyOS:终端降价背后的工程升级

1. 项目概述:这不是一条新闻,而是一组技术信号的交叉验证“极客日报:曝 iPhone 13 系列定价有望下调:起售价或低于 5499 元;TikTok 成为全球收入最高 App”——这个标题乍看是消费电子与移动应用市场的两条平行资讯&am…

2026/10/9 11:51:36

AnyPS5如何联网?libSceNet的TCP/UDP实现与完整移植拆解

AnyPS5如何联网?libSceNet的TCP/UDP实现与完整移植拆解 【免费下载链接】AnyPS5 Tool for automatic PS5 executables porting to Linux and Windows 项目地址: https://gitcode.com/GitHub_Trending/an/AnyPS5 AnyPS5 是一个将 PS5 可执行文件自动移植到 Li…

2026/10/9 11:46:35

Python EasyDict 配置管理实战:从嵌套字典痛点到工程化方案

1. 为什么一个字典模块值得单独写一篇第一次在项目里看到from easydict import EasyDict这行代码的时候,我的反应是:字典就字典,Python 自带的dict用了这么多年,为什么还要额外装一个第三方库?直到我在一个图像处理项目…

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/9 0:04:27

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略

毕业论文初稿完成后首次进行AIGC疑似度自查的摸底与分流策略当数万字的学位论文初稿经历开题、实验、问卷与多轮文献梳理最终成形时,绝大多数研究生都会面临一道全新的形式审查关卡:AIGC 疑似度排查。在高校毕业审核流程中,盲审前的文本检测通…

2026/10/9 0:04:27

食堂节能改造源头工厂,商用厨房设备焕新方案广受好评

商用厨房作为餐饮经营、单位供餐的核心后勤阵地,其设备配置、动线规划与运维体系直接决定后厨作业效率、运营成本与合规性。从基础的灶具、制冷存储设备,到油烟净化、水处理等配套系统,每一个环节的合理性都与食品安全、能耗管控、消防安全挂…

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

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

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