Informatique Graphique

ShaderToy

Le passage à la 3D

A partir de ShaderToy, il est également possible de représenter un monde en 3 dimesions

Calculer la couleur d'un pixel dans ce monde 3D va consister à raisonner, non plus en terme de coordonnées à 2 dimensions mais à 3 dimensions

Pour cela, considérons maintenant que notre fenêtre de pixels est une grille placée devant notre oeil dans un monde en 3 dimensions

Le passage à la 3D

A partir des coordonnées uv de notre pixel sur sa grille, nous pouvons déduire l'expression d'un rayon, partant de notre oeil et traversant ce pixel

Utilisons pour cela le code suivant qui vous donne l'origine et la direction d'un rayon pour chaque pixel.


                           vec2 uv = gl_FragCoord.xy / iResolution.xy;
vec3 origine = vec3(0.0, 0.0, -2.0);
vec3 direction = normalize(vec3(uv, 1.0));
                      

Le passage à la 3D

Ce rayon va représenter ce qu'on verrait si on observait le monde a travers ce pixel

L'idée va être maintenant de manipuler ce rayon pour déterminer ce qu'il intersecte dans le monde 3D

Pour cela, il est possible de calculer des intersections entre ce rayon et des objets 3D

Pour éviter les complexes calculs d'intersection, utilisons la technique du RayMarching.

Le RayMarching et son principe

Supposons que nous disposons pour un objet donné, d'une fonction qui nous indique si un point de l'espace 3D appartient ou non a cet objet

Le principe va être de "marcher" le long de notre rayon et d'interroger cette fonction à intervalle régulier pour déterminer si notre position actuelle appartient ou non à l'objet

Le RayMarching et son principe

  • Exprimons un point de l'espace de la manière suivante
    
    vec3 P = origine + k * direction;
                          
  • Effectuons maintenant la boucle suivante :
    • Verifier si P appartient à l'objet.
    • Avancer de k unités dans la direction et recalculer P
    • S'arreter lorsque P appartient à un objet (ou après n itérations)
  • Si P est dans l'objet, il faut maintenant calculer une couleur (BRDF, equation du rendu...)

Premier exercice : une sphere

  • En utisant le principe du RayMarching, afficher une sphére de position (1.0,1.0,1.0) et de rayon 1.0.
  • En faisant avancer votre rayon 512 fois avec un pas k de 0,05.
  • Si ce rayon traverse la sphère, donnez comme couleur à ce pixel la couleur de votre choix

Eclairage de phong

  • A l'étape précédente nous avons considéré que notre couleur était une constante
  • Changeons un peu cela
  • Calculons et choisissons comme couleur la valeur absolue de la normale de la sphère au point P

Eclairage de phong

  • Rajoutons une lumière (aux coordonnées (1,1,-2)) et calculons la composante diffuse du modèle de phong (Rappel : la couleur devient alors :
    
    vec3 Couleur = couleur_sphere * dot(normale,direction_Lumiere);
                          
  • Ajoutons maintenantla composante spéculaire avec un facteur spéculaire q de 64.
  • Affichons la sphère avec le modéle de phong complet (ambiant+diffus+speculaire).

Eclairage de phong

  • La Lumière est un point qu'il est possible de déplacer à l'aide des fonction sinus, cosinus et de la variable iTime.
  • Ajoutons donc des variations de lumière afin de la faire tourner autour de nous.
  • Modifions le facteur spéculaire q à l'aide de la position de la souris afin de mieux contrôler notre apparence

Pour aller plus loin

  • Remplacer la fonction qui calcule la distance d'un point à la sphère par une fonction de bruit "noise" en rajoutant les fonction ci-dessous au début de votre shader.
  • 
    
    
    vec3 mod289(vec3 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
    }
    vec4 mod289(vec4 x) {
    return x - floor(x * (1.0 / 289.0)) * 289.0;
    }
    vec4 permute(vec4 x) {
    return mod289(((x*34.0)+1.0)*x);
    }
    vec4 taylorInvSqrt(vec4 r)
    {
    return 1.79284291400159 - 0.85373472095314 * r;
    }
    float noise(vec3 v)
    {
    v=0.1*v;
    const vec2 C = vec2(1.0/6.0, 1.0/3.0) ;
    const vec4 D = vec4(0.0, 0.5, 1.0, 2.0);
    // First corner
    vec3 i = floor(v + dot(v, C.yyy) );
    vec3 x0 = v - i + dot(i, C.xxx) ;
    // Other corners
    vec3 g = step(x0.yzx, x0.xyz);
    vec3 l = 1.0 - g;
    vec3 i1 = min( g.xyz, l.zxy );
    vec3 i2 = max( g.xyz, l.zxy );
    // x0 = x0 - 0.0 + 0.0 * C.xxx;
    // x1 = x0 - i1 + 1.0 * C.xxx;
    // x2 = x0 - i2 + 2.0 * C.xxx;
    // x3 = x0 - 1.0 + 3.0 * C.xxx;
    vec3 x1 = x0 - i1 + C.xxx;
    vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y
    vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y
    // Permutations
    i = mod289(i);
    vec4 p = permute( permute( permute(
    i.z + vec4(0.0, i1.z, i2.z, 1.0 ))
    + i.y + vec4(0.0, i1.y, i2.y, 1.0 ))
    + i.x + vec4(0.0, i1.x, i2.x, 1.0 ));
    // Gradients: 7x7 points over a square, mapped onto an octahedron.
    // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294)
    float n_ = 0.142857142857; // 1.0/7.0
    vec3 ns = n_ * D.wyz - D.xzx;
    vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7)
    vec4 x_ = floor(j * ns.z);
    vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N)
    vec4 x = x_ *ns.x + ns.yyyy;
    vec4 y = y_ *ns.x + ns.yyyy;
    vec4 h = 1.0 - abs(x) - abs(y);
    vec4 b0 = vec4( x.xy, y.xy );
    vec4 b1 = vec4( x.zw, y.zw );
    //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0;
    //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0;
    vec4 s0 = floor(b0)*2.0 + 1.0;
    vec4 s1 = floor(b1)*2.0 + 1.0;
    vec4 sh = -step(h, vec4(0.0));
    vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ;
    vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ;
    vec3 p0 = vec3(a0.xy,h.x);
    vec3 p1 = vec3(a0.zw,h.y);
    vec3 p2 = vec3(a1.xy,h.z);
    vec3 p3 = vec3(a1.zw,h.w);
    //Normalise gradients
    vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3)));
    p0 *= norm.x;
    p1 *= norm.y;
    p2 *= norm.z;
    p3 *= norm.w;
    // Mix final noise value
    vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0);
    m = m * m;
    return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1),
    dot(p2,x2), dot(p3,x3) ) );
    }
    
                          

Du bruit partout

  • On considère être dans l'objet si la valeur retournée par la fonction noise est supérieure à 0.7
  • Il faut maintenant réintegrer notre eclairage précédent
  • Pour ce faire, il faut recalculer la normale. Avez vous des idées pour cela ?
  • Rajoutons maintenant un déplacement de la camera en fonction du temps.