Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Perspective correct texturing of trapezoid in OpenGL ES 2.0

I have drawn a textured trapezoid, however the result does not appear as I had intended.

Instead of appearing as a single unbroken quadrilateral, a discontinuity occurs at the diagonal line where its two comprising triangles meet.

This illustration demonstrates the issue:
Illustration demonstrating the source texture (a checkered pattern), the expected result, and the actual (incorrect) result
(Note: the last image is not intended to be a 100% faithful representation, but it should get the point across.)

The trapezoid is being drawn using GL_TRIANGLE_STRIP in OpenGL ES 2.0 (on an iPhone). It's being drawn completely facing the screen, and is not being tilted (i.e. that's not a 3D sketch you're seeing!)

I have come to understand that I need to perform "perspective correction," presumably in my vertex and/or fragment shaders, but I am unclear how to do this.

My code includes some simple Model/View/Projection matrix math, but none of it currently influences my texture coordinate values. Update: The previous statement is incorrect, according to comment by user infact.

Furthermore, I have found this tidbit in the ES 2.0 spec, but do not understand what it means:

The PERSPECTIVE CORRECTION HINT is not supported because OpenGL ES 2.0 requires that all attributes be perspectively interpolated.

How can I make the texture draw correctly?


Edit: Added code below:

// Vertex shader attribute vec4 position; attribute vec2 textureCoordinate;  varying vec2 texCoord;  uniform mat4 modelViewProjectionMatrix;  void main() {     gl_Position = modelViewProjectionMatrix * position;     texCoord = textureCoordinate; } 

// Fragment shader uniform sampler2D texture; varying mediump vec2 texCoord;  void main() {     gl_FragColor = texture2D(texture, texCoord); } 

// Update and Drawing code (uses GLKit helpers from iOS)  - (void)update {     float fov = GLKMathDegreesToRadians(65.0f);     float aspect = fabsf(self.view.bounds.size.width / self.view.bounds.size.height);     projectionMatrix = GLKMatrix4MakePerspective(fov, aspect, 0.1f, 50.0f);     viewMatrix = GLKMatrix4MakeTranslation(0.0f, 0.0f, -4.0f); // zoom out }  - (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {     glClearColor(0.0f, 0.0f, 0.0f, 1.0f);     glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);      glUseProgram(shaders[SHADER_DEFAULT]);      GLKMatrix4 modelMatrix = GLKMatrix4MakeScale(0.795, 0.795, 0.795); // arbitrary scale      GLKMatrix4 modelViewMatrix = GLKMatrix4Multiply(viewMatrix, modelMatrix);      GLKMatrix4 modelViewProjectionMatrix = GLKMatrix4Multiply(projectionMatrix, modelViewMatrix);     glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, GL_FALSE, modelViewProjectionMatrix.m);      glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_WALLS]);     glUniform1i(uniforms[UNIFORM_TEXTURE], 0);      glVertexAttribPointer(ATTRIB_VERTEX, 3, GL_FLOAT, GL_FALSE, 0, wall.vertexArray);     glVertexAttribPointer(ATTRIB_TEXTURE_COORDINATE, 2, GL_FLOAT, GL_FALSE, 0, wall.texCoords);     glDrawArrays(GL_TRIANGLE_STRIP, 0, wall.vertexCount);  } 
like image 757
Neema Avatar asked Mar 06 '13 08:03

Neema


1 Answers

(I'm taking a bit of a punt here, because your picture does not show exactly what I would expect from texturing a trapezoid, so perhaps something else is happening in your case - but the general problem is well known)

Textures will not (by default) interpolate correctly across a trapezoid. When the shape is triangulated for drawing, one of the diagonals will be chosen as an edge, and while that edge is straight through the middle of the texture, it is not through the middle of the trapezoid (picture the shape divided along a diagonal - the two triangles are very much not equal).

You need to provide more than a 2D texture coordinate to make this work - you need to provide a 3D (or rather, projective) texture coordinate, and perform the perspective divide in the fragment shader, post-interpolation (or else use a texture lookup function which will do the same).

The following shows how to provide texture coordinates for a trapezoid using old-school GL functions (which are a little easier to read for demonstration purposes). The commented-out lines are the 2d texture coordinates, which I have replaced with projective coordinates to get the correct interpolation.

glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0,640,0,480,1,1000); glMatrixMode(GL_MODELVIEW); glLoadIdentity();  const float trap_wide = 600; const float trap_narrow = 300; const float mid = 320;  glBegin(GL_TRIANGLE_STRIP); glColor3f(1,1,1);  //  glTexCoord4f(0,0,0,1); glTexCoord4f(0,0,0,trap_wide); glVertex3f(mid - trap_wide/2,10,-10);  //  glTexCoord4f(1,0,0,1); glTexCoord4f(trap_narrow,0,0,trap_narrow); glVertex3f(mid - trap_narrow/2,470,-10);  //  glTexCoord4f(0,1,0,1); glTexCoord4f(0,trap_wide,0,trap_wide); glVertex3f(mid + trap_wide/2,10,-10);  //  glTexCoord4f(1,1,0,1); glTexCoord4f(trap_narrow,trap_narrow,0,trap_narrow); glVertex3f(mid + trap_narrow/2,470,-10);  glEnd(); 

The third coordinate is unused here as we're just using a 2D texture. The fourth coordinate will divide the other two after interpolation, providing the projection. Obviously if you divide it through at the vertices, you'll see you get the original texture coordinates.

Here's what the two renderings look like:

enter image description here

If your trapezoid is actually the result of transforming a quad, it might be easier/better to just draw that quad using GL, rather than transforming it in software and feeding 2D shapes to GL...

like image 123
JasonD Avatar answered Sep 22 '22 16:09

JasonD