web-dev-qa-db-ja.com

このGLSL Rand()ワンライナーの起源は何ですか?

ウェブのあちこち :と呼ばれるシェーダーで使用するためのこの擬似乱数ジェネレーターを見ました。

float Rand(vec2 co){
  return fract(sin(dot(co.xy ,vec2(12.9898,78.233))) * 43758.5453);
}

それはさまざまに「標準」または「どこかでWebで見つけたワンライナー」と呼ばれています。

この機能の起源は何ですか?定数値は、見た目と同じくらいarbitrary意的ですか、それとも選択に何か芸術がありますか?この機能のメリットについての議論はありますか?

編集:私が出会ったこの関数への最も古い参照は、 このアーカイブは2008年2月から であり、元のページは現在ウェブから消えています。しかし、それについての議論は他のどこにもありません。

84
Grumdrig

非常に興味深い質問です!

私は答えを入力するときにこれを理解しようとしています:)最初にそれを遊ぶ簡単な方法: 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

次に、ここで何をしようとしているのか考えてみましょう。2つの入力座標x、yに対して、「乱数」を返します。しかし、これは乱数ではありません。同じx、yを入力するたびに同じです。それはハッシュ関数です!

関数が最初に行うことは、2dから1dに移行することです。それ自体はおもしろいものではありませんが、通常は繰り返されないように数字が選択されます。また、浮動小数点が追加されています。 yまたはxからもう少しビットがありますが、数字が適切に選択されている場合があります。

次に、ブラックボックスのsin()関数をサンプリングします。これは実装に大きく依存します!

最後に、分数を乗算して取得することにより、sin()実装のエラーを増幅します。

これは一般的なケースでは良いハッシュ関数ではないと思います。 sin()は、GPU上の数値的にはブラックボックスです。ほとんどすべてのハッシュ関数を使用して変換することにより、はるかに優れたものを構築できるはずです。難しいのは、CPUハッシュで使用される一般的な整数演算を浮動小数点演算(ハーフまたは32ビット)または固定小数点演算に変換することですが、それは可能であるはずです。

繰り返しになりますが、ハッシュ関数としてのこれに関する実際の問題は、sin()がブラックボックスであることです。

39
starmole

Originはおそらく紙です: 「y = [(a + x)sin(bx)] mod 1の助けを借りて、乱数を生成する」、W.J.J。レイ、統計学者の第22回欧州会議、および確率論と数学統計に関する第7回ビリニュス会議、1998年8月

編集:私はこの論文のコピーを見つけることができず、「TestU01」の参照は明確ではないかもしれないので、擬似CのTestU01で説明されているスキームを以下に示します。

#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;
} 

ここで、唯一の推奨される定数値はB1です。

これはストリーム用です。 1Dハッシュ「n」に変換すると、整数グリッドになります。だから、誰かがこれを見て、「t」を単純な関数f(x、y)に変換したのではないかと思います。上記の元の定数を使用すると、次の結果が得られます。

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

定数値は任意であり、特に非常に大きく、素数から数桁離れています。

4000倍された高振幅正弦波の1を超える係数は、周期関数です。 4000倍になり、ドット積によって斜めに回転するため、窓のブラインドや非常に小さく作られた段ボールのようなものです。

関数は2次元であるため、ドット積は周期関数をXおよびY軸に対して斜めに回転させる効果があります。およそ13/79の比率で。それは非効率的で、(13x + 79y)の副鼻腔を行うことで実際に同じことを達成できます。

XとYの両方で関数の周期が見つかった場合は、単純な正弦波のように見えるようにサンプリングできます。

これがズームインされた写真です グラフ

Originはわかりませんが、他の多くのものと似ています。定期的にグラフィックスで使用すると、モアレパターンが生成される傾向があり、最終的には再び発生することがわかります。

8
com.prehensible

たぶん、それはいくつかの非再帰的なカオスマッピングであり、それは多くのことを説明することができますが、大きな数を使った単なる任意の操作でもあります。

編集:基本的に、関数fract(sin(x)* 43758.5453)は単純なハッシュのような関数であり、sin(x)は-1から1までの滑らかなsin補間を提供するため、sin(x)* 43758.5453は-から補間されます43758.5453から43758.5453。これは非常に大きな範囲であるため、xの小さなステップでも結果に大きなステップがあり、小数部に大きな変動があります。 -fractは、-0.99 ...から0.999 ...の範囲の値を取得するために必要です。ここで、ハッシュ関数のようなものがある場合、ベクトルから生成ハッシュの関数を作成する必要があります。最も簡単な方法は、入力ベクトルのx成分とy成分に対して個別に「ハッシュ」を呼び出すことです。しかし、その後、いくつかの対称的な値が得られます。そのため、ベクトルから値を取得する必要があります。アプローチは、ランダムなベクトルを見つけ、そのベクトルへの「ドット」積を見つけることです。ここで、fract(sin(dot(co.xy、vec2(12.9898,78.233))) * 43758.5453);また、選択されたベクトルによると、その長さは「ドット」積が計算された後に「sin」関数のいくつかのペロイドを持つために十分長くなければなりません。

1
Roman