|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | ||
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Bonjour,
Je n'arrive pas à comprendre pourquoi les deux code sources suivants m'affichent un résultat différent: Fonctionne: Code :
gl_Position = mProjection * mView * mModel * vec4(vertexPosition, 1.0) Code :
Merci d'avance. |
||
|
|
00
|
|
|
#2 | |
|
Membre habitué
![]() Étudiant Inscription : juin 2009 Messages : 155 ![]() |
Bonjour
Citation:
projection non orthogonal (perspective): ![]() projection orthogonal : ![]() Donc la nécessité de diviser par gl_Position.w depend si tu es en GL_MODELVIEW ou en GL_PROJECTION, si tu est en GL_MODELVIEW, opengl fera automatiquement la division de gl_Position par sa composante w. donc en GL_MODELVIEW, le shader qui convient est celui ci : Code :
gl_Position = mProjection * mView * mModel * vec4(vertexPosition, 1.0); Personnellement, pour un rendu en perspective (donc en projection non orthogonal), je me met en en GL_MODELVIEW, et je laisse OpenGL se débrouiller comme il sais bien le faire avec la division par w ainsi que ses corrections de perspective. Tu n'as pas besoin de le codé toi même dans ton vertex (ainsi que dans le geometry ou tessellation évaluation) shader. Mais si tu insiste pour le faire quand même, passe en GL_PROJECTION, et donc OpenGL affichera tout bêtement ton triangles au coordonné gl_Position.xy de ton viewport, et gl_Position.z dans le depth buffet. D'un point de vu générale, lors d'une multiplication matricielle interprété comme un changement de repère, w = 1.0 pour effectivement avoir la translation en compte. Mais tu peut aussi avoir w = 0.0 pour réorienté les normals, car la translation n'est plus prise en compte. ![]() Code :
NormalOut = normalize((mModel * vec4(NormalIn, 0.0)).xyz) ; Note que si NormalIn est déjà normé, et que mModel est une matrice orthonormé, alors NormalOut sera aussi normé, donc normalize n'est plus nécessaire, ainsi tu gagnes en performance. Libre a toi d'avoir d'autre interprétations du produit matricielle bien sur (barycentres pour la tessellation etc ...) :cool: J'espère avoir été suffisamment clairs. |
|
|
00
|
|
|
#3 | ||
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Merci pour cette superbe réponse mais j'ai toujours du mal à comprendre
![]() 1) Je travail avec OpenGL 3.3 et donc il n'y a plus de GL_MODELVIEW / GL_PROJECTION à ma connaissance ! Je pensais qu'un OpenGL <3.3, le mode GL_PROJECTION permetait de charger une matrice de projection (orthogonal ou non) une fois pour toute (en général) et le mode GL_MODELVIEW permet de charger une matrice model et view qui doit être updaté à chaque fois qu'un object ou la caméra bouge. 2) Citation:
3) Citation:
|
||
|
|
00
|
|
|
#4 | ||
|
Membre habitué
![]() Étudiant Inscription : juin 2009 Messages : 155 ![]() |
A oui avec OpenGL 3.3, çà change. Il n'y a, en effet, plus ce contrôle de GL_MODELVIEW et GL_PROJECTION. Alors une partie ma réponse précédente servira (peut être) aux les lecteurs qui programment sur des versions antérieur.
Je me disais bien que c'était très hold school par rapport à ta question. Quelque soit ton utilisation, les coordonnées de gl_Position.xyz seront automatiquement divisé par gl_Position.w C'est très clairement dit paragraphe 2.13 Coordinate Transformations =>Documentation OpenGL 3.3 Citation:
Citation:
Or tu ne peut pas faire un genre de "discard" toi même dans le vertex shader pour çà. Donc si tu dev toi même la division par w, tu dois t'assuré qu'aucun vertex ne soit derrière ton plan de projection. Sinon c'est tout simplement une erreur de conception. Donc si tu veux quelque chose de fiable, contente toi de spécifié à OpenGL w, et il va faire le boulo à ta place.
|
||
|
00
|
|
|
#5 | |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
Citation:
x' = cos(a).x + sin(a).y + x0 y' = -sin(a).x + cos(a).y + y0 z' = z + z0 w = w Tout ceci s'écrit très bien avec des matrices puisque celles-ci représentent des transformations du type "x' = ax + by + cz + d". Maintenant, si l'on veut écrire une matrice de perspective, ça se complique. Les équations pour une perspective simple sont les suivantes: x' = x / z et y' = y / z. Là, nous sommes coincés, les matrices ne permettent pas de faire ce genre de transformations. Donc, on utilise une astuce. On écrit à la place: x' = x y' = y z' = z w' = z Et OpenGL se charge de diviser ensuite par "w". On parle pour les coordonnées non-divisées de "coordonnées homogènes". C'est ce que tu dois fournir à gl_position. Moralité de l'histoire : avec une matrice de projection orthogonale, w vaudrait toujours "1" et les deux codes produiraient le même résultat. Avec une perspective, en revanche, "w" sera différent de 1 et il est normal que les deux codes produisent un résultat différent. PS : translation pas translatation. |
|
|
|
00
|
|
|
#6 | |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Merci pour vos réponses et désolé pour ma réponse tardive.
Citation:
Pour la coordonnée Z: -1 = near plane & 1 = far plane. J'ai bon essayé de multiplier n'importe quel point non visible dans la scène par ma matrice de projection et de diviser par W, je n'arrive jamais à obtenir une coordonée Z entre -1 et 1. Je me trompe ? Donc il me semble que le rôle d'OpenGL est de simplement diviser les gl_Position par W et d'éliminer ceux qui ne se trouvent pas entre -1 et 1, non ? Ou est-ce que je me trompe ? Merci d'avance. |
|
|
|
00
|
|
|
#7 |
![]() ![]() ![]() Alexandre LaurentÉtudiant Inscription : mai 2008 Messages : 6 560 ![]() |
OpenGL enlève les coordonnées avant -1 et après 1, car elles sont simplement, hors écran
__________________
Vous souhaitez participer à la section Jeux ? Contactez-moi ![]() La rubrique a aussi un blog ! Ma page sur DVP Mon Portfolio Qui connaît l'erreur, connaît la solution. |
|
|
00
|
|
|
#8 |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Oui, ça j'avais bien compris mais ce que je ne comprends pas c'est ce que fait OpenGL à part diviser par W et éliminer les points qui ne sont pas entre -1 et 1 ?
Qu'est-ce que OpenGL doit faire de plus? Qu'attend t-on par "culé"? Je ne trouve aucun cas concrès où c'est nécessaire de faire autre chose que de diviser par W et éliminer les points non visibles. |
|
|
00
|
|
|
#9 |
![]() ![]() ![]() Alexandre LaurentÉtudiant Inscription : mai 2008 Messages : 6 560 ![]() |
Le depth culling ( test de profondeur). On utilise un buffer de profondeur qui fait en sorte de ne garder que la profondeur la plus proche de la caméra. Du coup les pixels derrière d'autres ne sont pas affichés.
Le backface culling ( test de normal / sens de dessin d'un triangle). Qui fait en sorte que selon l'orientation, le triangle est dessiner ou non. Peut être d'autres trucs, mais je ne me rappelle plus, sur le coup.
__________________
Vous souhaitez participer à la section Jeux ? Contactez-moi ![]() La rubrique a aussi un blog ! Ma page sur DVP Mon Portfolio Qui connaît l'erreur, connaît la solution. |
|
|
00
|
|
|
#10 | |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Merci pour ta réponse.
Citation:
Le "culling" comme expliqué par "LastSpear" ? Mais qu'est-ce que ça fait exactement ? |
|
|
|
00
|
|
|
#11 |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
Le "culling" se traduirait par "cueillette sélective".
* Backface culling: on élimine les faces arrière selon la disposition des sommets à l'écran (sens des aiguilles d'une montre = on garde ; sens trigonométrique = on jette - ou l'inverse). Les faces éliminées ne génèrent pas de fragments. * Depth-culling : on élimine les fragments selon le z-buffer, les fragments éliminés ne sont pas présentés aux framebuffers (pour écriture ou alpha blending). * Alpha testing : les fragments ayant un alpha non-valide ne sont pas emmenés jusqu'aux framebuffers (pour écriture ou alpha blending). Mais peu importe, ton problème est simple : puisque OpenGL divise lui aussi par "w", ton premier code revient à diviser par w et ton second par w². Si w est inférieur à 1 tu as donc des sommets qui vont se retrouver en-dehors du frustum. Je l'ai expliqué plus haut : dans le cas d'une perspective, w est différent de "1". |
|
|
00
|
|
|
#12 |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Merci pour ta réponse mais je ne suis pas d'accord à propos de la division par w².
En effet, le code suivant va remettre "w" à 1.0: J'ai même essayé de forcer de remettre "w" à 1.0: aucun changement. Donc à la seconde division par OpenGL, il va diviser par 1.0 (donc ne rien faire). |
|
|
00
|
|
|
#13 |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
@Zenux
Ah ! Oui, en effet, tu as raison. A moins que w ne soit égal à zéro, serait-ce possible ? |
|
|
00
|
|
|
#14 |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Je n'avais pas pensez au cas du w=0. Ca peut très bien arriver si on a un vecteur avec Z=0. Dans ce cas, avec une matrice de projection classique (gluPerpective), W vaudra 0 après multiplication par ce vecteur.
Je suppose qu'OpenGL supprime tout simplement ce point quand c'est le cas. En effet, un point qui se trouve en Z=0 est hors du frustum vu que le near plane doit être plus grand que 0 d'après la doc. Malheureusement, je n'ai pas de vecteur avec un Z=0 qui pourrait expliquer mon problème d'origine. Qu'est-ce que que OpenGL pourrait bien faire de plus que le depth culling, alpha culling, backface culling, w=0 culling et division par w ? |
|
|
00
|
|
|
#15 |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
On pourrait toutefois se retrouver avec Z=0 suite aux transformations matricielles mais ça semblerait assez improbable. Un cas avec w=0 révélerait plus vraisemblablement une erreur dans les matrices spécifiées.
Après, oui, j'imagine que OpenGl réagit dans ce cas en éliminant le sommet en question et les faces associées. Concernant les différentes opérations, je ne vois malheureusement rien d'autre que ce que tu as énoncé. Par contre, petite remarque terminologique, on parle plutôt de "z-testing" et "alpha-testing". Le terme "culling" est généralement réservé au backface culling et ce dernier est alors abrégé en un simple "culling". Par contre, je peux te suggérer une méthode : * Simplifie le programme : conserve ton shader mais teste-le avec des matrices plus simples et des primitives standard. Puis rebrousse chemin progressivement vers ton code d'arrivée. * Fais un test avec le GPU d'une autre marque. On rencontre parfois des bogues. * Désactive tous ces tests qui te chiffonnent : mets en commentaire les glEnable activant le culling, le z-testing et l'alpha-testing. Par défaut ces tests sont désactivés donc si tu n'as pas ces appels à glEnable, aucun souci. |
|
|
00
|
|
|
#16 |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
J'ai pu reproduire le problème en dessinant une ligne:
Point1: x=0.249636 y=-0.280748 z=-0.654037 1Point2: x=-0.206164 y=-0.363772 z=0.0258028 w=1Avant la multiplication par la matrice de projection, ma ligne se trouve en dessous du millieu de mon écran (Y < 0.0). Après multiplication par la matrice de projection, j'ai mon deuxième point qui se trouve totalement en dehors de mon écran (Y=24.4188). Je me retrouve donc avec une ligne qui par en dehors de mon écran vers le haut alors qu'a la base, elle était en dessous du millieu de mon écran. J'ai donc bien un problème d'affichage si c'est moi qui fait la division par W et pas OpenGL. Malheureusement, ça ne me dit toujours pas ce que fait OpenGL pour éviter ce genre de problème. |
|
|
00
|
|
|
#17 |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
As-tu essayé de remplacer la division par "w" par l'opération équivalente : "positiont = vec4(positiont.x / positiont.w, positiont.y / positiont.w, positiont.z / positiont.w, 1.0);" ?
EDIT : Je tiens quand même à préciser que les résultats que tu obtiens à la main sont corrects. Visiblement ta caméra est en (0;0) et ton second point serait donc juste derrière la caméra, d'où des x et y en-dehors de l'écran. D'ailleurs, zc/w est en-dehors de [-1 ; +1] (les coordonnées après division par w sont toutes attendues sur [-1 ; +1], ce n'est que lors de la rastérisation que vient la remise à l'échelle en fonction du viewport et des valeurs de glDepthRange). A mon avis, ce qui se passe est que lorsque tu laisses OpenGl faire la division, celui-ci détecte que le second point est derrière l'écran et optimise précocement en n'émettant les fragments que pour le segment allant du point A à l'intersection avec le near plane. Alors que si tu fais la division manuellement, OpenGL doit dessiner toute la ligne en clampant Z lors de l'enregistrement dans le depth buffer. Il serait intéressant de vérifier ça en mesurant précisément la position du second point dans le cas où tu laisses OGL se débrouiller, ou en activant le Z-buffer pour forcer la troncation, voire avec deux points situés devant la caméra. Accessoirement, j'aimerais savoir pourquoi tu cherches à faire la division toi-même. EDIT2 : Une matrice perspective ne produit normalement un "w" inférieur à 0 que pour les points situés derrière le near plane. OGL doit s'appuyer sur la valeur de w pour détecter les points à tronquer précocément. Du coup, oui, il y aurait bien un autre test, le w-testing. EDIT3 : Reformulation de plein de choses. Mais je ne comprends pas pourquoi OGL n'a pas clippé en fonction du near plane dans le cas "manuel". |
|
|
00
|
|
|
#18 |
|
Membre régulier
![]() Inscription : février 2006 Messages : 318 ![]() |
Pour moi, le signe de Y pour mon point 2 n'est pas bon.
Je viens de trouver ce que fait OpenGL: - http://www.opengl.org/discussion_boa...&Number=282497 : "Dark Photon" explique pourquoi on peut avoir un mauvais signe. - http://medialab.di.unipi.it/web/IUM/...oo/node51.html : ici on explique pourquoi faire le clipping de [-1 à 1] ne fonctionne pas dans tout les cas. Il faut faire le clipping avant de diviser par W en se servant de W (je suppose que c'est ce que OpenGL fait). Par contre, je ne sais toujours pas comment faire dans mon shader. En effet, maintenant, je sais quand un point doit être "clippé" mais je ne sais pas quoi en faire. |
|
|
00
|
|
|
#19 |
|
Expert Confirmé
![]() Inscription : septembre 2010 Messages : 1 350 ![]() |
Concernant le signe de Y, si le second point est bien derrière la caméra, tout est normal : ce qui est derrière la caméra est en miroir. Mais évidemment ce n'est pas commode pour le clipping.
Maintenant, le problème est simple : tu ne peux pas clipper dans le vertex shader, tu dois le faire dans le fragment shader puisque tu ne veux éliminer que les points en-dehors de la caméra et non tout le segment AB. Mais le clipping doit être fait dans en clip-space. La solution est de passer la valeur d'origine de w au fragment shader et de le mettre à 1 à la sortie du vertex shader. Puis tu fais le clipping et le reste dans le fragment shader. Mais j'imagine que tu vas t'exposer à d'autres surprises comme la non-correction de perspective sur les textures. |
|
|
00
|
Copyright © 2000-2012 - www.developpez.com