The Book of Shaders by Patricio Gonzalez Vivo & Jen Lowe

日本語 - 中文版 - 한국어 - Español - Français - Italiano - Deutsch - English


NASA / WMAP science team

Bruit

Il est temps de faire une pause! Nous avons joué avec des fonctions aléatoires qui ressemblaient au bruit blanc d'une télévision. On a encore la tête qui tourne en pensant aux shaders et nos yeux sont fatigués. Il est grand temps de sortir faire un tour!

Nous sentons l'air sur notre peau, le soleil dans les yeux, le monde est tellement vibrant et coloré. Les couleurs, les textures, les sons. Pendant notre balade, nous ne pouvons nous empêcher de remarquer la surface de la route, les rochers, les arbres et les nuages.

Le côté imprévisible des variations de ces textures pourrait s'apparenter à de l'aléatoire mais elles semblent loin des fonctions aléatoires que nous avons vues juste avant. Le monde est tellement complexe, comment pouvons nous programmer un shader pour nous rapprocher de cette immense variété?

Cette question fut posée à Ken Perlin en 1980 quand il fut missionné pour trouver un moyen de générer des textures plus réalistes pour le film "Tron". Le résultat de ses recherches fut un élégant algorithme de bruit qui fut récompensé par un Oscar (rien que ça).

Disney - Tron (1982)

Ce qui suit n'est pas l'algorithme de Perlin mais il donne les clés pour comprendre comment générer du bruit à partir de valeurs aléatoires.

Nous commençons par quelque chose d'assez proche de ce que nous avons fait au chapitre précédent. Nous récupérons les parties entières (i) et fractionnelles (f) de x. Nous utilisons floor() pour obtenir i et fract() pour obtenir f. Ensuite nous appelons rand() sur la partie entière de x, ce qui renvoie une valeur unique par entier.

Après quoi, vous voyez deux lignes commentées, la première fait une interpolation linéaire entre les deux valeurs rand( i ) et rand( i+1 )

y = mix(rand(i), rand(i + 1.0), f);

Décommentez cette ligne pour voir ce que ça donne. Nous utilisons la partie fractionnelle f pour mélanger (mix()) les deux valeurs aléatoires.

Nous savons qu'il existe différentes manières d'interpoler des valeurs entre 0 et 1. Décommentez la ligne suivante qui utilise un smoothstep() pour calculer l'interpolation au lieu de l'interpolation linéaire.

y = mix(rand(i), rand(i + 1.0), smoothstep(0.,1.,f));

Vous devriez constater que la transition entre pics est beaucoup plus souple. Dans certaines implémentations, vous verrez que les programmeurs préfèrent parfois coder leur propre fonction cubique (comme la formule suivante) au lieu d'utiliser smoothstep().

float u = f * f * (3.0 - 2.0 * f ); // custom cubic curve
y = mix(rand(i), rand(i + 1.0), u); // using it in the interpolation

Cet aléatoire lissé change tout pour les artistes et les designers ; il permet de générer des images au rendu plus naturel. L'algorithme de Perlin a été implémenté dans tous les langages et en plusieurs dimensions pour créer toutes sortes de motifs.

Robert Hodgin - Written Images (2010)

A votre tour:

Bruit 2D

A prséent que nous savons faire un bruit sur une dimension, passons à la 2D. En 2D, au lieu d'interpoler entre deux points sur une ligne, (floor(x) et floor(x)+1.0), nous allons interpoler entre les quatre coins d'un carré du plan: floor(st), floor(st)+vec2(1.,0.), floor(st)+vec2(0.,1.) et floor(st)+vec2(1.,1.).

De la même façon que si nous voulons un bruit en 3D, nous devrons créer une interpolation entre les huit coins d'un cube. Cette technique se base sur l'interpolation de valeurs c'est pourquoi on l'appelle bruit de valeurs (value noise).

Comme dans l'exemple à une dimension, nous n'utiliserons pas une interpolation linéaire mais cubique qui nous permettra de lisser les interpolations dans notre grille carrée.

Regardez la fonction de bruit suivante:

Nous commençons par redimensionner l'espace en le multipliant par 5 (ligne 45), de manière à mieux voir les interpolations de la grille. Ensuite, dans la fonction de bruit, nous subdivisons l'espace en cellules. Nous récupérons les valeurs entières et fractionnelles de la cellule. La valeur entière nous permet de créer les valeurs aléatoires des quatre coins du carré (lines 23-26). Enfin, ligne 35, nous interpolons entre les quatre valeurs aléatoires des coins en utilisant la valeur fractionnelle du fragment dans la cellule.

A votre tour, essayez les choses suivantes:

Mark Rothko - Three (1950)

Utilisation du bruit dans le design génératif

Les algorithmes de bruit ont été inventés pour apporter une touche plus naturelle aux textures digitales. Les implémentations 1D et 2D que nous avons vues jusqu'à présent étaient des interpolations entre valeurs aléatoires, d'où leur nom de bruit de valeurs (Value Noise), mais il existe d'autres manières de créer un bruit...

Inigo Quilez - Value Noise

Comme vous l'avez découvert dans les exercices précédents, les bruits de valeurs (value noise) paraissent souvent crénelés. Pour atténuer le crénelage, Ken Perlin développe en 1985 une autre implémentation de son algorithme appelée Bruit de Gradient. Ken a trouvé comment interpoler des dégradés plutôt que des valeurs. Ces dégradés sont le résultat d'une fonction aléatoire 2D qui renvoie une direction (représentée par un vec2) au lieu de renvoyer une valeur de type float (un chiffre). Cliquez sur le lien ci dessous pour de plus amples informations sur l'algorithme.

Inigo Quilez - Gradient Noise

Prenez un moment pour comparer ces deux exemples par Inigo Quilez et soyez attentifs aux différences entre bruit de valeurs et bruit de gradient.

Comme un peintre qui comprend comment marchent les pigments, plus vous en saurez sur les différentes implémentations de bruit, mieux vous saurez vous en servir. Par exemple, si on utilise un bruit 2D pour faire pivoter un espace sur lequel on rend des lignes droites, on obtiendra le motif suivant qui ressemble à du bois.

Cliquez sur les exemples pour voir à quoi ressemble le code:

Wood texture

    pos = rotate2d( noise(pos) ) * pos; // fait pivoter l'espace
    pattern = lines(pos,.5); // puis dessine des lignes

Une autre approche pour créer des motifs consiste à les considérer comme des champs de distances et à leur appliquer différents traitements décrits au chapitre des formes.

Splatter texture

    color += smoothstep(.15,.2,noise(st*10.)); // éclaboussure noire
    color -= smoothstep(.35,.4,noise(st*10.)); // 'trou' dans l'éclaboussure

Une troisième approche consiste consiste à moduler une forme. Cela fait également appel aux techniques vues au chapitre des formes.

Entraînement:

Jackson Pollock - Number 14 gray (1948)

Bruit simplexe (Simplex Noise)

Malgré son succès, Ken Perlin n'était toujours pas satisfait de son algorithme, il pensait qu'il pouvait aller plus vite. Au Siggraph 2001, il présenta le bruit simplexe qui lui permit d'apporter les améliorations suivantes:

Je sais ce que vous pensez... "mais qui est cet homme?!" Oui, son travail est fantastique! mais concrètement, comment a-t'il amélioré l'algorithme? Bien, nous avons vu comment, pour 2 dimensions, il interpolait entre les valeurs de 4 points (les quatre coins d'un carré). Donc on peut imaginer que pour trois (voir implémentation ici) et quatre dimensions, nous aurons besoin de 8 et 16 valeurs pour faire nos interpolations. En d'autres mots, pour N dimensions, il nous faudra 2 puissance N (2^N) valeurs à interpoler. Ken a remarqué que, bien qu'on pense immédiatement au carré comme forme de remplissage pour un espace 2D, la forme 2D la plus simple est le triangle équilatéral. Il commença donc à remplacer la grille carrée (dont nous venons de nous servir) par une trame de triangles équilatéraux.

La forme simplexe pour N dimensions est une forme à N + 1 points. Donc pour calculer un bruit à l'aide d'un simplexe 2D, il faut 3 points, pour un bruit simplexe 3D, il en faut 4, et pour un bruit 4D, il en faut 5. en regard des 4 coins du carré, des 8 points du cube et des 16 de l'hypercube, le bénéfice est considérable!

En deux dimensions, l'interpolation se comporte comme dans un bruit normal, en interpolant les valeurs des coins d'un cellule. A la différence qu'ici, en utilisant une trame de simplexes, nous devons uniquement interpoler entre 3 valeurs.

Comment fabrique-t'on la trame de simplexes? Encore une trouvaille brillante, la trame de simplexes s'obtient en subdivisant les cellules d'une grille carrée en 2 triangles isocèles puis en leur appliquant un cisaillement (skewing) pour les rendre équilatéraux.

Puis, comme Stefan Gustavson l'explique dans sa publication': "...by looking at the integer parts of the transformed coordinates (x,y) for the point we want to evaluate, we can quickly determine which cell of two simplices that contains the point. By also comparing the magnitudes of x and y, we can determine whether the point is in the upper or the lower simplex, and traverse the correct three corner points."

Dans le code suivant, vous pouvez décommenter la ligne 44 pour voir comment la grille est cisaillée (skewed) et la ligne 47 pouvoir comment la trame de simplexes est construite. Notez comment, ligne 22, nous divisons le carré cisaillé en deux triangles équilatéraux en détectant simplement si x > y (triangle du "bas") ou si y > x (triangle du "haut").

Une autre amélioration apportée par Perlin avec le bruit simplexe est le remplacement de la spline cubique d'Hermite ( f(x) = 3x^2-2x^3 , qui est ce que fait un smoothstep()) par une spline Quintique d'Hermite ( f(x) = 6x^5-15x^4+10x^3 ). Ce changement a pour effet de rendre les bords des courbes plus plats et permet une sutture invisible entre cellules. Pour voir le principe, décommentez la seconde formule de l'exemple suivant (ou regardez les 2 équations côte à côte ici).

Remarquez comme le début et la fin de la courbe changent vers 0 et 1. Vous pouvez en lire plus sur le site de Ken Perlin. Ces améliorations ont conduit à cette oeuvre d'art algorithmique qu'est le bruit simplexe

Ci-dessous vous trouverez une implémentation de cet algorithm par Ian McEwan (décrite dans cette publication) qui sera peut-être un peu difficile d'accès mais qui vous permettra de réaliser qu'au fond c'est moins compliqué qu'il n'y paraît.

Ian McEwan of Ashima Arts - Simplex Noise

Bon... assez parlé technique, il est temps d'utiliser cette ressource de façon créative:

Dans ce chapitre, nous avons appris à contrôler le chaos et ce n'était pas chose aisée! Devenir maître du Bruit demande temps et énergie.

Dans les chapitre suivants, nous verrons d'autres techniques pour perfectionner vos talents et obtenir le maximum de vos fonctions de bruit pour enrihir vos shaders. D'ici là, prenez le temps de contempler la nature et les motifs qu'elle vous offre. La capacité d'observation est au moins aussi importante (sinon plus) que la capacité de recérer ces motifs.

Sortez et profitez du reste de votre journée!

"Parler à l'arbre, s'en faire un ami." Bob Ross