Shader that transforms a mercator projection to equirectangular?

早过忘川 提交于 2021-02-18 16:55:14

问题


I am trying to make a shader in Unity taking a mercator projection texture as a source and converting it to an equirectangular projection texture.

Input example:

Output example:

This example does the opposite with an equirectangular as source.

If you look at the source of the above example:

 // 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);

Can anyone help to reverse this so I'm able to go from a mercator map as a source to equirectangular (or even better, azimuthal).

Looking for a way to do 2D texture deformations going from x/y to longitude(x)/latitude(y) and back.

I appreciate your input.


回答1:


If you want to output the equirectangular projection, you need to convert from equirectangular coordinates to mercator coordinates and then sample the mercator projection at those coordinates.

This is what it would look like in a fragment shader from 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));

The same thing applies to the azimuthal projection: You can go from azimuthal coordinates -> equirectangular -> mercator and sample the image. Or you can find a formula to go directly from azimuthal -> mercator. The wiki pages have a bunch of formulas to go back and forth between projections.

Here is a full shader to play around with. Input is a mercator projection and outputs a equirectangular or azimuthal projection (choose from the dropdown menu)

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
        }
    }
}


来源:https://stackoverflow.com/questions/59907996/shader-that-transforms-a-mercator-projection-to-equirectangular

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!