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/7/31 18:55:29 阅读更多 →
使用大疆TSDK提取热红外图像(RJPG)温度信息,热红外图像转tiff或tif,并使用pix4d将tiff或tif进行拼接 | 热红外照片温度信息提取,可处理1280x1024图像大小(重制版二)

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

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

2026/7/31 18:54:29 阅读更多 →
银河麒麟V10硬件信息排查:dmidecode/lshw/lsscsi常用命令手册

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

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

2026/7/31 18:54:29 阅读更多 →

最新新闻

大流量活动前端容量预估:从 PV 到 QPS 的换算与压测策略(续篇)

大流量活动前端容量预估:从 PV 到 QPS 的换算与压测策略(续篇)

大流量活动前端容量预估:从 PV 到 QPS 的换算与压测策略(续篇) 场景痛点 双十一秒杀活动。预估峰值PV 500万。前端团队问:500万PV对应多少QPS?需要多少台服务器?CDN能不能扛住? 运营给的数据…

2026/7/31 19:28:07 阅读更多 →
轻量级智能控制:G-Helper重新定义华硕笔记本性能管理

轻量级智能控制:G-Helper重新定义华硕笔记本性能管理

轻量级智能控制:G-Helper重新定义华硕笔记本性能管理 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook, Exp…

2026/7/31 19:28:07 阅读更多 →
BProgress自定义指南:3行代码修改颜色、高度和动画效果

BProgress自定义指南:3行代码修改颜色、高度和动画效果

BProgress自定义指南:3行代码修改颜色、高度和动画效果 【免费下载链接】next-nprogress-bar BProgress is a lightweight, customizable progress bar for better user experience. 项目地址: https://gitcode.com/gh_mirrors/ne/next-nprogress-bar BProgr…

2026/7/31 19:28:07 阅读更多 →
MinIO 部署安装与使用指南

MinIO 部署安装与使用指南

MinIO 是一款高性能、开源的分布式对象存储系统,100% 兼容 Amazon S3 API,常用于存储非结构化数据(图片、视频、日志文件、备份数据等),具备轻量易部署、性能优异、扩展性强的特点。一、部署方式MinIO 支持多种部署模式…

2026/7/31 19:28:07 阅读更多 →
2026年8月AI技术展望:值得重点关注的模型发布、工具更新与行业动态

2026年8月AI技术展望:值得重点关注的模型发布、工具更新与行业动态

2026年8月AI技术展望:值得重点关注的模型发布、工具更新与行业动态 一、8月AI领域的节奏预判——暑期收官前的密集发布 每年8月是AI领域较活跃的时间节点。一方面,上半年积累的研究成果会在夏季会议季集中发布;另一方面,各厂商会…

2026/7/31 19:28:07 阅读更多 →
7天掌握OpenMetadata:AI时代的数据治理自动化终极指南

7天掌握OpenMetadata:AI时代的数据治理自动化终极指南

7天掌握OpenMetadata:AI时代的数据治理自动化终极指南 【免费下载链接】OpenMetadata The Open Context Layer for Data and AI , OpenMetadata is the open platform for building trusted data context and business semantics for humans, AI assistants, and ag…

2026/7/31 19:27:07 阅读更多 →

日新闻

物理复制比逻辑复制好在哪?数据库复制原理详解

物理复制比逻辑复制好在哪?数据库复制原理详解

数据库复制是把主库数据同步到备库的机制,分为逻辑复制和物理复制两种。逻辑复制传输的是 SQL 语句或行变更事件,物理复制传输的是存储引擎底层的物理日志。阿里云 PolarDB(云原生数据库)采用物理复制,在同步延迟、数据…

2026/7/31 0:00:34 阅读更多 →
BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南

BilibiliDown:3分钟学会B站视频下载的终极指南 【免费下载链接】BilibiliDown (GUI-多平台支持) B站 哔哩哔哩 视频下载器。支持稍后再看、收藏夹、UP主视频批量下载|Bilibili Video Downloader 😳 项目地址: https://gitcode.com/gh_mirrors/bi/Bilib…

2026/7/31 0:00:34 阅读更多 →
有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

有哪些游戏数据AI平台?游戏行业Data+AI融合方案盘点

当前,游戏行业的“DataAI融合”已从概念验证进入价值落地阶段。根据IDC 2025年数据,中国AI游戏云市场规模已达18.6亿元;同时,游戏研发环节AI渗透率高达86%,生成式AI内容普及率超过50%。面对庞大的市场,游戏…

2026/7/31 0:00:34 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档,可以直接使用!系统支持图片、视频、摄像头等多种方式检测裂缝,功能强大实用。 1数据集6000张 8各类别

2026/7/31 1:03:03 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像! pubg绝地求生目标检测数据集 1分类:e_body,14905个标签,txt格式 共计14244张图,99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别: allies enemy tag图片总量:7247张训练集:5139张验证集:1425张测试集:683张标注状态:全部已标注,即拿即用数据格式:支持YOLO格式及其他格式&#…

2026/7/31 4:19:39 阅读更多 →

月新闻