UnityShader实战(2) 基于UV坐标绘制可动态调节的2D圆环

张开发
2026/5/31 16:22:49 15 分钟阅读
UnityShader实战(2) 基于UV坐标绘制可动态调节的2D圆环
1. UV坐标与2D圆环绘制原理第一次接触Unity Shader时我被UV坐标这个概念困扰了很久。直到有一天我把UV值直接输出为颜色才恍然大悟——原来UV就是每个像素在纹理上的家庭住址。在2D Sprite渲染中UV坐标从左上角的(0,0)延伸到右下角的(1,1)这个坐标系就是我们绘制完美圆环的秘密武器。要画圆环核心是计算每个像素到中心点的距离。这里有个数学小技巧不用直接计算平方根比较距离平方更高效。在片元着色器中我这样实现float disToCenter (i.uv.x-0.5)*(i.uv.x-0.5) (i.uv.y-0.5)*(i.uv.y-0.5);这个公式相当于把UV坐标系原点移到Sprite中心(0.5,0.5)然后计算欧式距离的平方。比起使用内置的distance函数手动计算可以避免不必要的开方运算这在移动端性能优化时特别重要。2. 动态参数化圆环的实现2.1 颜色与宽度调节让美术同事能随时调整圆环外观是必备功能。我在Shader的Properties块中暴露了两个关键参数_ColorTint (Color, Color) (1, 1, 1, 1) _CircleEdge (CircleEdge, float) 0.1这里有个坑我踩过_CircleEdge参数如果直接用作圆环宽度会导致缩放变形。后来我将其转换为内半径比例float _CircleEdgeInFrag (1 - _CircleEdge)*0.5;这样当_CircleEdge0.1时内半径就是外半径的90%无论怎么缩放都能保持视觉一致性。测试时发现用0.5*0.5作为外半径基准值最合适刚好匹配Unity的默认Sprite尺寸。2.2 抗锯齿优化原始方案会有明显的锯齿边缘特别是在移动设备上。我改进后的抗锯齿版本使用了smoothstep函数float edgeFactor smoothstep( _CircleEdgeInFrag*_CircleEdgeInFrag - 0.001, _CircleEdgeInFrag*_CircleEdgeInFrag 0.001, disToCenter ); float outerFactor smoothstep( 0.5*0.5 - 0.001, 0.5*0.5 0.001, disToCenter ); float finalAlpha outerFactor * (1 - edgeFactor);这个方案通过在边缘区域创建渐变过渡让圆环看起来更加平滑。参数0.001控制抗锯齿范围可以根据实际分辨率调整。3. 缩放时的宽度保持方案3.1 C#控制逻辑要实现缩放时圆环宽度不变需要在Update中动态调整Shader参数。关键代码void Update() { circleEdgeMaterial.SetFloat(_CircleEdge, rate / transform.localScale.x); }这里有个性能优化点不要在每帧获取Material而是在Start中缓存Material实例。我遇到过批量渲染失效的问题就是因为直接修改了Material属性而非MaterialInstance。3.2 数学关系推导保持物理宽度的核心公式是实际宽度 原始宽度 / 当前缩放比例在项目中我封装了一个CircleRing组件自动处理这些逻辑[RequireComponent(typeof(SpriteRenderer))] public class CircleRing : MonoBehaviour { [Range(0.01f, 0.5f)] public float physicalWidth 0.1f; private MaterialPropertyBlock _propBlock; void OnValidate() { UpdateRingProperties(); } void UpdateRingProperties() { if(_propBlock null) _propBlock new MaterialPropertyBlock(); var renderer GetComponentSpriteRenderer(); renderer.GetPropertyBlock(_propBlock); _propBlock.SetFloat(_CircleEdge, physicalWidth / transform.lossyScale.x); renderer.SetPropertyBlock(_propBlock); } }使用MaterialPropertyBlock而不是直接修改Material可以保持合批优化不受影响。4. 高级应用与性能优化4.1 多圆环混合效果通过叠加多个Pass可以实现同心圆环效果。每个Pass使用不同的_CircleEdge参数Pass { Name OuterRing CGPROGRAM // ...省略相同代码... fixed4 frag (v2f i) : SV_Target { float edge 0.3; float inner (1 - edge)*0.5; // ...距离计算... } ENDCG } Pass { Name InnerRing Blend SrcAlpha OneMinusSrcAlpha CGPROGRAM // ...修改edge值为0.15... ENDCG }这种技术可以用在技能冷却指示器等场景。注意要合理设置Blend模式避免颜色叠加异常。4.2 移动端优化技巧在低端设备上测试时发现两个优化点将if语句改为step函数GPU更擅长处理纯数学运算预计算常量把0.5*0.5这类计算移到CPU端优化后的片元着色器核心代码fixed4 frag (v2f i) : SV_Target { float disToCenter dot(i.uv-0.5, i.uv-0.5); float inRing step(_CircleEdgeSqr, disToCenter) * step(disToCenter, _OuterSqr); return fixed4(_ColorTint.rgb, inRing); }实测在Adreno 506 GPU上渲染耗时从1.2ms降低到0.8ms。对于需要大量使用圆环的UI系统这种优化非常值得。

更多文章