在2D游戏中实现地球仪的旋转

发布时间: 2022年06月04日阅读数: 9

球面UV

代码和说明

1. 固定地轴Shader


主要逻辑在Fragment Shader中,具体如下:

real4 frag(v2f i) : SV_Target
{
    // 将UV原点换到图片中央
    float2 uv = i.uv - 0.5;
    
    // 根据勾股定理计算 z = sqrt( r*r - (x*x + y*y ))
    float3 pos = float3(uv.x, uv.y, sqrt(0.25 - (uv.x * uv.x + uv.y * uv.y)));
    
    // 将得到的点坐标转换到球面坐标系,得到phi角度并转到(-1,1)
    float p = atan2(pos.x, pos.z) * INV_PI;
    
    // 结合_Time.y 和 输入参数转动速度
    float2 suv = float2(frac(p + (_Speed * _Time.y)), i.uv.y);
    
    // 使用distance确定出圆的范围,结合smoothstep获得平滑的圆边缘
    real4 col = tex2D(_MainTex, suv) * smoothstep(0.51, 0.49, distance(i.uv, 0.5));
    return col;
}

2.地轴可旋转Shader

效果如下:

Shader说明如下:

real4 frag(v2f i) : SV_Target
{
    // 球的半径为UV范围的一半
    half r = 0.5;
    // 半径的平方
    half rr = r * r;
    
    // 移动UV原点到画布中心
    float2 uv = i.uv - r;
    
    // 将角度转为弧度
    half angle = radians(_Angle);

    float x = uv.x;
    float xx = x * x;
    
    // 根据勾股定理算出当前点在笛卡尔坐标相对x轴的距离
    float le = sqrt(rr - xx);
    
    // 计算当前点所在YZ平面确定的圆上旋转角度
    float angleY = asin(uv.y/le);

    // 当前旋转角度加上给定的地轴旋转角度就是当前点的最终角度
    // 根据该角度计算当前点的最终坐标
    float y = sin(angleY + angle) * le;
    float z = sqrt(rr - (xx + y * y));

    // 当前点如果位于背面,因为地轴旋转导致显示出现的情况,则需要将z值反转
    if(HALF_PI - angleY < angle)
    {
        z = -z;
    }

    // 将最终坐标转成球面坐标系,然后映射到UV的范围
    float p = atan2(x, z) * INV_PI * 0.5;
    float t = (asin(y / r) * INV_PI + .5);
    
    // 最终经过球面映射的UV
    float2 suv = float2(frac(p  + (_Speed * _Time.y)), t);
    
    real4 col = tex2D(_MainTex, suv) * smoothstep(0.51, 0.49, distance(i.uv, 0.5));
    return col;
}