web-dev-qa-db-fra.com

Perspective correcte texturation du trapèze dans OpenGL ES 2.0

J'ai dessiné un trapèze texturé, mais le résultat n'apparaît pas comme prévu.

Au lieu d'apparaître sous la forme d'un quadrilatère unique et non fragmenté, une discontinuité se produit au niveau de la diagonale où se rencontrent ses deux triangles.

Cette illustration montre le problème:
Illustration demonstrating the source texture (a checkered pattern), the expected result, and the actual (incorrect) result
(Remarque: la dernière image ne vise pas à être une représentation fidèle à 100%, mais elle devrait faire passer le message.)

Le trapèze est dessiné à l'aide de GL_TRIANGLE_STRIP dans OpenGL ES 2.0 (sur un iPhone). Il est dessiné complètement vers l’écran et n’est pas incliné (c’est-à-dire qu’il ne s’agit pas d’une esquisse 3D!)

J'ai fini par comprendre que je devais effectuer une "correction de perspective", probablement dans mon vertex et/ou mes shaders de fragments, mais je ne sais pas comment faire.

Mon code inclut des calculs mathématiques matriciels simples Modèle/Vue/Projection, mais aucun d’entre eux n’influence actuellement les valeurs de mes coordonnées de texture. Mise à jour: L'instruction précédente est incorrecte, selon les commentaires de l'utilisateur.

De plus, j'ai trouvé cette friandise dans la spécification ES 2.0, mais je ne comprends pas ce que cela signifie:

Le CONSEIL DE CORRECTION DE PERSPECTIVE n'est pas pris en charge car OpenGL ES 2.0 requiert que tous les attributs soient interpolés en perspective.

Comment puis-je dessiner la texture correctement?


Edit: Code ajouté ci-dessous:

// 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);

}
25
Neema

(Je suis un peu punt ici, parce que votre photo ne montre pas exactement ce que j'attendrais de texturer un trapèze, alors peut-être que quelque chose d'autre se passe dans votre cas - mais le problème général est bien connu)

Les textures ne seront pas (par défaut) interpolées correctement sur un trapèze. Lorsque la forme est triangulée pour le dessin, l’une des diagonales est choisie comme contour et, bien que ce contour soit droit au milieu de la texture, il n’est pas centré au centre du trapézoïde (imaginez la forme divisée en diagonale - les deux triangles ne sont vraiment pas égaux).

Pour que cela fonctionne, vous devez fournir plus qu'une coordonnée de texture 2D: vous devez fournir une coordonnée de texture 3D (ou plutôt projective) et effectuer la division de la perspective dans le shader de fragment, la post-interpolation (ou utiliser une recherche de texture). fonction qui fera la même chose).

Ce qui suit montre comment fournir les coordonnées de texture d’un trapèze à l’aide des fonctions old-school GL (qui sont un peu plus faciles à lire à des fins de démonstration). Les lignes commentées sont les coordonnées de texture 2d, que j'ai remplacées par des coordonnées projectives pour obtenir une interpolation correcte.

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();

La troisième coordonnée est inutilisée ici car nous utilisons simplement une texture 2D. La quatrième coordonnée divisera les deux autres après interpolation, en fournissant la projection. Évidemment, si vous le divisez au niveau des sommets, vous verrez que vous obtenez les coordonnées de texture d'origine.

Voici à quoi ressemblent les deux rendus:

enter image description here

Si votre trapèze est en fait le résultat de la transformation d'un quad, il serait peut-être plus facile/mieux de dessiner ce quad en utilisant GL, plutôt que de le transformer au moyen d'un logiciel et d'alimenter des formes 2D en GL ...

17
JasonD

La réponse acceptée donne la solution et l'explication correctes, mais pour ceux qui recherchent un peu plus d'aide sur le pipeline OpenGL (ES) 2.0 ...

const GLfloat L = 2.0;
const GLfloat Z = -2.0;
const GLfloat W0 = 0.01;
const GLfloat W1 = 0.10;

/** Trapezoid shape as two triangles. */
static const GLKVector3 VERTEX_DATA[] = {
    {{-W0,    0,  Z}},
    {{+W0,    0,  Z}},
    {{-W1,    L,  Z}},

    {{+W0,    0,  Z}},
    {{+W1,    L,  Z}},
    {{-W1,    L,  Z}},
};

/** Add a 3rd coord to your texture data.  This is the perspective divisor needed in frag shader */
static const GLKVector3 TEXTURE_DATA[] = {
    {{0, 0, 0}},
    {{W0, 0, W0}},
    {{0, W1, W1}},

    {{W0, 0, W0}},
    {{W1, W1, W1}},
    {{0, W1, W1}},
};

////////////////////////////////////////////////////////////////////////////////////

// frag.glsl

varying vec3 v_texPos;
uniform sampler2D u_texture;

void main(void) 
{
    // Divide the 2D texture coords by the third projection divisor
    gl_FragColor = texture2D(u_texture, v_texPos.st / v_texPos.p);
}

Alternativement, dans le shader, selon la réponse de @ maverick9888, vous pouvez utiliser texture2Dproj bien que pour iOS/OpenGLES2, il ne supporte toujours qu'une entrée vec3 ...

void main(void) 
{
    gl_FragColor = texture2DProj(u_texture, v_texPos);
}

Je ne l'ai pas vraiment comparée correctement, mais pour mon cas très simple (une texture 1d vraiment), la version de la division semble un peu plus vive.

0
Hari Karam Singh

Ce que vous essayez ici est une texture asymétrique. Un exemple de fragment fragment est le suivant:

precision mediump float;
varying vec4 vtexCoords;
uniform sampler2D sampler;

void main()
{
   gl_FragColor = texture2DProj(sampler,vtexCoords);
}

2 choses qui devraient être différentes sont:

1) Nous utilisons varying vec4 vtexCoords;. Les coordonnées de texture sont en 4 dimensions. 2) texture2DProj() est utilisé à la place de texture2D()

En fonction de la longueur des petits et des grands côtés de votre trapèze, vous allez attribuer des coordonnées de texture. Suivre l’URL pourrait aider: http://www.xyzw.us/~cass/qcoord/

0
maverick9888