IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

OpenGL Discussion :

[GLSL] Calcul de la position écran dans un vertex shader


Sujet :

OpenGL

  1. #1
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut [GLSL] Calcul de la position écran dans un vertex shader
    Salut tout le monde,

    Je suis en train de bosser sur une simulation de fluide où j'ai besoin de faire un texture lookup dans un vertex shader. Pour cela j'ai besoin de calculer la position écran du point que je dessine à partir de ses coordonnées 3D. Voilà ce que je fais (modelview est à l'identité donc je ne m'en sers pas) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    vec4 Position = gl_ProjectionMatrix * vec4(VertexPosition, 1.0);
    gl_Position = Position;
    Position.xy = (Position.xy / Position.w) * 0.5 + 0.5;
    ...
    vec4 color = texture(mySampler, Position.xy);
    Ce qui marche parfaitement sur une carte ATI (HD 5870) mais ne fonctionne pas sur une NVIDIA (GTX 580), les drivers ont bien été mis à jours.

    Description de l'erreur : Les shaders compilent, mais apparemment il y a une erreur de précision sur mon calcul avec la carte nvidia. En effet, pour pouvoir débugger je coloris mes points en vert quand la position calculé correspond à la position réel (celle rasterizé par la carte graphique) et sinon en rouge. Pour avoir cette info j'effectue une premiere passe ou je dessine mes points dans une texture et dans ma 2° passe ou j'essaye de calculer à la main la position écran de mon point je fais un lookup sur la texture de la passe précédente et je vérifie qu'elle contient bien mon point.
    Sur une carte ATI dans tous les cas les points sont verts.
    Sur une carte NVIDIA ils sont généralement verts, mais quelques fois une ligne ou une colonne devient rouge (je dessine une grille de points).
    Je m'en rend compte en positionnant les points à un certain endroit ou en les déplaçant très lentement.

    Je pense que le problème survient quand les points approchent la limite entre deux pixels et qu'une erreur de précision survient dans mon calcul du point par rapport à celui qui est fait par la carte graphique.

    Sur un autre forum on m'a orienté vers le mot clé "precise" qui permet d'empêcher certaines optimisations sur les calculs flottant surtout sur les cartes nvidia mais cela n'a pas fonctionné.

    Quelqu'un a-t-il une idée à mon insoluble problème

    Merci

  2. #2
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Salut,

    Essaye un truc du genre:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    // floor pour avoir le pixel correspondant, +0.5 pour être au milieu du pixel, et /textureSize pour retourner en [0, 1].
    vec2 positionTex = (floor(Position.xy*textureSize) + 0.5) / textureSize;
    vec4 color = texture(mySampler, positionTex);
    Il faut absolument que ta coord de texture tape au milieu d'un pixel si tu souhaite avoir sa valeur. Si tu tape au bord d'un pixel le résultat n'est pas le même sur NVidia et ATI (en gros, ATI considère le pixel de gauche, NVidia le pixel de droite).

  3. #3
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Salut et merci pour ta réponse.
    Malheureusement j'ai déjà testé et cela n'a pas fonctionné
    Quand j'évalue la distance entre mon point 3D calculé erroné (avec ta formule aussi) et le centre du pixel, je trouve une valeur = 0.5 + epsilon (128 / 255 quand je le visualise sur la texture) alors que pour qu'il soit valide la distance devrait être inférieur à 0.5.
    On dirait que c'est l'opération
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    vec4 Position = gl_ProjectionMatrix * vec4(VertexPosition, 1.0);
    qui me donne quelque chose d'imprécis mais ça ne me parait pas possible vu que gl_Position est aussi l'input du rasterizer :/
    En fait pour te donner une image du problème, ton calcul avec le floor où floor(X) == Y donne dans mon cas où le point est à une certaine coordonnée qui fait que cela ne fonctionne pas : floor(X) == Y + 1 (car l'epsilon du à l'imprécision doit faire un truc du genre floor(0.99 + e) == 1.0 alors que cela devrait faire 0.0)

    Need heeeelllppp

  4. #4
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Hmmm, essaye de remplacer
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Position.xy = (Position.xy / Position.w) * 0.5 + 0.5;
    Par
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
     
    Position.xy = fma(Position.xy / Position.w, 0.5, 0.5);
    fma(a, b, c) a une meilleur précision que a*b+c, ça peut peut être marcher...
    Sinon je ne vois pas trop, ça m'étonne qu'il y ait des erreurs de précisions sur des calculs aussi "simple". A mon avis, c'est plus un "0.5" qui manque quelque part, mais où ?...

  5. #5
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Ou alors, les points que tu dessines ne tombent-il pas pile entre 2 pixels écran ?

    Par exemple, avec un viewport de 2x2 et un view volume centré en [-1, 1] (une projection matrix à l'identité), il faut bien dessiner les points en -0.5 et 0.5 pour tomber au milieu du pixel, et non en -1, 0 ou 0, 1.

    Que vaut gl_ProjectionMatrix, VertexPosition et la taille de ton viewport dans ton cas ?

  6. #6
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Hey, je connaissais pas l'opérateur "fma" mais j'en frémis si ça marche et il y a moyen :p
    Si en effet je pense que mes points erronés sont quasi à cheval entre 2 pixels (c'est ce qui me fait penser à une erreur de précision) et que mon calcul fait que le pixel A est choisi alors que le rasterizer trouve que c'est le B.
    J'utilise une projection ortho (0, 60, 0, 60, -1, 0), VertexPosition appartient au domaine ([0.0 ; 60.0[, [0.0 ; 60.0[) et mon viewport est (0, 0, 512, 512).

    Merci pour l'aide que tu m'apportes ^^

  7. #7
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Et bien ça ne marche paaaaas snif.
    Voilà l'image qui me sert de debug :

    Bon là j'ai beaucoup simplifié au final mes points subissent une accélération vers en bas à droite et via du double buffering je les fais se déplacer dans un vertex shader et met à jour grâce à un transform feedback. Là j'ai fais en sorte que dès leur position initiale une ligne de points mal calculés soient mis en évidence. Mon but est donc de trouver le calcul qui fera que toute la grille de points soit verte.

  8. #8
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Je pense le problème vient du VertexPosition.

    Avec ta projection et ton viewport, VertexPosition doit valoir 0.5*60/512 + k * 60/512, avec k entier pour tomber au milieu d'un pixel.

    N'oublie pas que le point [0, 0, 0] est sur le coin bas/gauche du pixel bas/gauche du viewport, et le point [60, 60, 0] est sur le coin haut/droit du pixel haut/droit du viewport (dsl, je n'ai pas le temps de faire dessin )
    Donc si tu affiche "bêtement" tes points aux coords 1*60/512, 2*60/512, 3*60/512 etc... tu tombe systématiquement entre 2 pixels. Il faut que tu décales tout d'1/2 pixel, soit 0.5*60/512.

    Vérifie tes calculs (avec un papier et un crayon ) en faisant un viewport de 2x2, une ortho de (0, 1, 0, 1) et vois si tes points tombent bien au milieu des pixels (moi-même j'y arrive rarement du 1er coup avec ces histoires de milieu de pixel et de +0.5).

    Pour les opérateur GLSL, vas faire un tour sur http://www.opengl.org/sdk/docs/manglsl/ (si ce n'est pas déjà fait). Il y en a de très intéressants (mais pas tous GLSL 1.10 )

  9. #9
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Nouvel élément qui me laisse penser que c'est un problème de précision, au lieu d'essayer de prévoir où va être rasterizé mon point, je fais en sorte que gl_Position possède la même erreur de précision que mon calcul de façon à ce que le rasterizer fasse la même erreur que moi et rasterize sur le pixel que je veux. Cela marche, ma ligne de points rouges devient bien verte mais les points sont donc dessiné un pixel au dessus de celui sur lequel ils devraient être dessiné, ce qui n'est pas acceptable mais je pense que ça rend bien l'erreur visible. Voici mon code :
    Vertex shader :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    #version 150 compatibility
     
    #pragma debug(on)
    #pragma optimize(off)
     
    in vec3		VertexPosition;
     
    uniform vec2	BucketResolution;
     
    flat out vec2	fake_position;
     
    void main()
    {
    	vec4 pos = vec4(gl_ProjectionMatrix * vec4(VertexPosition, 1.0));
    	pos.xy = floor((pos.xy * 0.5 + 0.5) * BucketResolution) + 0.5;
    	fake_position = pos.xy;
    	pos.xy = (pos.xy / BucketResolution - 0.5) * 2.0;
    	gl_Position = pos;
    }
    Fragment shader :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    #version 150 compatibility
     
    #pragma debug(on)
    #pragma optimize(off)
     
    uniform vec2	BucketResolution;
     
    flat in vec2	fake_position;
     
    void main()
    {
    	gl_FragData[0] = vec4(0.0, 1.0, 0.0, 1.0);
    	if(ivec2(fake_position) == ivec2(gl_FragCoord.xy))
    		return;
     
    	gl_FragData[0] = vec4(1.0, 0.0, 0.0, 1.0);
    }

  10. #10
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Peux-tu donner une valeur de VertexPosition que tu envois au shader ? (coté CPU donc). J'aimerai essayer de faire les calculs à la main voir à quel endroit le problème apparait. Même 2 valeurs si c'est possible, une qui marche et une qui ne marche pas.

  11. #11
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Oui ok je vais essayer de savoir qu'elle valeur fait que ça ne marche pas.
    Merci

  12. #12
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Je te donne juste le y vu que là c'est une ligne de points qui est erroné le x n'a donc pas d'importance.

    VertexPosition.y d'un point bien calculé : 49.3137
    Rasterizé == Calculé

    VertexPosition.y d'un point mal calculé : 50.7422
    Rasterizé : 432
    Calculé : 433
    A la main je trouve 433 :/

    Taille du viewport : 512, 512
    Ortho : 0, 60, 0, 60, -1, 0

  13. #13
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    AHAH !!!

    C'est bien un problème de 0.5.
    Avec 49.3137, tu arrive à une position pixel de 420.81 (49.3137 / 60 * 512)
    Avec 50.7422, tu arrive à une position pixel de 433.001, avec les imprécision ça peut donner 433.001 ou 432.999, donc le floor pète.

    Normalement, tu es censé arriver au milieu du pixel, donc avoir des valeurs du genre 433.4999 ou 433.5001, qui ne te poseront pas de problème de précision (le floor retournera toujours 433).

    Donne voir le code qui calcul VertexPosition ?

  14. #14
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    vec4 pos = vec4(gl_ProjectionMatrix * vec4(VertexPosition, 1.0));
    gl_Position = pos;
    pos.xy = floor((pos.xy * 0.5 + 0.5) * BucketResolution) + 0.5;
    fake_position = pos.xy;
    Et le problème c'est que des fois j'ai des positions raster à X+0.1 ou à X+0.9 correctes, si c'était un problème de 0.5 j'aurais beaucoup plus de points erronés là c'est très rare !

  15. #15
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    nonon, le code CPU qui calcul VertexPosition avant de l'envoyer au shader.

  16. #16
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    bah VertexPosition (correspond à gl_Vertex dans un vertex shader) c'est juste un vec3 dans un VBO qui représente la position d'un point, il peut valloir n'importe quelle valeur c'est une contrainte vu qu'après les particules se déplacent elles doivent pouvoir avoir n'importe quelle valeur même celle qui ne m'arrange pas, tant qu'elle reste dans le domaine [0;60[

    Voilà le code mais bon c'est pas utile normalement :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    XParticle xParticle;
    for(unsigned int y = 0; y < heightParticleNum; ++y)
        for(unsigned int x = 0; x < widthParticleNum; ++x)
        {
            randX = 16.445561;
            randY = 23.45651;
     
            xParticle.position = QVector3D((x * mySurfaceSize.x()) / (sqrtParticleNum - 1.0) / 2.0 + mySurfaceSize.x() / 20.0 + randX,
                                           (y * mySurfaceSize.y()) / (sqrtParticleNum - 1.0) / 2.0 + mySurfaceSize.y() * 2.0 / 40.0 + randY,
                                           1.0);
        }
    Je fixe mon rand pour pouvoir retomber sur l'erreur juste en relançant l'application. (la grille dépasse un peu du domaine en hauteur mais on s'en fou ça pose pas problème ^^)

  17. #17
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Ah, ok, je pensais qu'il s'agissait d'une grille régulière.
    Dans ce cas essaye de rajouter dans le shader:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
    vec4 pos = vec4(gl_ProjectionMatrix * vec4(VertexPosition, 1.0));
    pos.xyz /= pos.w;
    pos.xy = (floor(pos.xy*textureSize) + 0.5) / textureSize;
    gl_Position = pos;
    ...
    Ou alors, tu réaligne tes points coté CPU.
    Par contre, tu aura toujours un décalage d'au pire 1/2 pixel entre tes points CPU et tes points GPU (etant donné que tes points doivent aligné sur ta grille de pixel). Je ne sais pas si ça peut te poser problème dans tes calculs...

  18. #18
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Oui c'est une grille régulière vu que le rand ne change pas (et c'est pour ça que c'est toute une ligne de points qui est rouge), en fait je viens de reregarder, je me suis trompé, c'est bien le calcul du rasterizer qui se gourre (j'ai mis à jour les données au dessus). Le rasterizer trouve 432 au lieu de 433 !
    J'ai essayé mais ça ne donne rien

  19. #19
    Membre émérite
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Mars 2011
    Messages
    618
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mars 2011
    Messages : 618
    Par défaut
    Oui, mais tu peux pas lui en vouloir. Si tu lui donne 433.0001, c'est normal qu'il se trompe un peu et tombe sur du 432.999 (ça en fait des calculs un raterizer mine de rien !), qu'il arrondi à 432. C'est pour ça que le seul moyen d'être sûr qu'il ne se trompe pas, c'est de lui filer 433.5001. Au pire, il tombe sur 433.4999 et arrondi à 433.

  20. #20
    Membre très actif Avatar de oxyde356
    Homme Profil pro
    Ingénieur Recherche Imagerie
    Inscrit en
    Février 2006
    Messages
    797
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Ingénieur Recherche Imagerie

    Informations forums :
    Inscription : Février 2006
    Messages : 797
    Par défaut
    Ok pour le forcer à rasterizer là où je veux il n'y a pas de problèmes, mais selon toi c'est normal qu'il se trompe ??? c'est fou ça :o surtout que ça le fait pas avec une carte ATI ... si c'est vraiment une optimisation de la part de nvidia je la trouve dangeureuse. J'aimerais bien trouver un article qui en parle :/

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo