Linear/Nonlinear Texture Mapping a Distorted Quad

opengl, texture-mapping, textures

Solution

The best approach to a solution working with modern gpu-api's can be found on Nathan Reed's blog.

But you end up with a problem similar to the original problem with the perspective :( I try to solve it and will post a solution once i have it.

Edit: There is a working sample of a Quad-Texture on shadertoy.

Here is a simplified modified version I did, all honors deserved to Inigo Quilez:

float cross2d( in vec2 a, in vec2 b ) 
{
    return a.x*b.y - a.y*b.x; 
}

// given a point p and a quad defined by four points {a,b,c,d}, return the bilinear
// coordinates of p in the quad. Returns (-1,-1) if the point is outside of the quad.
vec2 invBilinear( in vec2 p, in vec2 a, in vec2 b, in vec2 c, in vec2 d )
{
    vec2 e = b-a;
    vec2 f = d-a;
    vec2 g = a-b+c-d;
    vec2 h = p-a;
    
    float k2 = cross2d( g, f );
    float k1 = cross2d( e, f ) + cross2d( h, g );
    float k0 = cross2d( h, e );

    float k2u = cross2d( e, g );
    float k1u = cross2d( e, f ) + cross2d( g, h );
    float k0u = cross2d( h, f);    

    float v1, u1, v2, u2;

    float w = k1*k1 - 4.0*k0*k2;

    w = sqrt( w );

    v1 = (-k1 - w)/(2.0*k2);    
    u1 = (-k1u - w)/(2.0*k2u);        
    bool  b1 = v1>0.0 && v1<1.0 && u1>0.0 && u1<1.0;
    if( b1 ) 
        return vec2( u1, v1 );

    v2 = (-k1 + w)/(2.0*k2);
    u2 = (-k1u + w)/(2.0*k2u);
    bool  b2 = v2>0.0 && v2<1.0 && u2>0.0 && u2<1.0;        
    if( b2 ) 
        return vec2( u2, v2 )*.5;

    return vec2(-1.0);
}

float sdSegment( in vec2 p, in vec2 a, in vec2 b )
{
    vec2 pa = p - a;
    vec2 ba = b - a;
    float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 );
    return length( pa - ba*h );
}

vec3  hash3( float n ) 
{ 
    return fract(sin(vec3(n,n+1.0,n+2.0))*43758.5453123); 
}

//added for dithering
bool even(float a) 
{ 
    return fract(a/2.0) <= 0.5; 
}

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    vec2 p = (-iResolution.xy + 2.0*fragCoord.xy)/iResolution.y;

    // background
    vec3 bg = vec3(sin(iTime+(fragCoord.y * .01)),sin(3.0*iTime+2.0+(fragCoord.y * .01)),sin(5.0*iTime+4.0+(fragCoord.y * .01)));
    vec3 col = bg;

    // move points
    vec2 a = sin( 0.11*iTime + vec2(0.1,4.0) );
    vec2 b = sin( 0.13*iTime + vec2(1.0,3.0) );
    vec2 c = cos( 0.17*iTime + vec2(2.0,2.0) );
    vec2 d = cos( 0.15*iTime + vec2(3.0,1.0) );

    // area of the quad
    vec2 uv = invBilinear( p, a, b, c, d );
    if( uv.x>-0.5 )
    {
        col = texture( iChannel0, uv ).xyz;
    }

    //mesh or screen door or dithering like in many sega saturn games
    fragColor = vec4( col, 1.0 );
}

Problem

In my previous question, it was established that, when texturing a quad, the face is broken down into triangles and the texture coordinates interpolated in an affine manner. Unfortunately, I do not know how to fix that. The provided link was useful, but it doesn't give the desired effect. The author concludes: "Note that the image looks as if it's a long rectangular quad extending into the distance. . . . It can become quite confusing . . . because of the "false depth perception" that this produces." What I would like to do is to have the texturing preserve the original scaling of the texture. For example, in the trapezoidal case, I want the vertical spacing of the texels to be the same (example created with paint program): Notice that, by virtue of the vertical spacing being identical, yet the quad's obvious distortion, straight lines in texture space are no longer straight lines in world space. Thus, I believe the required mapping to be nonlinear. The question is: is this even possible in the fixed function pipeline? I'm not even sure exactly what the "right answer" is for more general quads; I imagine that the interpolation functions could get very complicated very fast, and I realize that "preserve the original scaling" isn't exactly an algorithm. World-space triangles are no longer linear in texture space. As an aside, I do not really understand the 3rd and 4th texture coordinates; if someone could point me to a resource, that would be great.

Original source

Related problems