Quelle est l'origine de ce GLSL rand() one-liner?
J'ai vu ce générateur de nombres pseudo-aléatoires à utiliser dans les shaders référencés à ici et là sur le web :
float rand(vec2 co){
return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}
Il est diversement appelé "canonique", ou "un one-liner que j'ai trouvé sur le web quelque part".
Quelle est l'origine de cette fonction? Les valeurs constantes sont-elles aussi arbitraires qu'elles semblent ou y a-t-il de l'art dans leur sélection? Y a-t-il une discussion sur les mérites de cette fonction?
EDIT: la référence la plus ancienne à cette fonction que j'ai rencontrée est cette archive de Feb ' 08 , la page d'origine étant maintenant partie du web. Mais il n'y a pas plus de discussion là-bas qu'ailleurs.
4 réponses
Question très intéressante!
J'essaie de comprendre cela en tapant la réponse :) D'abord un moyen facile de jouer avec elle: http://www.wolframalpha.com/input/?i=plot%28+mod%28+sin%28x*12.9898+%2B+y*78.233%29+*+43758.5453%2C1%29x%3D0..2%2C+y%3D0..2%29
Alors réfléchissons à ce que nous essayons de faire ici: pour deux coordonnées d'entrée x, y nous retournons un "nombre aléatoire". Maintenant, ce n'est pas un nombre aléatoire. C'est la même chose chaque fois que nous entrons le même x, Y. c'est un hachage la fonction!
La première chose que fait la fonction est de passer de 2d à 1d. ce n'est pas intéressant en soi, mais les nombres sont choisis de sorte qu'ils ne se répètent pas typiquement. Nous avons aussi un ajout à virgule flottante là-bas. Il y aura quelques bits de plus de y ou x, mais les nombres pourraient juste être choisis correctement, donc il fait un mélange.
Ensuite, nous échantillonnons une fonction Black box sin (). Cela dépendra beaucoup de la mise en œuvre!
Enfin, il amplifie l'erreur dans l'implémentation de sin () par multiplier et prendre la fraction.
Je ne pense pas que ce soit une bonne fonction de hachage dans le cas général. Le sin () est une boîte noire, sur le GPU, numériquement. Il devrait être possible d'en construire un bien meilleur en prenant presque n'importe quelle fonction de hachage et en le convertissant. La partie difficile est de transformer l'opération entière typique utilisée dans le hachage du processeur en opérations flottantes (demi ou 32 bits) ou à points fixes, mais cela devrait être possible.
Encore une fois, le vrai problème avec ceci en tant que fonction de hachage est que sin() est une boîte noire.
L'origine est probablement le livre: "Sur la génération de nombres aléatoires, avec l'aide de y= [(a+x)sin(bx)] mod 1", W. J. J. Rey, 22e Réunion Européenne des Statisticiens et des le 7e de la Conférence de Vilnius sur la Théorie des Probabilités et Statistique Mathématique, août 1998
EDIT: puisque je ne trouve pas de copie de cet article et que la référence" TestU01 " peut ne pas être claire, voici le schéma décrit dans TestU01 dans pseudo-C:
#define A1 ???
#define A2 ???
#define B1 pi*(sqrt(5.0)-1)/2
#define B2 ???
uint32_t n; // position in the stream
double next() {
double t = fract(A1 * sin(B1*n));
double u = fract((A2+t) * sin(B2*t));
n++;
return u;
}
Où la seule valeur constante recommandée est B1.
Notez que c'est pour un flux. La conversion en un hachage 1D ' n ' devient la grille entière. Donc, je suppose que quelqu'un a vu cela et a converti 't' en une simple fonction f(x,y). En utilisant les constantes d'origine ci-dessus qui donneraient:
float hash(vec2 co){
float t = 12.9898*co.x + 78.233*co.y;
return fract((A2+t) * sin(t)); // any B2 is folded into 't' computation
}
Les valeurs constantes sont arbitraires, surtout qu'elles sont très grandes, et à quelques décimales des nombres premiers.
Un module sur 1 d'un sinus d'amplitude hi multiplié par 4000 est une fonction périodique. c'est comme un store de fenêtre ou un métal ondulé fait très petit parce qu'il est multiplié par 4000, et tourné à un angle par le produit dot.
Comme la fonction est 2-D, le produit dot a pour effet de tourner la fonction périodique à un oblique par rapport à X et Y axe. Par rapport 13/79 environ. C'est inefficace, vous pouvez réellement atteindre la même chose en faisant sinus de (13x + 79y) cela permettra également d'atteindre la même chose que je pense avec moins de maths..
Si vous trouvez la période de la fonction dans X et Y, Vous pouvez l'échantillonner pour qu'elle ressemble à une simple onde sinusoïdale.
Voici une photo zoomée graphe
Je ne connais pas l'origine mais elle est similaire à beaucoup d'autres, si vous l'avez utilisée dans les graphiques à intervalles réguliers il aurait tendance à produire des motifs moiré et vous pourriez voir qu'il est finalement va à nouveau.
Peut-être que c'est une cartographie chaotique non récurrente, alors cela pourrait expliquer beaucoup de choses, mais peut aussi être juste une manipulation arbitraire avec de grands nombres.
EDIT: fondamentalement, la fonction fract (sin (x) * 43758.5453) est une simple fonction de hachage, le sin(x) fournit une interpolation sin lisse entre -1 et 1, donc sin (x) * 43758.5453 sera interpolation de -43758.5453 à 43758.5453. C'est une gamme assez énorme, donc même une petite étape dans x fournira une grande étape dans le résultat et vraiment grande variation dans la partie fractionnaire. Le "fract" est nécessaire pour obtenir des valeurs dans la plage -0,99... à 0.999... . Maintenant, quand nous avons quelque chose comme la fonction de hachage, nous devrions créer une fonction pour le hachage de production à partir du vecteur. Le moyen le plus simple est d'appeler "hash" séparément pour x n'importe quel composant y du vecteur d'entrée. Mais alors, nous aurons des valeurs symétriques. Donc, nous devrions obtenir une valeur du vecteur, l'approche est de trouver certains vecteur aléatoire et de trouver des "points" produit vectoriel, ici nous allons: fract(sin(point(co.xy, vec2 (12,9898,78,233))) * 43758,5453); En outre, selon le vecteur sélectionné, sa longueur devrait être longue pour avoir plusieurs péroïdes de la fonction" sin "après que le produit" dot " sera calculé.