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

Moteurs 3D Discussion :

Multipass rendering en pratique


Sujet :

Moteurs 3D

  1. #1
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Février 2008
    Messages : 15
    Points : 5
    Points
    5
    Par défaut Multipass rendering en pratique
    Bonjour !
    Je connais la partie théorique du principe du multipass rendering mais j'ai du mal à voir comment peut s'articuler le code de ce type de rendu (en openGl pour ma part).

    Est-ce que ce sera du type:
    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
     
    void draw() {
     
       /* pass 1 */
       -- instructions specifiques pass 1 --
       for each object {
           drawObject();  /* = instructions Gl */
       }
     
        /* pass 2 */
        -- instructions spécifiques pass 2 --
       for each object {
           drawObject();  /* = instructions Gl */
       }
     
    }
    Ou plutôt quelque chose du genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    void draw() {
     
       for each object {   
     
       /* pass 1 */
       -- instructions specifiques pass 1 --
           drawObject();  /* = instructions Gl */
     
        /* pass 2 */
        -- instructions spécifiques pass 2 --
           drawObject();  /* = instructions Gl */
    }
    ou encore deux appels consécutifs à draw() avec un paramètre qui donnerait le numéro de pass ?

    ou autre chose qui n'aurait rien à voir ?

    Comment est fait le culling, rastering et autres fonctions fixe du pipeline dans un algo multipass ? Pour chaque objet pour chaque pass ?

    Merci de m'éclairer

  2. #2
    Membre expérimenté

    Profil pro
    Programmeur
    Inscrit en
    Août 2002
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Programmeur

    Informations forums :
    Inscription : Août 2002
    Messages : 1 091
    Points : 1 679
    Points
    1 679
    Par défaut
    Citation Envoyé par Shirakana Voir le message
    Est-ce que ce sera du type:
    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
     
    void draw() {
     
       /* pass 1 */
       -- instructions specifiques pass 1 --
       for each object {
           drawObject();  /* = instructions Gl */
       }
     
        /* pass 2 */
        -- instructions spécifiques pass 2 --
       for each object {
           drawObject();  /* = instructions Gl */
       }
     
    }
    Ou plutôt quelque chose du genre

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
     
    void draw() {
     
       for each object {   
     
       /* pass 1 */
       -- instructions specifiques pass 1 --
           drawObject();  /* = instructions Gl */
     
        /* pass 2 */
        -- instructions spécifiques pass 2 --
           drawObject();  /* = instructions Gl */
    }
    ou encore deux appels consécutifs à draw() avec un paramètre qui donnerait le numéro de pass ?

    ou autre chose qui n'aurait rien à voir ?
    En général tu cherches à minimiser les changements d'état du pipeline graphique, quels que soient ces changements. : minimisation du changement du nombre de render to texture, de copies entre buffers, du nombre de changement de données de vertex, de constantes etc.. Bien entendu, c'est un peu compliqué par le fait que chaque nouvel objet et chaque passe entraine un changement d'état.

    Avantage de la méthode 1 :
    - si tu as un shader commun à beaucoup d'objets, des blend states communs à beaucoup d'objet (ou communs à une passe), une texture commune à beaucoup d'objets, des lumières communes à beaucoup d'objet, alors tu peux économiser ces changements d'état. L'idéal serait d'avoir à n'envoyer les états qu'une seule fois et n'envoyer que de la géométrie dans la boucle for(;;).
    - Avoir tous les objets rendus dans une pré-passe légère permet de préparer le terrain pour une seconde passe plus lourde en pixel shaders. Par exemple c'est ce qui est fait dans plusieurs jeux récents et dans 3Dmark (pour l'anecdote) : le jeu remplit le ZBuffer avec l'information de profondeur et le color buffer avec une information de lumière ambiante (sans texture). Grâce à cette passe légère, le early-z et le z-culling sont plus efficaces dans la deuxième passe où le pixel shader peut faire plus de cent instructions (avec branchements, lectures de texture etc.). Aucun intéret par contre si ta plateforme ne supporte pas l'early-Z..

    Avantage de la méthode 2 :
    - Si tu as des données géométriques non résidentes ou que la carte graphique par une méthode ou une autre doit déplacer de la mémoire système via le bus PCI, ou pire que tu dois déplacer avec le CPU. Dans ce cas la méthode 2 peut être gagnante : par exemple, une géométrie dynamique qu'il faut mettre à jour par frame et qui ne peut pas rester résidente en mémoire vidéo.
    - Meilleur pattern d'accès au cache mémoire ? à voir (j'aurais tendance à penser que les changements d'états sont plus importants mais j'ai un point de vue biaisé).

    Citation Envoyé par Shirakana Voir le message
    Comment est fait le culling, rastering et autres fonctions fixe du pipeline dans un algo multipass ? Pour chaque objet pour chaque pass ?
    Pire que ça : le vertex et index fetching (lecture des données géométriques), le transform and lighting (plus généralement le vertex program), le clipping, le triangle setup, la rasterisation, l'interpolation, le culling (par le zbuffer, mais peut-etre plus rapide en 2e passe), le pixel shading (mais chaque passe est un peu moins complexe qu'une seule grosse passe) et l'écriture dans le z-buffer et le color-buffer (read modify write puisque tu es en multipasse). Tout ça est multiplié par le nombre de passes. Et ne pas oublier le coût CPU de l'envoi de géométrie, du parcours de ton arbre de rendu, du changement d'états entre objets etc.

    Donc si tu est limité par une de ces parties (goulet d'étranglement), passer d'une passe à plusieurs passes aura une conséquence néfaste sur tes performances.
    Certaines personnes contournent ça en utilisant des méthodes de rendu différées (deferred lighting/shading). Mais cela vient avec plein de conséquences sur ce qu'ils peuvent faire ou ne pas faire (antialiasing par multisampling, anisotropic filtering etc.)

    Ceci dit on est sur PC donc le goulet d'étranglement variera d'une PC à un autre (voire d'un setting graphique à l'autre). Bonne chance :)

    LeGreg

    Mon site web | Mon blog | Mes photos | Groupe USA
    > BONJOUR, JE SUIS NOUVEAU SUR CE FORUM
    > presse la touche caps lock, stp
    > OH.. MERCI C EST BEAUCOUP PLUS FACILE COMME CA

  3. #3
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Février 2008
    Messages : 15
    Points : 5
    Points
    5
    Par défaut
    Le deferred shading est typiquement un rendu multi pass puisqu'il consiste en un premier rendu pour construire le G-Buffer (ou fat-buffer) puis un (ou plusieurs) rendu(s) à partir du G-Buffer en fonction des effets désirés.

    Donc si j'ai bien compris l'architecture du code d'un rendu multipass dépend de l'effet qu'on veut obtenir.

    Par exemple si je veux faire du shadowmapping ce sera qch du genre (en schématisant grossièrement):

    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
    20
    21
     
    void draw() {
     
      // Pass 1 = construction des shadow maps
      for each light {
        changeViewPort(light[i]);  // On place la caméra la où se trouve la lumière
        for each object {
           drawObject();
       }
       shadowMap[i] = getZBuffer();
     }
     
      // Pass 2 = rendu de la scène "classique" avec un shader qui se charge des ombres
      resetViewPort(); // On revient à la position initial de la caméra
      setShaderAttribute(shadowMapShader,shadowMap);  // On passe au shader les SM obtenues en pass 1
      activateShader(shadowMapShader);
      for each object {
          drawObject();  // -> les appels Gl vont faire appel au shader qui va rendre chaque fragment coloré ou ombré
      }
     
    }
    Une autre chose obscure pour moi est le moment où est activé le pipeline de rendu du GPU (transformation - clipping - rasterisation...): à la fin de chaque bloc glBegin()...glEnd() ou à la fin de la fonction draw() une fois que toutes la géométrie à été envoyé à la carte ?

  4. #4
    Membre expérimenté

    Profil pro
    Programmeur
    Inscrit en
    Août 2002
    Messages
    1 091
    Détails du profil
    Informations personnelles :
    Localisation : Etats-Unis

    Informations professionnelles :
    Activité : Programmeur

    Informations forums :
    Inscription : Août 2002
    Messages : 1 091
    Points : 1 679
    Points
    1 679
    Par défaut
    le shadow mapping n'est pas considéré à proprement parler comme du multipass. Certes il nécessite plusieurs passes, mais celles-ci sont indépendantes (différents points de vue) et ne pourraient pas être fusionnées en une seule..

    Enfin bon c'est discutable, mais quand on me parle de multipass c'est ce à quoi je pense (ou à Korben Dallas).

    Une autre chose obscure pour moi est le moment où est activé le pipeline de rendu du GPU (transformation - clipping - rasterisation...): à la fin de chaque bloc glBegin()...glEnd() ou à la fin de la fonction draw() une fois que toutes la géométrie à été envoyé à la carte ?
    Cela varie. Le GPU et le CPU vivent chacun dans un espace-temps séparé. Ils ne communiquent que par un wormhole monodirectionnel qui s'appelle le push buffer ou command buffer. Ce wormhole ne se vide qu'à la vitesse de réaction du GPU, ce qui peut-etre très rapide (si le GPU n'a rien à faire entre deux commandes) ou très lent (si le GPU est surchargé).

    LeGreg

    Mon site web | Mon blog | Mes photos | Groupe USA
    > BONJOUR, JE SUIS NOUVEAU SUR CE FORUM
    > presse la touche caps lock, stp
    > OH.. MERCI C EST BEAUCOUP PLUS FACILE COMME CA

  5. #5
    Membre averti

    Profil pro
    Étudiant
    Inscrit en
    Décembre 2004
    Messages
    499
    Détails du profil
    Informations personnelles :
    Âge : 37
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Décembre 2004
    Messages : 499
    Points : 422
    Points
    422
    Par défaut
    salut
    le programmeur est censé considérer que le gpu fait tout le traitement des triangles d'un coup une fois le paquet envoyé

    (glBegin/glEnd, display list ou vbo)

    sinon je ne connais pas d'algo où il est possible d'utiliser une fonction draw du type

    void draw() {

    for each object {

    /* pass 1 */
    -- instructions specifiques pass 1 --
    drawObject(); /* = instructions Gl */

    /* pass 2 */
    -- instructions spécifiques pass 2 --
    drawObject(); /* = instructions Gl */
    }

    à moins que le pass1 et le pass2 n'écrivent pas les même buffer, dans ce cas il y a sûrement une pass3

    et comme c'est possible on préfère alors dessiner toute la scène en une prémière passe 1, puis toute la scène en une deuxième passe

    enfin pour les histoires de changements d'état d'opengl, il ne faut pas se prendre trop la tête, ça sera bien souvent le détail le moins important pour ton framerate

  6. #6
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Février 2008
    Messages : 15
    Points : 5
    Points
    5
    Par défaut
    La je cherche à faire du SSAO et j'ai du mal à voir comment vont s'enchainer les deux passes. Mais si j'ai bien compris les posts précédents ça donnera qch du genre:

    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
     
    void draw() {
     
      // Pass 1 = rendu normal de la scène
      for each object
         drawObject();
     
      // Donc la je considère que la cg a effectué le pipeline de rendu pour toute la géométrie ?
      zbuffer = getCurrentZBuffer();
      colorBuffer = getCurrentColorBuffer();
      setShaderAttribute(ssao,zbuffer);
      setShaderAttribute(ssao,colorBuffer);
      activateShader(ssao);
      drawFullScreeQuad();
     
    }

  7. #7
    Futur Membre du Club
    Profil pro
    Inscrit en
    Février 2008
    Messages
    15
    Détails du profil
    Informations personnelles :
    Âge : 41
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Février 2008
    Messages : 15
    Points : 5
    Points
    5
    Par défaut
    Apparemment la réponse est: glFlush() (il existe aussi un glFinish() mais je ne connais pas la différence entre les deux)

    Donc ça donnera quelque chose comme:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
     
    void draw() {
     
      // pass 1
      for each object
         drawObject();
     
      glFlush();  // toute la géométrie est transféré vers la carte graphique est le Z-buffer (ou autre) est assuré d'être complet
     
      // pass 2
      ...
    }

Discussions similaires

  1. Qui pratique la programmation spontanée ?
    Par Doloop dans le forum Débats sur le développement - Le Best Of
    Réponses: 486
    Dernier message: 11/04/2023, 19h50
  2. Bonnes pratiques de protections individuelles
    Par Community Management dans le forum Sécurité
    Réponses: 22
    Dernier message: 05/04/2013, 11h47
  3. [Jtable] renderer?
    Par britou dans le forum Composants
    Réponses: 5
    Dernier message: 18/05/2004, 12h10
  4. Render to texture???
    Par sebh dans le forum OpenGL
    Réponses: 2
    Dernier message: 17/01/2004, 09h50
  5. [Sockets] Questions pratiques
    Par ludovic.fernandez dans le forum Réseau
    Réponses: 5
    Dernier message: 16/01/2004, 18h53

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