技术目标
平滑圆角在系统中是一个极其底层的图元。 它会出现在按钮、卡片、遮罩、图标容器、面板切角等大量位置上,而且半径跨度极大,从 到 都可能出现。
这意味着技术方案必须同时满足四个条件:
- 形状稳定,不能因为缩放而产生明显变形
- 边缘平滑,不能在高对比场景里出现肉眼可见的毛刺
- 运行时成本可控,不能让系统里每一个圆角都重复做重计算
- 适配范围足够大,能覆盖从 2D UI 到更复杂实时渲染场景的长期演进
技术结构
整个技术链路可以概括成四步:
- 使用高连续性的曲线定义理想圆角形状
- 用高质量抗锯齿方案把矢量曲线栅格化为缓存贴图
- 对缓存图生成多级 Mipmap,保证不同缩放比例下的稳定显示
- 在 GPU 中按实际 LOD 对材质进行滤波采样并输出到屏幕
这套结构的关键在于,把“昂贵但高质量”的工作尽量前置到离线或预计算阶段,把运行时的责任压缩到 GPU 最擅长的采样环节。
曲线建模:为什么选 Degree 4 NURBS
NURBS 曲线的可控性很强,可以比较直接地控制局部曲率变化。 在这个项目里,曲线建模需要同时满足两个要求:
- 达到 G4 连续:圆角既是视觉元素,也是可能进入光照、法线生成等链路的几何基础。连续性做得越高,后续适配空间越大。
- 形状尽可能接近正圆:系统历史上大量元素已经与正圆圆角建立了认知绑定,升级时不能让整体气质突然偏移。


如果只看数学上的“更平滑”,超椭圆也是一个很自然的候选。 但在系统设计里,曲线不是孤立存在的,多个圆角并排时形成的负形空间同样会改变用户对平台气质的感知。 因此最终的技术选型其实是一个被设计语言约束过的工程决策。
栅格化:为什么选 SSAA
现代屏幕本质上是像素阵列,矢量曲线最终仍然需要落到像素上。 栅格化方案并不缺,真正的问题是:在“圆角形状已经固定”的前提下,哪一种方案值得被预计算并缓存。
| 技术方案 | 简介 | 优点 | 缺点 |
|---|---|---|---|
| SSAA (超级采样抗锯齿) | 渲染高分辨率图像后降采样, 获得更平滑的边缘 | 画质最佳, 几乎无锯齿, 适用所有场景 | 性能消耗大, 内存占用高 |
| MSAA (多重采样抗锯齿) | 对几何边界进行多重采样, 提升边缘平滑度 | 性能与画质平衡, 对几何边界效果好 | 对透明纹理和复杂像素效果支持有限 |
| FXAA (快速近似抗锯齿) | 基于像素着色器的图像后处理, 检测边缘并模糊处理 | 性能消耗极低, 易于集成 | 细节丢失, 边缘模糊, 对高对比度场景效果有限 |
| TAA (时域抗锯齿) | 利用多帧历史数据进行采样和混合, 消除锯齿 | 能有效消除锯齿, 适合动态场景 | 容易产生拖影和鬼影, 动态场景下表现不佳 |
| CMAA (保守混合抗锯齿) | 结合边缘检测和混合, 优化性能与效果 | 性能消耗低, 兼容性好 | 效果有限, 部分场景下锯齿仍明显 |

因为圆角形状是可缓存的系统资产,而不是每一帧都在变化的复杂几何,我们可以直接选画质最好的 SSAA,先栅格化,再复用结果。 这类“高质量但昂贵”的方案,一旦被前置到缓存阶段,运行时负担就会显著下降。
重采样:为什么是 Lanczos + 9 层 Mipmap
单张高分辨率圆角图并不能解决所有问题。 如果只依赖一张 的缓存图在运行时做大范围缩小,图像会因为采样频率失配出现明显混叠。
这也是我最终把问题转化为典型纹理管线问题的原因:
- 先做高质量母版缓存
- 再生成多级 Mipmap
- 让运行时采样在更合适的频段上完成
下表是做方案评估时的几个候选重采样算法:
| 算法/类型 | 做法 | 优点 | 缺点/备注 |
|---|---|---|---|
| Nearest Neighbor (最近点) | 取最近整数坐标像素值, 无插值。 | 硬件支持 实现极快, 代码简单 | 明显块状、锯齿, 质量最低 |
| Bilinear (双线性) | 取周围 2x2 像素加权平均, 线性插值。 | 硬件支持 平滑、速度快 | 边缘模糊, 细节丢失 |
| Bicubic (三次插值) | 取 4x4 像素, 用三次卷积核插值。 | 清晰度与平滑度平衡, 细节较好 | 计算量较大, 可能轻微振铃 |
| Lanczos 重采样 (窗化 sinc) | 取更大邻域 (如 6x6) , 用 sinc 核窗化插值。 | 锐利且混叠抑制优秀 | 成本高, 可能出现振铃 (ringing) |
| Mitchell-Netravali | 三次核, 参数可调, 常用于高质量缩放。 | 细节与平滑度兼顾, 适合内容管线 | 算法复杂度中等 |
| Area/Box (区域平均) | 输出像素对应输入区域平均, 常用于缩小图像。 | 缩小时抗混叠好, 速度快 | 放大时模糊, 细节损失 |
| Gaussian (高斯核) | 用高斯权重模糊后采样。 | 平滑自然, 混叠抑制好 | 细节损失, 计算较重 |
| Catmull-Rom | 三次插值家族, 锐度高, 常用于内容管线。 | 边缘清晰, 细节好 | 振铃风险, 成本中等 |

最终选择 Lanczos 的原因有两个:
- 它对混叠抑制足够好,缩小时边缘更干净
- 它对原始信号的形状保持更完整,不容易把设计好的曲率关系“抹软”
我们可以选用 的窗口 Lanczos 归一化离散核对原始图 进行卷积以获得第一层重采样图像 :
接着对 递归操作,获得剩余 Mipmap 层级:
使用多级 Mipmap 而不是对单图直接缩小,是必要的。否则在大缩放比场景中会更容易出现 摩尔纹。

图片来自维基百科: https://upload.wikimedia.org/wikipedia/commons/5/59/Mipmap_Aliasing_Comparison.png
下面是 Nearest、Linear、Mitchell 和 Lanczos 的实机拍摄对比。可以看到 Nearest 和 Linear 有明显“毛刺”,Mitchell 与 Lanczos 都已经足够平滑,但继续放大后,Lanczos 对原始曲线形状的保留更好。


运行时采样:为什么只用 Trilinear
技术框架的最后一步是在多级 Mipmap 中进行材质采样和滤波。 常见选项如下:
| 滤波方式 | 简介 | 优点 | 缺点 |
|---|---|---|---|
| Nearest-neighbor | 选择最近像素点的颜色作为采样结果 | 实现简单, 性能高 | 画面锯齿明显, 细节丢失 |
| Nearest-neighbor with Mipmapping | 最近点采样结合多级 Mipmap | 性能高, 减少远距离采样伪影 | 仍有锯齿, 过渡不自然 |
| Bilinear | 对采样点周围四个像素做线性插值 | 边缘平滑, 性能较好 | 细节模糊, 远距离仍有伪影 |
| Trilinear with Mipmapping | 在 Mipmap 层间做线性插值 (双线性+层间插值) | 过渡自然, 锯齿和伪影减少 | 性能消耗增加, 细节仍有限 |
| Anisotropic (各向异性滤波) | 根据视角优化采样, 提升斜角和远距离纹理清晰度 | 细节保留好, 远距离和斜角表现最佳 | 性能消耗较高, 硬件支持要求高 |
考虑到目标场景主要是移动端 UI,而不是大透视角度的斜向纹理观察,因此各向异性并不划算。 最终选择 Trilinear with Mipmapping,本质上是一个性价比很高的终端解法:画质已经足够稳定,且现代 GPU 对它有成熟硬件支持。

实际结果


用显微镜观察屏幕次像素的表现可以明显感知到,新的平滑圆角在直边转向圆角边的过渡更加平滑,基本消除了原先的 bump 感。

后续演进
经过三年多断断续续的研究和对行业其他实现的分析,目前已经可以把高性能平滑圆角抽象成一条比较稳定的技术架构。 之后如果图形学某个中间环节出现更优算法,只需要替换该阶段,而不需要重做整条链路。
比如在重采样部分,John Costella 提出过 Magic Kernel 这样的卷积核,它在某些低分辨率设备上可能会有潜在价值。 对我来说,这个项目真正重要的不是“这次用了哪一种算法”,而是把圆角问题转译成了一条可以持续升级的图形管线。