【问题标题】:Shader that transforms a mercator projection to equirectangular?将墨卡托投影转换为等距矩形的着色器?
【发布时间】:2020-01-25 09:30:10
【问题描述】:

我正在尝试在 Unity 中制作一个着色器,以 mercator projection texture 作为源并将其转换为 equirectangular projection texture。

输入示例:

输出示例:

This example does the opposite with an equirectangular as source.

如果你看一下上面例子的出处:

 // mercator
 float latClamped = clamp(lat, -1.4835298641951802, 1.4835298641951802);
 float yMerc = log(tan(PI / 4.0 + latClamped / 2.0)) / PI2;
 float xMerc = xEqui / 2.0;
 vec4 mercatorPos = vec4(xMerc, yMerc, 0.0, 1.0);

任何人都可以帮助扭转这种情况,这样我就可以从作为源的墨卡托地图转到 equirectangular(甚至更好,azimuthal)。

寻找一种方法来进行从 x/y 到经度 (x)/纬度(y) 并返回的 2D 纹理变形。

感谢您的意见。

【问题讨论】:

    标签: unity3d math shader gis map-projections


    【解决方案1】:

    如果要输出等角投影,则需要将等矩形坐标转换为墨卡托坐标,然后在这些坐标处对墨卡托投影进行采样。

    这就是它在 uvs 的片段着色器中的样子:

    //uv to equirectangular
    float lat = (uv.x) * 2 * PI;    // from 0 to 2PI
    float lon = (uv.y - .5f) * PI;  // from -PI to PI
    
    // equirectangular to mercator
    float x = lat;
    float y = log(tan(PI / 4. + lon / 2.));
    
    // bring x,y into [0,1] range
    x = x / (2*PI);
    y = (y+PI) / (2*PI);
    
    // sample mercator projection
    fixed4 col = tex2D(_MainTex, float2(x,y));
    

    同样的事情也适用于方位角投影:您可以从方位角坐标 -> equirectangular -> 墨卡托对图像进行采样。或者你可以找到一个公式直接从方位角 -> 墨卡托。 wiki 页面有一堆公式可以在投影之间来回切换。

    这是一个完整的着色器。输入是墨卡托投影,输出等距或方位角投影(从下拉菜单中选择)

    Shader "Unlit/NewUnlitShader 1"
    {
        Properties
        {
            _MainTex ("Texture", 2D) = "white" {}
            [Enum(Equirectangular,0,Azimuthal,1)]
            _Azimuthal("Projection", float) = 0
    
        }
        SubShader
        {
            Tags { "RenderType"="Opaque" }
            LOD 100
    
            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;
                };
    
                sampler2D _MainTex;
                float4 _MainTex_ST;
                float _Azimuthal;
    
                v2f vert (appdata v)
                {
                    v2f o;
                    o.vertex = UnityObjectToClipPos(v.vertex);
                    o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                    return o;
                }
    #define PI 3.141592653589793238462f
    #define PI2 6.283185307179586476924f
    
                float2 uvToEquirectangular(float2 uv) {
                    float lat = (uv.x) * PI2;   // from 0 to 2PI
                    float lon = (uv.y - .5f) * PI;  // from -PI to PI
                    return float2(lat, lon);
                }
    
                float2 uvAsAzimuthalToEquirectangular(float2 uv) {                  
                    float2 coord = (uv - .5) * 4; 
    
                    float radius = length(coord);
                    float angle = atan2(coord.y, coord.x) + PI;
    
                    //formula from https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection
                    float lat = angle;
                    float lon = 2 * acos(radius / 2.) - PI / 2;
                    return float2(lat, lon);
                }           
    
                fixed4 frag(v2f i) : SV_Target
                {
                    // get equirectangular coordinates
                    float2 coord = _Azimuthal ? uvAsAzimuthalToEquirectangular(i.uv) : uvToEquirectangular(i.uv);
    
                    // equirectangular to mercator
                    float x = coord.x;
                    float y = log(tan(PI / 4. + coord.y / 2.));
                    // brin x,y into [0,1] range
                    x = x / PI2;
                    y = (y + PI) / PI2;                 
    
                    fixed4 col = tex2D(_MainTex, float2(x,y));
    
                    // just to make it look nicer
                    col = _Azimuthal && length(i.uv*2-1) > 1 ? 1 : col;
    
                    return col;
                }
                ENDCG
            }
        }
    }
    

    【讨论】:

    • 非常感谢@Pluto !!,完美运行。我只加了夹子以避免重复。浮动 yClamped = 钳位(coord.y,-1.4835298641951802,1.4835298641951802);浮动 y = log(tan(PI / 4. + yClamped / 2.));
    • @Pluto 我一直在尝试理解您的代码,而您的 //uv to equirectangular 部分似乎没有进行任何转换,它只是将范围从 0-1 移动到弧度,这让我想知道,如何此代码是否将墨卡托图像拉伸为等距矩形?我已经证实它确实确实如此,我只是在努力理解“魔法”数学在哪里。
    • 我的假设是图像在球体周围的自然环绕充当一种墨卡托到等角柱形转换(因为展开的球体纹理是 2 倍宽,1 倍高,并且在包裹图像时)在两极看起来蜷缩在一起)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-10-03
    • 1970-01-01
    • 1970-01-01
    • 2011-03-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多