Unity Shader实现环形圆形:解决圆形向中心挤压问题
问题解决:Unity Shader环形排列圆形的挤压与偏移问题
问题分析
你的核心问题出在宽高比的错误应用和UV坐标空间的不一致处理上:
- 错误地将
center的x坐标乘以宽高比,导致整个UV空间偏移 - 对UV的x轴重复缩放(先减偏移再乘宽高比,后续又对圆的位置乘宽高比),导致圆形被挤压
- 角度计算使用了已经缩放后的UV,导致环形排列的逻辑错乱
修正方案
我们需要统一UV空间的处理,确保宽高比只在需要修正圆形形状的时候应用,而不是破坏整个坐标系统的中心和角度计算:
修正后的片元函数
fixed4 frag(v2f i) : SV_Target { float aspectRatio = _ScreenParams.x / _ScreenParams.y; // 正确的UV中心,UV本身是0-1范围,中心就是(0.5,0.5) float2 center = float2(0.5, 0.5); float2 uv = i.uv - center; // 仅在计算距离时修正宽高比,保证圆形为正圆,不影响角度计算 float2 uvForDistance = uv; uvForDistance.x *= aspectRatio; // 使用原始UV计算角度,确保环形排列的角度逻辑正确 float angle = atan2(uv.y, uv.x); // 统一角度到0-2π范围,替代原有的三目判断 angle = fmod(angle + UNITY_TWO_PI, UNITY_TWO_PI); // 计算每个小圆的间隔角度,确定目标位置 float stepAngle = UNITY_TWO_PI / _CircleNumber; float targetAngle = floor(angle / stepAngle) * stepAngle + stepAngle * 0.5; float2 circlePos = _DistanceFromCenter * float2(cos(targetAngle), sin(targetAngle)); // 用修正宽高比后的UV计算点到圆心的距离 float distance = length(uvForDistance - circlePos * float2(aspectRatio, 1.0)); float circlePattern = smoothstep(_CircleRadius, _CircleRadius + 0.01, distance); // 修正lerp顺序:距离小于半径时显示圆颜色,否则显示背景 fixed4 result = lerp(_CircleColor, _BackgroundColor, circlePattern); return result; }
关键修正点
- 中心坐标修正:直接使用
(0.5,0.5)作为UV中心,避免坐标偏移 - 角度与距离分离处理:用原始UV计算角度保证环形布局正确,仅在距离计算时修正宽高比保证正圆
- 角度步进优化:通过
UNITY_TWO_PI / _CircleNumber计算小圆间隔角度,修复原代码角度逻辑错误 - smoothstep逻辑修正:调整lerp顺序,让圆形显示逻辑符合预期
完整修正后的Shader代码
Shader "Magnality/Loading/LoadingCircle" { Properties { _BackgroundColor("Background Color", Color) = (0, 0, 0, 0) _CircleColor("Circle Color", Color) = (1, 1, 1, 1) _CircleRadius("Circle Radius", Float) = 0.1 _CircleNumber("Circle Number", Int) = 10 _DistanceFromCenter("Distance from Center", Float) = 0.2 [HideInInspector] _MainTex("Dummy Texture", 2D) = "white" { } } SubShader { Tags { "RenderType"="Opaque" "Queue"="Transparent" } LOD 100 Blend SrcAlpha OneMinusSrcAlpha // 支持透明背景设置 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; fixed4 _BackgroundColor; fixed4 _CircleColor; float _CircleRadius; int _CircleNumber; float _DistanceFromCenter; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = v.uv; return o; } fixed4 frag(v2f i) : SV_Target { float aspectRatio = _ScreenParams.x / _ScreenParams.y; float2 center = float2(0.5, 0.5); float2 uv = i.uv - center; float2 uvForDistance = uv; uvForDistance.x *= aspectRatio; float angle = atan2(uv.y, uv.x); angle = fmod(angle + UNITY_TWO_PI, UNITY_TWO_PI); float stepAngle = UNITY_TWO_PI / _CircleNumber; float targetAngle = floor(angle / stepAngle) * stepAngle + stepAngle * 0.5; float2 circlePos = _DistanceFromCenter * float2(cos(targetAngle), sin(targetAngle)); float distance = length(uvForDistance - circlePos * float2(aspectRatio, 1.0)); float circlePattern = smoothstep(_CircleRadius, _CircleRadius + 0.01, distance); fixed4 result = lerp(_CircleColor, _BackgroundColor, circlePattern); return result; } ENDCG } } }
注:添加的透明混合标签用于支持背景透明需求,若不需要可移除Blend和Queue配置。
内容的提问来源于stack exchange,提问作者Magnality
相关产品推荐
相关产品推荐

