correct glsl affine texture mapping

后端 未结 6 1336
一整个雨季
一整个雨季 2020-12-19 10:43

i\'m trying to code correct 2D affine texture mapping in GLSL.

Explanation:

\"\"

...NONE of thi

相关标签:
6条回答
  • 2020-12-19 11:20

    In case anyone's still interested, here's a C# implementation that takes a quad defined by the clockwise screen verts (x0,y0) (x1,y1) ... (x3,y3), an arbitrary pixel at (x,y) and calculates the u and v of that pixel. It was originally written to CPU-render an arbitrary quad to a texture, but it's easy enough to split the algorithm across CPU, Vertex and Pixel shaders; I've commented accordingly in the code.

                float Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, A, B, C;
    
                //These are all uniforms for a given quad. Calculate on CPU.
                Ax = (x3 - x0) - (x2 - x1);
                Bx = (x0 - x1);
                Cx = (x2 - x1);
                Dx = x1;
    
                Ay = (y3 - y0) - (y2 - y1);
                By = (y0 - y1);
                Cy = (y2 - y1);
                Dy = y1;
    
                float ByCx_plus_AyDx_minus_BxCy_minus_AxDy = (By * Cx) + (Ay * Dx) - (Bx * Cy) - (Ax * Dy);
                float ByDx_minus_BxDy = (By * Dx) - (Bx * Dy);
    
                A = (Ay*Cx)-(Ax*Cy);
    
                //These must be calculated per-vertex, and passed through as interpolated values to the pixel-shader 
                B = (Ax * y) + ByCx_plus_AyDx_minus_BxCy_minus_AxDy - (Ay * x);
                C = (Bx * y) + ByDx_minus_BxDy - (By * x);
    
                //These must be calculated per-pixel using the interpolated B, C and x from the vertex shader along with some of the other uniforms.
                u = ((-B) - Mathf.Sqrt((B*B-(4.0f*A*C))))/(A*2.0f);
                v = (x - (u * Cx) - Dx)/((u*Ax)+Bx);
    

    0 讨论(0)
  • 2020-12-19 11:24

    This works well as long as you have a trapezoid, and its parallel edges are aligned with one of the local axes. I recommend playing around with my Unity package.

    GLSL:

    varying vec2 shiftedPosition, width_height;
    
    #ifdef VERTEX
    void main() {
        gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
        shiftedPosition = gl_MultiTexCoord0.xy; // left and bottom edges zeroed.
        width_height = gl_MultiTexCoord1.xy;
    }
    #endif
    
    #ifdef FRAGMENT
    uniform sampler2D _MainTex;
    void main() {
        gl_FragColor = texture2D(_MainTex, shiftedPosition / width_height);
    }
    #endif
    

    C#:

    // Zero out the left and bottom edges, 
    // leaving a right trapezoid with two sides on the axes and a vertex at the origin.
    var shiftedPositions = new Vector2[] {
        Vector2.zero,
        new Vector2(0, vertices[1].y - vertices[0].y),
        new Vector2(vertices[2].x - vertices[1].x, vertices[2].y - vertices[3].y),
        new Vector2(vertices[3].x - vertices[0].x, 0)
    };
    mesh.uv = shiftedPositions;
    
    var widths_heights = new Vector2[4];
    widths_heights[0].x = widths_heights[3].x = shiftedPositions[3].x;
    widths_heights[1].x = widths_heights[2].x = shiftedPositions[2].x;
    widths_heights[0].y = widths_heights[1].y = shiftedPositions[1].y;
    widths_heights[2].y = widths_heights[3].y = shiftedPositions[2].y;
    mesh.uv2 = widths_heights;
    
    0 讨论(0)
  • 2020-12-19 11:26

    thanks for answers, but after experimenting i found a solution.

    two triangles on the left has uv (strq) according this and two triangles on the right are modifed version of this perspective correction.

    Numbers and shader:

    tri1 = [Vec2(-0.5, -1), Vec2(0.5, -1), Vec2(1, 1)]
    tri2 = [Vec2(-0.5, -1), Vec2(1, 1), Vec2(-1, 1)]
    
    d1 = length of top edge = 2
    d2 = length of bottom edge = 1
    
    tri1_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(d2 / d1, 0, 0, d2 / d1), Vec4(1, 1, 0, 1)]
    tri2_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(1, 1, 0, 1), Vec4(0, 1, 0, 1)]
    

    only right triangles are rendered using this glsl shader (on left is fixed pipeline):

    void main()
    {
        gl_FragColor = texture2D(colormap, vec2(gl_TexCoord[0].x / glTexCoord[0].w, gl_TexCoord[0].y);
    }
    

    so.. only U is perspective and V is linear.

    0 讨论(0)
  • 2020-12-19 11:30

    Tessellation solves this problem. Subdividing quad vertex adds hints to interpolate pixels.

    Check out this link. https://www.youtube.com/watch?v=8TleepxIORU&feature=youtu.be

    0 讨论(0)
  • 2020-12-19 11:35

    I recently managed to come up with a generic solution to this problem for any type of quadrilateral. The calculations and GLSL maybe of help. There's a working demo in java (that runs on Android), but is compact and readable and should be easily portable to unity or iOS: http://www.bitlush.com/posts/arbitrary-quadrilaterals-in-opengl-es-2-0

    0 讨论(0)
  • 2020-12-19 11:42

    I had similar question ( https://gamedev.stackexchange.com/questions/174857/mapping-a-texture-to-a-2d-quadrilateral/174871 ) , and at gamedev they suggested using imaginary Z coord, which I calculate using the following C code, which appears to be working in general case (not just trapezoids):

    //usual euclidean distance
    float distance(int ax, int ay, int bx, int by) {
      int x = ax-bx;
      int y = ay-by;
      return sqrtf((float)(x*x + y*y));
    }
    
    void gfx_quad(gfx_t *dst //destination texture, we are rendering into
                 ,gfx_t *src //source texture
                 ,int *quad  // quadrilateral vertices
                 )
    {
      int *v = quad; //quad vertices
      float z = 20.0;
      float top = distance(v[0],v[1],v[2],v[3]); //top
      float bot = distance(v[4],v[5],v[6],v[7]); //bottom
      float lft = distance(v[0],v[1],v[4],v[5]); //left
      float rgt = distance(v[2],v[3],v[6],v[7]); //right
    
      // By default all vertices lie on the screen plane
      float az = 1.0;
      float bz = 1.0;
      float cz = 1.0;
      float dz = 1.0;
    
      // Move Z from screen, if based on distance ratios.
      if (top<bot) {
        az *= top/bot;
        bz *= top/bot;
      } else {
        cz *= bot/top;
        dz *= bot/top;
      }
    
      if (lft<rgt) {
        az *= lft/rgt;
        cz *= lft/rgt;
      } else {
        bz *= rgt/lft;
        dz *= rgt/lft;
      }
    
      // draw our quad as two textured triangles
      gfx_textured(dst, src
                  , v[0],v[1],az, v[2],v[3],bz, v[4],v[5],cz
                  , 0.0,0.0,      1.0,0.0,      0.0,1.0);
      gfx_textured(dst, src
                  , v[2],v[3],bz, v[4],v[5],cz, v[6],v[7],dz
                  , 1.0,0.0,      0.0,1.0,      1.0,1.0);
    }
    

    I'm doing it in software to scale and rotate 2d sprites, and for OpenGL 3d app you will need to do it in pixel/fragment shader, unless you will be able to map these imaginary az,bz,cz,dz into your actual 3d space and use the usual pipeline. DMGregory gave exact code for OpenGL shaders: https://gamedev.stackexchange.com/questions/148082/how-can-i-fix-zig-zagging-uv-mapping-artifacts-on-a-generated-mesh-that-tapers

    0 讨论(0)
提交回复
热议问题