Pipeline Graphique en WebGL

Que se passe-t-il sur la carte graphique ?

  • Three.js effectue le rendu d'une scène
  • Comment cela marche en pratique ?
  • Que se passe-t-il sur la carte graphique ?

Le pipeline graphique

  • Une liste de sommets
  • Une liste d'attributs (couleurs, normales...)
  • Des relations d'adjacence
  • + autres... (caméras, lumières, textures...)
Pipeline

Une grille 2D de pixels

Le pipeline graphique simplifié

Pipeline

Le pipeline graphique

Opérations par sommet

Pipeline
  • En parallèle pour chaque sommet
  • Programmable : Vertex Program
  • Transformations géométriques et projection en 2D
  • Déplacement des sommets (déformation de la géométrie)
  • Au minimum : Position de chaque sommet (gl_Position)

Le pipeline graphique

Assemblage des primitives

Pipeline
  • Programmable : Geometry Program
  • Information d'adjacence (Identique pour tous les sommets)
  • Assemble les sommets 2D transformés
  • Produit une primitive dans le plan 2D

Le pipeline graphique

Rasterisation

Pipeline
  • Discretisation des polygones, tracés de ligne, remplissage des polygones
  • Génère un ensemble de fragments
  • Fragments de pixel
    • Correspond à une partie d'un pixel dans l'espace final
    • A une position fixe (le pixel)
  • Interpolation des attributs

Le pipeline graphique

Rasterisation

Pipeline
  • En parallèle pour chaque fragment
  • Programmable : Fragment Program
  • Calcul d'éclairage (eq. du rendu)
    • Changement d'apparence
    • La plupart des effets spéciaux
  • Au minimum : Couleur de chaque fragment

Le pipeline graphique

Combinaison des fragments

Pipeline
  • En parallèle pour chaque fragments
  • Elimination des parties cachées
  • Opérations de transparence
  • Mélange des fragments avec le frame buffer
  • Remplissage du frame buffer

Pour résumer

  • Un sommet de la géometrie : Un triplet XYZ
  • Opération par sommet : Vertex program
    • Transformations géometriques
    • Projections
  • Assemblage et Rasterisation
  • Fragment : Partie potentielle de pixel
  • Opération par fragment : Fragment program
    • Illumination
    • Apparence
  • Combinaison et remplissage du Frame Buffer

Les shaders

Le principe est de créer un materiau et d'ecrire les programmes correspondant pour changer l'execution du pipeline

Un style de materiau : MeshShaderMaterial


         var shaderMaterial = new THREE.MeshShaderMaterial({
        uniforms: uniforms,
        vertexShader: document.getElementById( 'vertexShader' ).textContent,
        fragmentShader: document.getElementById( 'fragmentShader' ).textContent
        

En WebGL, il est obligatoire de définir un vertex et un fragment program (Three.js le fait pour les matériaux utilisés jusqu'à présent)

Un shader, qu'est ce que c'est ?

  • Un algorithme (ici en GLSL)
  • S'applique a tous les éléments d'une étape
  • S'exécute sur la carte graphique
  • Ne peux pas accéder directement à la mémoire de l'application
  • Doit être compilé au préalable
  • Algorithme instancié et executé une fois pour chaque élément
  • Programmation parallèle
    • Indépendance des données
    • Séparation des opérations

Variables des shaders

  • Chaque variable (globale) correspond à une donnée.
  • Chaque donnée possède (en plus de son type) un qualificateur précisant sa nature
  • Des données qui traversent le pipeline
    • Des données qui entrent dans le pipeline (qualificateur attribute)
    • Des données qui transitent (entre les shaders) (qualificateur varying)
  • Des données qui ne traversent pas le pipeline mais servent à en contrôler ou modifier l'exécution (qualificateur uniform)

Variables entrantes

  • Doivent avoir le qualificateur attribute
  • Sont différentes pour chaque instance du shader
  • Sont différentes pour chaque sommet de la géométrie
  • Encodent une information par sommet (position,normale,couleur...)
  • Proviennent du CPU
  • Doivent être traitées (lues) par le shader
  • Sont en lecture seule
  • Exemple : La position des sommets dans l'espace 3D

Variables de transition

  • Doivent avoir le qualificateur varying
  • Sont différentes pour chaque instance du shader
  • Sont écrite par une étape du pipeline (le vertex shader)
  • Sont envoyées à l'étape suivante du pipeline (le fragment shader)
  • Servent a communiquer entre les étapes du pipeline
  • Sont interpolées lors de l'étape de rasterisation
  • Correspondance de nom et de type
  • La variable varying toto d'une étape se retrouve comme la variable varying toto de l'étape suivante
  • Variables de contrôle

    • Doivent avoir le qualificateur uniform
    • Sont identiques pour chaque instance du shader
    • Ne traversent pas le pipeline
    • Proviennent de l'application
    • Permettent la communication entre le shader (GPU) et l'application (CPU)
    • Contr?lent l'eécution du pipeline (ce sont des paramètres)
    • Exemple : matrices de transformations, position des lumières

    En pratique en Three.js et WebGL

    • Il existe des variables prédéfinies pour chaque ?tape
      • position contient la position d'un sommet dans l'espace 3D (c'est une variable attribute)
      • modelViewMatrix contient la matrice de transformation entre un objet et la caméra (c'est une variable uniform)
      • projectionMatrix contient la matrice de projection liée à la caméra (c'est une variable uniform)
      • gl_Position doit être remplie avec la position projetée des sommets (c'est une variable varying)
      • gl_FragColor doit être remplie avec la couleur du fragment
    • Plus d'info dans la carte de réference rapide WebGL

    Premiers Shaders

    Que faire avec ces shaders ?

    Si on ne fait pas la projection, on ne voit pas grand chose

    Commençons par simuler le pipeline fixe : le matériau MeshBasicMaterial

    Simuler le pipeline fixe de Three.js

    Vertex Program (rappel)

    Pipeline

    Simuler le pipeline fixe de Three.js

    Vertex Program

    • Doit (au minimum) calculer la projection des sommets sur le plan image
      • Réaliser la multiplication de la position des sommets (position) par les matrices de transformations et de projection
      • La variable modelViewMatrix contient la matrice des transformations
      • La variable projectionMatrix contient la matrice de la projection
      • gl_Position doit contenir la position projetée des sommets
    • 
             void main() {
        gl_Position = projectionMatrix *
                      modelViewMatrix *
                      vec4(position,1.0);
      }
              

    Simuler le pipeline fixe de Three.js

    Fragment Program (rappel)

    Pipeline

    Simuler le pipeline fixe de Three.js

    Fragment Program

    • Doit (au minimum) calculer la couleur des fragments
      • Remplir la variable gl_FragColor
      
             void main() {
        // d?finir la couleur sous la forme RGBA (0.0 - 1.0)
        gl_FragColor = vec4(0.0,0.5,0.5,1.0);
        }
              

    Résultat du premier shader

    Interactivité et communication avec le shaders

    Passage de paramètres

    • Pas encore tout a fait ca
    • La couleur finale est écrite en dur dans le shader
    • Comment passer des paramètres depuis WebGL aux shaders ?
    • Mécanisme de variables uniform

    Passage de paramètres

    • Déclarer (dans le script) une variable myUniforms contenant les variables uniformes
      
            var myUniforms = 
            {
                color :
        {
          type :'c', // une couleur
          value : new THREE.Color( 0xffffff )
        },
      }
              
    • Integrer notre variable dans le materiau
      
            var material = new THREE.ShaderMaterial( {
        uniforms : myUniforms,
          vertexShader: document.getElementById( 'vertexShader' ).textContent,
          fragmentShader: document.getElementById( 'fragmentShader' ).textContent
      } );
              
    • Modifier la variable depuis le javascript
      
              myUniforms.color.value = new THREE.Color(value);
      
                  

    Résultat du shader (avec interface)

    Operation par sommet

    • Déplacer chaque sommet le long de sa normale d'une amplitude variable en fonction du temps
      
      
              uniform float multiplicateur; // entre -1 et 1
      
              void main() {
              vec3 myPosition = position + normal*multiplicateur; 
              gl_Position = projectionMatrix *
              modelViewMatrix *
              vec4(myPosition,1.0);
      }
             
      
              
    • Faire varier multiplicateur en fonction du temps (fct animate)
    • normal est un attribut par sommet défini par three.js

    Résultat du shader

    Opérations par fragments

    • Opérations réalisées dans le fragment shader
    • Modifier la couleur par fragment
    • Exemple
      • Moduler la couleur en fonction de la position du fragment a l'écran
        
               void main() {
               gl_FragColor = vec4(gl_FragCoord.xy*0.001,0.5,1.0);
               }
        
                  
      • gl_FragCoord donne les coordonnées (en pixel) du fragment

    Résultat du shader

    Résultat du shader - Exemple ShaderToy

    Beaucoup d'opérations possibles

    • ShaderToy : uniquement un fragment shader
    • Beaucoup de fonctions (noise, utilisation de textures, calcul physiques...)