Unity UGUI Image解析(二)
Unity UI Image 源码解析二Filled 径向填充与 RadialCut本文是《Unity UI Image 源码解析》的第二篇继续分析Image.Type.Filled重点说明Radial90、Radial180、Radial360以及底层的RadialCut算法。第一篇负责梳理Simple、Sliced、Tiled和Filled的整体网格生成流程本文只讨论 Filled 的径向裁剪部分。本文对照的源码版本Unity2022.3.62f3uGUIcom.unity.ugui1.0.0。源码位置Library/PackageCache/com.unity.ugui1.0.0/Runtime/UI/Core/Image.cs系列导航上一篇Unity UI Image 源码解析一Simple、Sliced、Tiled 与 Filled当前Unity UI Image 源码解析二Filled 径向填充与 RadialCut。一、径向填充的整体流程GenerateFilledSprite使用两个长度为 4 的静态数组保存矩形位置和 UV。源码中的实际类型是Vector3[]不是Vector2[]staticreadonlyVector3[]s_XynewVector3[4];staticreadonlyVector3[]s_UvnewVector3[4];初始化后的顶点顺序为索引位置坐标0左下(v.x, v.y)1左上(v.x, v.w)2右上(v.z, v.w)3右下(v.z, v.y)UV 使用相同顺序s_Xy[0]newVector2(v.x,v.y);s_Xy[1]newVector2(v.x,v.w);s_Xy[2]newVector2(v.z,v.w);s_Xy[3]newVector2(v.z,v.y);s_Uv[0]newVector2(tx0,ty0);s_Uv[1]newVector2(tx0,ty1);s_Uv[2]newVector2(tx1,ty1);s_Uv[3]newVector2(tx1,ty0);虽然右侧赋值使用了Vector2但它会隐式转换并保存到Vector3[]中Z 分量为 0。1. fillAmount 的边界处理fillAmount的公开属性会把输入限制在 0 到 1publicfloatfillAmount{get{returnm_FillAmount;}set{if(SetPropertyUtility.SetStruct(refm_FillAmount,Mathf.Clamp01(value))){SetVerticesDirty();}}}生成网格时还有两个重要边界m_FillAmount 0.001f直接返回不生成顶点m_FillAmount 1f径向模式不再执行RadialCut而是直接添加完整 Quad。因此 Radial180 和 Radial360 在fillAmount1时不会继续拆成 2 个或 4 个区域而是只生成一个完整 Quad。2. Fill Origin 对照同一个m_FillOrigin在不同填充模式下含义不同值Origin90Origin180Origin3600BottomLeftBottomBottom1TopLeftLeftRight2TopRightTopTop3BottomRightRightLeftfillMethod改变时Unity 会把m_FillOrigin重置为 0。反序列化时也会修正超出当前模式范围的 Origin。二、RadialCut 的核心逻辑径向模式最终都会调用下面这个入口staticboolRadialCut(Vector3[]xy,Vector3[]uv,floatfill,boolinvert,intcorner){// 没有需要绘制的部分if(fill0.001f)returnfalse;// 奇数 corner 会反转裁剪方向if((corner1)1)invert!invert;// 已经是完整区域无需调整顶点if(!invertfill0.999f)returntrue;// 把 0~1 映射到 0~90°floatangleMathf.Clamp01(fill);if(invert)angle1f-angle;angle*90f*Mathf.Deg2Rad;floatcosMathf.Cos(angle);floatsinMathf.Sin(angle);// 位置和 UV 必须使用相同参数一起裁剪RadialCut(xy,cos,sin,invert,corner);RadialCut(uv,cos,sin,invert,corner);returntrue;}这里有三个容易遗漏的细节m_FillClockwise作为invert传入但它不一定等于辅助函数最终使用的invert当corner为奇数时源码会执行invert !invert位置数组xy和 UV 数组uv必须做完全相同的裁剪否则几何边界和纹理边界会错位。源码注释写的是 “Even corners invert the fill direction”但实际条件是(corner 1) 1执行逻辑对应奇数corner。分析行为时应以代码条件为准。1. corner 如何决定顶点索引第二个重载先根据corner旋转四个索引inti0corner;inti1(corner1)%4;inti2(corner2)%4;inti3(corner3)%4;因此i0、i1、i2、i3不是固定对应某个屏幕方向。不同 Origin 或不同分区传入不同corner后同一条赋值语句会作用到不同的实际顶点。后面的示意图可以帮助理解某个具体 Origin但不能把图中的 B、C、D、E 点名直接推广到所有corner。2. 为什么比较 sin 和 cossin和cos决定裁剪射线先碰到矩形的水平边还是垂直边sin cos把sin归一化为 1再用cos / sin计算横向插值cos sin把cos归一化为 1再用sin / cos计算纵向插值sin cos对应 45°两个比例都设为 1。以偶数corner分支为例源码为if(cossin){sin/cos;cos1f;if(!invert){xy[i1].yMathf.Lerp(xy[i0].y,xy[i2].y,sin);xy[i2].yxy[i1].y;}}elseif(sincos){cos/sin;sin1f;if(invert){xy[i2].xMathf.Lerp(xy[i0].x,xy[i2].x,cos);xy[i3].xxy[i2].x;}}else{cos1f;sin1f;}if(invert)xy[i3].yMathf.Lerp(xy[i0].y,xy[i2].y,sin);elsexy[i1].xMathf.Lerp(xy[i0].x,xy[i2].x,cos);奇数corner还会进入另一套 X/Y 对调后的分支。这样同一套算法才能覆盖四个起始角。三、FillMethod.Radial90Radial90以矩形的一个角为径向中心把fillAmount映射到 0° 到 90°if(fillMethodFillMethod.Radial90){if(RadialCut(s_Xy,s_Uv,m_FillAmount,m_FillClockwise,m_FillOrigin)){AddQuad(toFill,s_Xy,color,s_Uv);}}它最多添加一个 Quad因此最多生成 4 个顶点和 2 个三角形。下面沿用原图以Origin90.BottomLeft也就是corner0为例。因为 0 是偶数invert不会被额外翻转所以此时辅助函数中的invert与m_FillClockwise相同。1. Clockwise true(逆时针)此时inverttrue角度使用补值计算floatangleMathf.Clamp01(fill);if(invert)angle1f-angle;angle*90f*Mathf.Deg2Rad;1cos sin下图左下角为i0A左上角为i1E右上为i2D右下为i3B这一分支先执行sin/cos;cos1f;对于偶数corner且inverttrue随后通过sin更新i3的 Y 坐标B移到C处xy[i3].yMathf.Lerp(xy[i0].y,xy[i2].y,sin);2 cos sin45° 时不需要先缩放某一个比例最后按inverttrue的路径更新i1的 y 坐标。B移到C处上图画错3cos sin这一分支先把sin归一化为 1再同时修正i2、i3的 X 坐标最后还会更新i3的 Y 坐标B和C移到D处cos/sin;sin1f;xy[i2].xMathf.Lerp(xy[i0].x,xy[i2].x,cos);xy[i3].xxy[i2].x;xy[i3].yMathf.Lerp(xy[i0].y,xy[i2].y,sin);2. Clockwise false(顺时针)对于当前的偶数corner此时invertfalse不计算补角。1sin cos下图左下角为i0A左上角为i1E右上为i2c右下为i3B该分支只归一化比例最终通过cos更新i1的 X 坐标E移到D处cos/sin;sin1f;xy[i1].xMathf.Lerp(xy[i0].x,xy[i2].x,cos);2sin cos45° 时不需要先缩放某一个比例最后按invertfalse的路径更新i1的 X 坐标。D移到C处3sin cos该分支会先让i1和i2使用相同的 Y 坐标再更新i1的 X 坐标E和D移到C处sin/cos;cos1f;xy[i1].yMathf.Lerp(xy[i0].y,xy[i2].y,sin);xy[i2].yxy[i1].y;xy[i1].xMathf.Lerp(xy[i0].x,xy[i2].x,cos);四、FillMethod.Radial180Radial180不直接对完整矩形做一次 180° 裁剪而是先拆成两个半区再把每个半区作为一个 Radial90 区域处理。1. Bottom 或 Top拆成左右两半当m_FillOrigin为 Bottom0或 Top2时if(m_FillOrigin0||m_FillOrigin2){fy00f;fy11f;if(sideeven){fx00f;fx10.5f;}else{fx00.5f;fx11f;}}B为半圆圆心C移到D点左右半区的归一化范围分别为左半区fx0 0.0fx1 0.5 右半区fx0 0.5fx1 1.0 纵向 fy0 0.0fy1 1.0even m_FillOrigin 1 ? 1 : 0用于调整 Top 与 Bottom 情况下两个半区的处理顺序。2. Left 或 Right拆成上下两半当m_FillOrigin为 Left1或 Right3时源码改为拆分 Y 轴fx00f;fx11f;if(sideeven){fy00.5f;fy11f;}else{fy00f;fy10.5f;}G为半圆圆心A移到B点上下半区的归一化范围分别为上半区fy0 0.5fy1 1.0 下半区fy0 0.0fy1 0.5 横向 fx0 0.0fx1 1.03. 每个半区的局部填充值两个半区分别计算自己的局部valfloatvalm_FillClockwise?fillAmount*2f-side:m_FillAmount*2f-(1-side);这里源码第一项使用公开属性fillAmount第二项使用字段m_FillAmount由于属性 getter 直接返回该字段两者数值相同。val随后会经过Mathf.Clamp01if(RadialCut(s_Xy,s_Uv,Mathf.Clamp01(val),m_FillClockwise,(sidem_FillOrigin3)%4)){AddQuad(toFill,s_Xy,color,s_Uv);}因此一个半区可以处于三种状态val 0RadialCut返回 false不添加该半区0 val 1裁剪当前半区val 1当前半区完整显示。fillAmount 1时 Radial180 最多添加两个独立 Quad也就是最多 8 个顶点、4 个三角形。五、FillMethod.Radial360Radial360把完整矩形拆成四个象限corner区域X 范围Y 范围0左下00.500.51左上00.50.512右上0.510.513右下0.5100.5源码中的区域计算为for(intcorner0;corner4;corner){floatfx0,fx1,fy0,fy1;if(corner2){fx00f;fx10.5f;}else{fx00.5f;fx11f;}if(corner0||corner3){fy00f;fy10.5f;}else{fy00.5f;fy11f;}// 根据 fx0、fx1、fy0、fy1 生成当前象限的位置和 UV。}每个象限的局部填充值为floatvalm_FillClockwise?m_FillAmount*4f-((cornerm_FillOrigin)%4):m_FillAmount*4f-(3-((cornerm_FillOrigin)%4));然后把当前象限交给RadialCutif(RadialCut(s_Xy,s_Uv,Mathf.Clamp01(val),m_FillClockwise,(corner2)%4)){AddQuad(toFill,s_Xy,color,s_Uv);}J为圆心B和C移到D点图中的四个区域都复用 Radial90 的裁剪逻辑。m_FillOrigin决定从哪个象限开始m_FillClockwise决定象限推进顺序val决定当前象限是不绘制、部分绘制还是完整绘制。fillAmount 1时 Radial360 最多添加 4 个独立 Quad也就是最多 16 个顶点、8 个三角形。fillAmount 1时则直接添加一个完整 Quad。六、算法总结1. 执行流程阶段操作关键变量或方法1清空旧顶点数据toFill.Clear()2过滤接近 0 的填充值m_FillAmount 0.001f3获取绘制矩形和 Outer UVGetDrawingDimensions、GetOuterUV4初始化 4 个位置和 UVs_Xy、s_Uv5根据模式拆成 1、2 或 4 个区域Radial90、Radial180、Radial3606计算当前区域的局部填充值val7同时裁剪位置与 UVRadialCut(xy, ...)、RadialCut(uv, ...)8提交有效区域AddQuad2. 局部填充值公式模式方向公式Radial90两种方向直接使用m_FillAmountRadial180ClockwisefillAmount * 2 - sideRadial180Counter-clockwisem_FillAmount * 2 - (1 - side)Radial360Clockwisem_FillAmount * 4 - ((corner origin) % 4)Radial360Counter-clockwisem_FillAmount * 4 - (3 - ((corner origin) % 4))所有 Radial180 和 Radial360 的局部val都会先经过Mathf.Clamp01再传给RadialCut。3. 常用变量变量源码类型含义vVector4绘制矩形x左、y下、z右、w上outerVector4Sprite Outer UVx左、y下、z右、w上tx0, ty0, tx1, ty1floatUV 的左右下上边界s_XyVector3[]当前 Quad 的 4 个位置s_UvVector3[]当前 Quad 的 4 个 UVm_FillAmountfloat总填充比例范围为 01m_FillClockwisebool径向填充方向m_FillOriginint当前 FillMethod 对应的起点枚举值cornerint当前 Radial90 局部区域的角索引invertboolRadialCut内部实际使用的裁剪方向4. 顶点数量模式fillAmount 1时的最大顶点数最大三角形数Radial9042Radial18084Radial360168这些 Quad 之间不共享顶点。fillAmount 1时三种径向模式都会直接生成一个完整 Quad即 4 个顶点和 2 个三角形。七、总结Radial90 直接裁剪一个矩形Radial180 先拆成两个半区再分别执行 Radial90 裁剪Radial360 先拆成四个象限再分别执行 Radial90 裁剪RadialCut会同步修改位置与 UVcorner为奇数时会翻转invert所以m_FillClockwise不能在所有情况下直接等同于辅助函数中的最终裁剪方向s_Xy和s_Uv的真实类型是Vector3[]fillAmount接近 0 时不生成网格等于 1 时直接生成完整 Quad。理解这套算法的关键不是死记某个图中的顶点字母而是先确认当前corner对应的i0i3再结合invert、sin和cos判断实际移动的顶点。

相关新闻

如何专业部署RR引导:3大实战技巧与自动化NAS解决方案

如何专业部署RR引导:3大实战技巧与自动化NAS解决方案

如何专业部署RR引导:3大实战技巧与自动化NAS解决方案 【免费下载链接】rr Redpill Recovery (arpl-i18n) 项目地址: https://gitcode.com/gh_mirrors/rr2/rr RR引导(Redpill Recovery)是一个革命性的开源项目,专为在x86/x6…

2026/10/22 22:55:04 阅读更多 →
使用大疆TSDK提取热红外图像(RJPG)温度信息,热红外图像转tiff或tif,并使用pix4d将tiff或tif进行拼接 | 热红外照片温度信息提取,可处理1280x1024图像大小(重制版二)

使用大疆TSDK提取热红外图像(RJPG)温度信息,热红外图像转tiff或tif,并使用pix4d将tiff或tif进行拼接 | 热红外照片温度信息提取,可处理1280x1024图像大小(重制版二)

看到很多人在处理热红外图像的时候会遇到两个问题,一个是在代码中调整四个参数然后再arcmap中打开查看太费时间和精力了,还有一个是代码只能处理640x512大小的热红外图像,处理1280x1024大小的热红外图像会报错。这次的重置版主要解决这两个问…

2026/10/22 23:11:13 阅读更多 →
银河麒麟V10硬件信息排查:dmidecode/lshw/lsscsi常用命令手册

银河麒麟V10硬件信息排查:dmidecode/lshw/lsscsi常用命令手册

查询硬件信息硬件关系图谱Windowslinux查看cpu信息lscpufreedmidecode命令(硬件)所有参数biossystembaseboard(主板)chassis()processor(处理器)网卡网卡硬件信息查看所有网络接口查看网口详细信息lspci硬件关系图谱 #mermaid-svg-VA7dGFSnQSy0GWkF{font…

2026/10/22 16:49:01 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/22 10:18:38 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/22 11:01:53 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/22 9:44:47 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/22 10:42:00 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/22 10:40:08 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/22 10:56:37 阅读更多 →

日新闻

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型(GMM)的语音识别系统,很多人第一反应就是一堆数学公式,觉得门槛很高。但如果你真的在MATLAB里把它落地跑通,会发现核心其实就是三件事:把声音变成特征向量,用GMM给每个类别…

2026/10/22 1:47:45 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人,但落到具体操作上,无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程:对图像做反白、改调色板、彩色转灰度,最后把像素数据导出成txt文件。单看每一…

2026/10/22 21:33:42 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词,你会发现一个很有意思的现象:每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型,有人指的是运动控制系统,还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/22 5:12:39 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

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

2026/10/22 22:56:44 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

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

2026/10/22 22:56:12 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

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

2026/10/22 19:06:46 阅读更多 →