web-dev-qa-db-fra.com

Quelle est l'origine de cette doublure GLSL Rand ()?

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é quelque part sur le web".

Quelle est l'origine de cette fonction? Les valeurs constantes sont-elles aussi arbitraires qu'elles le paraissent ou y a-t-il un art dans leur sélection? Y a-t-il une discussion sur les mérites de cette fonction?

EDIT: La plus ancienne référence à cette fonction que j'ai rencontrée est cette archive de février 08 , la page d'origine étant maintenant partie du Web. Mais il n'y a pas plus de discussion là-bas que partout ailleurs.

84
Grumdrig

Question très intéressante!

J'essaie de comprendre cela en tapant la réponse :) Premièrement, un moyen facile de jouer avec: 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

Réfléchissons ensuite à ce que nous essayons de faire ici: pour deux coordonnées d'entrée x, y nous renvoyons un "nombre aléatoire". Maintenant, ce n'est pas un nombre aléatoire. C'est la même chose chaque fois que nous entrons les mêmes x, y. C'est une fonction de hachage!

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 généralement pas. Nous avons également un ajout en virgule flottante. Il y aura quelques bits de plus à partir de y ou x, mais les nombres pourraient juste être choisis correctement pour qu'il fasse un mélange.

Ensuite, nous échantillonnons une fonction de boîte noire sin (). Cela dépendra beaucoup de la mise en œuvre!

Enfin, il amplifie l'erreur dans l'implémentation sin () en multipliant et en prenant 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 une bien meilleure en prenant presque n'importe quelle fonction de hachage et en la convertissant. La partie difficile est de transformer l'opération d'entier typique utilisée dans le hachage de processeurs en opérations flottantes (demi ou 32 bits) ou en points fixes, mais cela devrait être possible.

Encore une fois, le vrai problème avec cela en tant que fonction de hachage est que sin () est une boîte noire.

39
starmole

L'origine est probablement le papier: "Sur la génération de nombres aléatoires, à l'aide de y = [(a + x) sin (bx)] mod 1", W.J.J. Rey, 22e réunion européenne des statisticiens et 7e conférence de Vilnius sur la théorie des probabilités et les statistiques mathématiques, août 1998

EDIT: Étant donné que je ne trouve pas de copie de ce document et que la référence "TestU01" n'est peut-être pas claire, voici le schéma décrit dans TestU01 en 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 le 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 converti 't' en une simple fonction f (x, y). L'utilisation des constantes d'origine ci-dessus donnerait:

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
}
18
MB Reynolds

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 élevée 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 car il est multiplié par 4000 et tourné en biais par le produit scalaire.

comme la fonction est 2D, le produit scalaire a pour effet de tourner la fonction périodique en oblique par rapport aux axes X et Y. Par rapport 13/79 environ. C'est inefficace, vous pouvez réellement obtenir la même chose en faisant des sinus de (13x + 79y), cela permettra également d'obtenir la même chose, je pense, avec moins de maths ..

Si vous trouvez la période de la fonction à la fois dans X et Y, vous pouvez l'échantillonner pour qu'elle ressemble à nouveau à une simple onde sinusoïdale.

En voici une photo agrandie graphique

Je ne connais pas l'Origine mais il est similaire à beaucoup d'autres, si vous l'utilisiez dans des graphiques à intervalles réguliers, il aurait tendance à produire des motifs moirés et vous pourriez voir qu'il finit par recommencer.

8
com.prehensible

C'est peut-être 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 de péché fluide entre -1 et 1, donc sin (x) * 43758.5453 sera une interpolation de - 43758.5453 à 43758.5453. Il s'agit d'une plage assez vaste, donc même un petit pas en x fournira un grand pas en résultat et une très grande variation dans la partie fractionnaire. La "fracture" est nécessaire pour obtenir des valeurs comprises entre -0,99 ... et 0,999 .... Maintenant, quand nous avons quelque chose comme la fonction de hachage, nous devons créer une fonction pour la production de hachage à partir du vecteur. La manière la plus simple est d'appeler "hachage" séparément pour x toute composante y du vecteur d'entrée. Mais alors, nous aurons des valeurs symétriques. Donc, nous devrions obtenir une valeur du vecteur, l'approche consiste à trouver un vecteur aléatoire et à trouver un produit "point" vers ce vecteur, nous y voilà: fract (sin (dot (co.xy, vec2 (12.9898,78.233)))) * 43758,5453); De plus, selon le vecteur sélectionné, sa longueur devrait être longue pour avoir plusieurs peroïdes de la fonction "sin" après que le produit "dot" sera calculé.

1
Roman