Search code examples
geometryglslprojection

How to do a shader to convert to azimuthal_equidistant


I have a 360 texture in Equirectangular Projection.

With what GLSL shader can I convert it into a azimuthal equidistant projection?

See also: http://earth.nullschool.net/#current/wind/isobaric/500hPa/azimuthal_equidistant=24.64,98.15,169


Solution

  • I would do it in Fragment shader.

    1. bind Equirectangular texture as 2D texture
    2. bind projection shader
    3. draw Quad covering the screen or target texture
    4. store or use the result.

    In Vertex shader I would:

    Just pass the vertex coordinates as varying to fragment shader (no point using matrices here you can directly use x,y coordinates in range <-1,+1>)

    In fragment shader I would:

    1. compute azimuth and distance of interpolated vertex from point (0,0) (simple length and atan2 call)
    2. then convert them to (u,v) coordinates of texture (just scale...)
    3. and lastly render fragment with selected texel or throw it out if out of range ...

    [edit1] just did bust a small example:

    GL draw

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    GLint id;
    glUseProgram(prog_id);
    id=glGetUniformLocation(prog_id,"txr"); glUniform1i(id,0);
    
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glMatrixMode(GL_TEXTURE);
    glLoadIdentity();
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,txrmap);
    
    glBegin(GL_QUADS);
    glColor3f(1,1,1);
    glVertex2f(-1.0,-1.0);
    glVertex2f(-1.0,+1.0);
    glVertex2f(+1.0,+1.0);
    glVertex2f(+1.0,-1.0);
    glEnd();
    glDisable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D,0);
    
    glUseProgram(0);
    glFlush();
    SwapBuffers(hdc);
    

    Vertex:

    varying vec2 pos;
    void main()
        {
        pos=gl_Vertex.xy;
        gl_Position=gl_Vertex;
        }
    

    Fragment:

    uniform sampler2D txr;
    varying vec2 pos;
    void main()
        {
        const float pi2=6.283185307179586476925286766559;
        vec4 c=vec4(0.0,0.0,0.0,1.0);
        vec2 uv;        // texture coord = scaled spherical coordinates
        float a,d;      // azimuth,distance
        d=length(pos);
        if (d<1.0)      // inside projected sphere surface
            {
            a=atan(-pos.x,pos.y);
            if (a<0.0) a+=pi2;
            if (a>pi2) a-=pi2;
            uv.x=a/pi2;
            uv.y=d;
            c=texture2D(txr,uv);
            }
        gl_FragColor=c;
        }
    

    Input texture:

    earth rectangular

    Output render:

    earth azimuthal equidistant

    [notes]

    The vertical line is caused by not using GL_CLAMP_TO_EDGE on source texture. It can be repaired by using texture coordinates range shifted by 1 pixel on booth sides or use GL_CLAMP_TO_EDGE extension if present.

    Weird atan() operands are result of rotating left by 90 degrees to match North azimuth to be UP.