Munus

background

「リアルタイムグラフィックスの数学」勉強ログ - 第2章疑似乱数

「リアルタイムグラフィックスの数学」勉強ログ - 第2章疑似乱数
目次

はじめに#

「リアルタイムグラフィックスの数学」の第 2 章の疑似乱数についての勉強ログです。

レガシー乱数#

GLSL ES 1.0 ではビット演算が使えないため、ハッシュ関数ではなくサイン関数を使用した乱数生成が行われていました。The Book of Shaders ではサイン関数を使用した乱数生成の方法が紹介されています。

The Book of Shaders
Gentle step-by-step guide through the abstract and complex universe of Fragment Shaders.
The Book of Shaders favicon
thebookofshaders.com
The Book of Shaders

サイン関数自体には乱数性はないですが、サイン関数の値に大きい値をかけて桁を上げ、fractで小数部分を取り出すことで乱数のように見えることができます。

レガシー乱数の 1 変数と 2 変数のコードは次のようになります。

レガシー乱数
// 1変数のレガシー乱数
float fractSin11(float x) {
  return fract(1000.0 * sin(x));
}
 
// 2変数のレガシー乱数
float fractSin21(vec2 xy) {
  return fract(sin(dot(xy, vec2(12.9898, 78.233))) * 43758.5453123);
}

結果は下図のようになります。

レガシー乱数の 1 変数と 2 変数の結果
レガシー乱数の 1 変数と 2 変数の結果

2 変数のレガシー乱数で使用しているマジックナンバーは、GLSL で伝統的によく使用されている値になります。

符号なし整数の可視化#

サンプルコード 2.2 のコードを説明します。このサンプルでは 32 ビットの 2 進数を白黒で可視化しています。ここで 「特定のビットを取り出し」0 か 1 かを判定しているコードは次のようになってます。

b = (b << uint(pos.x)) >> 31;

分かりやすく考えるために、10 進数で 9 の場合を例としてみましょう。
10 進数で 9 を 32 ビットで表現すると次のようになります。

b = 0000 0000 0000 0000 0000 0000 0000 1001

4 ビット目の 1 を取得する場合を考えてみましょう。まず314=2731 - 4 = 27なので 27 ビット分を左シフトします。

b = 0000 0000 0000 0000 0000 0000 0000 1001
b << 27 // 31 - 4
b = 1001 0000 0000 0000 0000 0000 0000 0000

これを 31 ビット分を右シフトすることで 4 ビット目の 1 が取得できます。

b = 1001 0000 0000 0000 0000 0000 0000 0000
b >> 31
b = 0000 0000 0000 0000 0000 0000 0000 0001

サンプルコードではフラグメント範囲の x 座標を 32 に拡大しているので、uint(pos.x)を取ることで 32 ビットの 2 進数を白黒で可視化することができています。

浮動小数点数のビット列#

符号なし整数(uint, unsigned integer)はビット列をそのまま 2 進数変換することで得られます。ですが、負の数や小数部分を含む浮動小数点の場合は、ビット列から数値への変換は自明ではないです。32 ビット浮動小数点は IEEE754 と呼ばれる標準規格があり、highp 精度ではこれに準拠して変換されます。

IEEE754 では、32 ビットの情報をb0...b31b_0...b_{31}の 2 進数で表したとき、符号部b0b_0、指数部b1...b8b_1...b_8、仮数部b9...b31b_9...b_{31}のように分けて、c=b1...b8c = b_1...b_8としたときに次のように計算します。

(1)b0×2c127×1.b9...b31(-1)^{b_0} \times 2^{c-127} \times 1.b_9...b_{31}

書籍の例として 11.5625 の浮動小数点を符号なし整数に変換してみましょう。
整数部分の 11 を 2 進数に変換すると 1011 になります。小数部分の 0.5625 を 2 進数に変換すると 0.1001 になります。これを計算式に合うように整数部分に 1 が来るように232^3かけて右にずらします。

11.5625=1011.1001=1.0111001×2311.5625 = 1011.1001 = 1.0111001 \times 2^3

この01110010...001110010...0が仮数部になります。符号部に関しては、正の数なのでb0b_000 になります。

最後に指数部に関しては、c127=3c - 127 = 3なのでc=130c=130になり、130130を 2 進数で表すと1000001010000010になるのでこの値が指数部になります。

結果として、11.5625 の符号なし整数は下記になります。

0 10000010 0111001...0

GLSL ではビット列としての浮動小数点を符号なし整数に変換する関数として、floatBitsToUint関数が組み込まれています。

ハッシュ関数#

ハッシュ関数による乱数生成法については、難しかったので機会があれば調べる予定。

書籍で紹介されていた、Jarzynski-Olano の約 30 ものハッシュ関数についてのコストパフォマンスについての論文Hash Functions for GPU Rendering - Jarzynski-Olano

Shadertoy での実装についてはこちら

後で詳しく調べるものリスト#

参考書籍#

PR