2013-03-06 3 views
25

J'ai tracé un trapèze texturé, mais le résultat n'apparaît pas comme prévu. Au lieu d'apparaître comme un seul quadrilatère non rompu, une discontinuité se produit au niveau de la diagonale où se rencontrent ses deux triangles.Perspective texturation correcte du trapèze dans OpenGL ES 2.0

Cette illustration montre la question:
Illustration demonstrating the source texture (a checkered pattern), the expected result, and the actual (incorrect) result
(Note:. La dernière image ne vise pas à être une représentation fidèle à 100%, mais il devrait obtenir le point à travers)

Le trapézoïdal est en cours d'élaboration en utilisant GL_TRIANGLE_STRIP dans OpenGL ES 2.0 (sur un iPhone). Il est dessiné complètement en face de l'écran et n'est pas incliné (c'est-à-dire que ce n'est pas une esquisse 3D que vous voyez!)

Je comprends que j'ai besoin d'effectuer une "correction de perspective", vraisemblablement dans mon vertex et/ou fragment shaders, mais je ne sais pas comment faire cela. Mon code inclut des maths simples de matrices de Model/View/Projection, mais rien de cela n'influence actuellement mes valeurs de coordonnées de texture. Mise à jour: La déclaration précédente est incorrecte, selon le commentaire de l'utilisateur.

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

La correction PERSPECTIVE TRUC est pas pris en charge parce que OpenGL ES 2.0 exige que tous les attributs soient perspectivement interpolé.

Comment dessiner la texture correctement?


Edit: Ajout de code 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); 

} 
+0

La forme a-t-elle pris la forme d'un quad qui a été transformé et projeté, ou s'agit-il d'une forme 2D arbitraire? La solution la plus simple serait de rendre un quadrilatère incliné en 3D ... – JasonD

+0

Vous devriez vérifier UV pour le deuxième triangle.Semble comme le décalage de texture –

+1

Montrez-nous votre code. Regarde un peu _backshortened _... –

Répondre

17

(je prends un peu d'un botté de dégagement ici, parce que votre image ne pas montrer exactement ce que je m'attendrais à texturer un trapèze oid, donc 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 s'interpoleront pas (par défaut) correctement sur un trapèze. Quand la forme est triangulée pour le dessin, une des diagonales sera choisie comme un bord, et tandis que ce bord est droit au milieu de la texture, ce n'est pas au milieu du trapèze (image la forme divisée le long d'une diagonale - les deux triangles sont très différents).

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

La figure suivante montre comment fournir des coordonnées de texture pour un trapèze à l'aide de fonctions GL obsolètes (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 l'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 n'utilisons qu'une texture 2D. La quatrième coordonnée divisera les deux autres après l'interpolation, 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 ce que les deux interprétations ressemblent:

enter image description here

Si votre trapézoïdal est en fait le résultat de la transformation d'un quad, il pourrait être plus facile/mieux dessiner juste que quad en utilisant GL, plutôt que de transformer il dans le logiciel et l'alimentation des formes 2D à GL ...

+0

Malheureusement, GL_QUAD n'est pas disponible dans OpenGL ES 2.0. Cette approche peut-elle être adaptée pour fonctionner avec GL_TRIANGLE_STRIP et ES 2.0? Plus précisément, j'ai juste essayé de mettre en œuvre ce qui précède et je ne suis pas sûr de savoir comment utiliser les deux coordonnées de texture supplémentaires dans mon shader. – Neema

+0

Quad est une pertinence, il pourrait être deux triangles. Parmi les deux autres coordonnées de texture, l'une est inutilisée, l'autre doit être utilisée pour diviser les deux premières, comme avec une transformation de perspective. – JasonD

+1

J'essaie d'appliquer votre exemple ci-dessus à mon code de test.À ce stade, j'ai découvert que je peux utiliser 'texture2DProj()' dans mon fragment shader au lieu de diviser par texCoord.w. Je pense que c'est la solution, et maintenant je dois juste le faire fonctionner. – Neema

0

Ce que vous essayez ici est la texture asymétrique. Un fragment shader de l'échantillon est la suivante:

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

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

2 choses qui devraient regarder différentes sont:

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

Sur la base de la longueur des petites et grand côté de votre Trapèze attribuerez texture coordonnées. URL suivante pourrait aider: http://www.xyzw.us/~cass/qcoord/

0

La réponse acceptée donne la solution et l'explication correcte, 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, en le shader, comme par @ réponse de maverick9888, vous pouvez utiliser texture2Dproj que pour iOS/OpenGLES2 il supporte encore qu'une entrée vec3 ...

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

Je ne l'ai pas vraiment benchmarkée il p à la rigueur mais pour mon cas très simple (une texture 1d vraiment) la version de division semble un peu plus accrocheuse.

+0

Je m'attendrais à ce que la version de division soit plus lente que la version 'texture2DProj', car elle provoque la lecture d'une texture dépendante. – Tenfour04

+0

Qu'entendez-vous par "lecture de texture dépendante"? –

+1

http://stackoverflow.com/a/27028431/506796 Sur les GPU mobiles, beaucoup d'optimisation est perdue si un appel à lire à partir d'une texture utilise autre chose qu'un vecteur directement passé (ou des composants pq de vec 4). – Tenfour04