libgdxglslopengl-es-2.0shaderglsles

GLSL ES - Mapping texture from rectangular to polar coordinates with repeating


I need to warp a rectangular texture to texture with polar coordinates. To spread the light on my problem, I am going to illustrate it:

I have the image: original image

and I have to deform it using shader to something like this: result

then I'm going to map it to a plane. How can I do this? Any help will be appreciated!


Solution

  • That is not particularly hard. You just need to convert your texture coordinates to polar coordinates, and use the radius for the texture's s direction, and the azimuth angle to the t direction.

    Assuming you want to texture a quad that way, and also assuming you use standard texcoords for this, so the lower left vertex will have (0,0), the upper right one (1,1) as texture coords.

    So in the fragment shader, you just need to convert the interpolated texcoords (using tc for this) to polar coordinates. SInce the center will be at (0.5, 0.5), we have to offset this first.

     vec2 x=tc - vec2(0.5,0.5);
     float radius=length(x);
     float angle=atan(x.y, x.x);
    

    Now all you need to do is to map the range back to the [0,1] texture space. The maximum radius here will be 0.5, so you simply can use 2*radius as the s coordinate, and angle will be in [-pi,pi], so you should map that to [0,1] for the t coordinate.

    UPDATE1

    There are a few details I left out so far. From your image it is clear that you do not want the inner circle to be mapped to the texture. But this can easily be incorparated. I just assume two radii here: r_inner, which is the radius of the inner circle, and r_outer, which is the radius onto which you want to map the outer part. Let me sketch out a simple fragment shader for that:

    #version ...
    precision ...
    
    varying vec2 tc; // texcoords from vertex shader
    uniform sampler2D tex;
    
    #define PI 3.14159265358979323844
    
    void main ()
    {
        const float r_inner=0.25; 
        const float t_outer=0.5; 
    
        vec2 x = v_tex - vec2(0.5);
        float radius = length(x);
        float angle = atan(x.y, x.x);
    
        vec2 tc_polar; // the new polar texcoords
        // map radius so that for r=r_inner -> 0 and r=r_outer -> 1
        tc_polar.s = ( radius - r_inner) / (r_outer - r_inner);
    
        // map angle from [-PI,PI] to [0,1]
        tc_polar.t = angle * 0.5 / PI + 0.5;
    
        // texture mapping
        gl_FragColor = texture2D(tex, tc_polar);
    }
    

    Now there is still one detail missing. The mapping generated above generates texcoords which are outside of the [0,1] range for any position where you have black in your image. But the texture sampling will not automatically give black here. The easiest solution would be to just use the GL_CLAMP_TO_BORDER mode for GL_TEXTURE_WRAP_S (the default border color will be (0,0,0,0) so you might not need to specify it or you can set GL_TEXTURE_BORDER_COLOR explicitly to (0,0,0,1) if you work with alpha blending and don't want any transparency that way). That way, you will get the black color for free. Other options would be using GL_CLAMP_TO_EDGE and adding a black pixel column both the left and right end of the texture. Another way would be to add a brach to the shader and check for tc_polar.s being below 0 or above 1, but I wouldn't recommend that for this use case.