Bruit de Perlin

Le bruit de Perlin est une texture procédurale utilisée comme effet visuel pour augmenter le réalisme apparent dans la synthèse d'image. La fonction a une apparence pseudo-aléatoire, et pourtant tous ses détails visuels sont de taille égale (voir image). Cette propriété permet à cette texture d'être facilement contrôlable. De multiples copies zoomées de bruit de Perlin peuvent être insérées dans des expressions mathématiques pour créer une grande variété de textures procédurales.
Usages
[modifier | modifier le code]
Le bruit de Perlin est une primitive utilisée pour la génération de textures procédurales. C'est un bruit de gradient (par opposition aux bruits de valeur) utilisé pour améliorer le réalisme des infographies. La fonction a un aspect pseudo-aléatoire, cependant ses détails sont de la même taille : cette propriété la rend facile à contrôler et à combiner à différentes échelles.
Le bruit de Perlin est souvent utilisé dans les images de synthèse pour des éléments tels que le feu, la fumée ou les nuages.
Les démos utilisent couramment le bruit de Perlin car il est très économe en espace mémoire.
Il est aussi utilisé dans le jeu vidéo. Par exemple, Minecraft utilise le bruit de Perlin pour générer ses mondes.
-
Feu généré à partir de bruit de Perlin.
-
Terrain généré à partir de bruit de Perlin.
Développement
[modifier | modifier le code]Le bruit de Perlin a été développé par Ken Perlin en 1985. À cette époque, après avoir travaillé sur les effets spéciaux de Tron pour MAGI (en) en 1981, il cherchait à éviter le rendu « très artificiel »[1] des images de synthèse. Il commença donc à mettre au point une fonction pseudo-aléatoire de bruit qui remplit les trois dimensions de l'espace[2],[3], avant d'inventer l'année suivante le premier langage de shading (en)[4]. Ses travaux sur les textures procédurales ont valu à Ken Perlin l'Academy Award for Technical Achievement (en) en 1997[5].
Algorithme
[modifier | modifier le code]Le bruit de Perlin est le plus couramment utilisé à deux, trois, voire quatre dimensions, il peut être défini pour un nombre quelconque de dimensions. L'algorithme se décompose en trois étapes :
- Définition de la grille avec des vecteurs de gradient aléatoires
- Calcul du produit scalaire entre le vecteur de gradient et le vecteur distance
- Interpolation entre ces valeurs
Définition de la grille
[modifier | modifier le code]Définir une grille à n dimensions. À chaque intersection de la grille (ou sommet), attribuer un vecteur de gradient unitaire, de direction aléatoire et de dimension n.
Par exemple :
- Pour une grille à deux dimensions (n = 2), on assigne à chaque sommet un vecteur aléatoire du cercle unité.
- Pour une grille à une dimension (n = 1), on assigne à chaque sommet un nombre aléatoire entre -1 et 1.
Le calcul des gradients (pseudo)-aléatoires en une et deux dimensions est trivial en utilisant un générateur de nombres aléatoires. Pour les dimensions supérieures, une approche Monte Carlo peut être utilisée où les coordonnées cartésiennes aléatoires sont choisies dans un cube unité, les points situés en dehors de la sphère unité sont rejetés et les points restants sont normés.
Produit scalaire
[modifier | modifier le code]Pour calculer la valeur du bruit en un point P donné, déterminer dans quelle cellule de la grille se trouve P. Pour chaque sommet de cette cellule, calculer le vecteur de distance entre P et le sommet. Calculer le produit scalaire entre le vecteur de gradient au sommet et le vecteur de distance.
Pour un point dans une grille à deux dimensions, il faut calculer 4 vecteurs de distance et 4 produits scalaires, tandis que dans une grille à trois dimensions, il faut calculer 8 vecteurs de distance et 8 produits scalaires. Ceci conduit à une complexité algorithmique .
Interpolation
[modifier | modifier le code]La dernière étape est l'interpolation entre les produits scalaires calculés aux sommets de la cellule contenant P. Cela a pour conséquence que la fonction de bruit renvoie 0 pour les sommets de la grille.
L'interpolation est effectuée en utilisant une fonction dont la dérivée première (et éventuellement la dérivée seconde) est nulle aux nœuds de la grille . Cela a pour effet que le gradient de la fonction de bruit résultante à chaque nœud de grille coïncide avec le vecteur de gradient aléatoire précalculé. Par exemple, si , un exemple de fonction qui interpole entre la valeur au nœud de grille 0 et la valeur au nœud de grille 1 est
où la fonction smoothstep (en) a été utilisée.
Les fonctions de bruit utilisées dans l'infographie produisent généralement des valeurs comprises dans l'intervalle [-1.0,1.0]. Afin de produire du bruit Perlin dans cet intervalle, la valeur interpolée peut avoir besoin d'être mise à l'échelle par un facteur d'échelle.
Pseudo-code
[modifier | modifier le code]Voici le pseudo-code de l'implémentation du bruit de Perlin dans un espace à deux dimensions.
// Fonction lisse de l'intervalle [0.0, 1.0] vers lui-même function smoothstep(float w) { if (w <= 0.0) return 0.0; if (w >= 1.0) return 1.0; return w * w * (3.0 - 2.0 * w); } // Fonction d'interpolation lisse entre a0 et a1 // Le poids w doit être dans l'intervalle [0.0, 1.0] function interpolate(float a0, float a1, float w) { return a0 + (a1 - a0) * smoothstep(w); } // Fonction de calcul du produit scalaire entre le vecteur de distance et le vecteur de gradient. function dotGridGradient(int ix, int iy, float x, float y) { // Vecteurs de gradient aux intersections de la grille (pré-calculés ou non) extern float Gradient[IYMAX][IXMAX][2]; // Calcul du vecteur de distance float dx = x - (float)ix; float dy = y - (float)iy; // Calcul du produit scalaire return (dx*Gradient[iy][ix][0] + dy*Gradient[iy][ix][1]); } // Calcul du bruit de Perlin au point de coordonnées (x, y) function perlin(float x, float y) { // Coordonnées de la case de la grille dans laquelle se trouve le point int x0 = floor(x); int x1 = x0 + 1; int y0 = floor(y); int y1 = y0 + 1; // Poids pour l'interpolation // (On pourrait utiliser des polynômes d'ordre supérieur ou d'autres fonctions lisses) float sx = x - (float)x0; float sy = y - (float)y0; // Interpolation entre les coins de la case float n0, n1, ix0, ix1, value; n0 = dotGridGradient(x0, y0, x, y); n1 = dotGridGradient(x1, y0, x, y); ix0 = interpolate(n0, n1, sx); n0 = dotGridGradient(x0, y1, x, y); n1 = dotGridGradient(x1, y1, x, y); ix1 = interpolate(n0, n1, sx); value = interpolate(ix0, ix1, sy); return value; }
Annexes
[modifier | modifier le code]Notes et références
[modifier | modifier le code]- ↑ (en) Ken Perlin, « History », sur www.noisemachine.com (consulté le ).
- ↑ (en) Ken Perlin, « Controlled random primitive », sur www.noisemachine.com (consulté le ).
- ↑ (en) Ken Perlin, « coherent noise function over 1, 2 or 3 dimensions », sur nyu.edu (consulté le ). Code (en C) de la première version de la fonction, en 1983.
- ↑ (en) Ken Perlin, « Rapid adoption », sur www.noisemachine.com (consulté le ).
- ↑ (en) Ken Perlin, « Noise and Turbulence », sur nyu.edu (consulté le ).