uniformSphere
#ベクトル #乱数 #数学 #シェーダー
単位球表面に一様分布する点を生成する
一様分布に従う乱数 $ u_1・$ u_2を用意し、
$ \phi = 2 \pi u_1
$ \cos(\theta) = 1 - 2 u_2
$ \sin(\theta) = \sqrt{1 - \cos^2(\theta)}
$ \vec v = \left( \begin{matrix} \sin(\theta) \cos(\phi) \\ \sin(\theta) \sin(\phi) \\ \cos(\theta) \end{matrix} \right)
$ \theta の値域を$ [0, \pi] ととるか$ \left[ -\frac{\pi}{2}, \frac{\pi}{2} \right] ととるかで$ \sin(\theta)と$ \cos(\theta)が入れ替わるが、どっちでも良い
上の式は$ \left[ 0, \pi \right] で考えてるほう
Z+が基準と考え、Z+を向いていれば向いているほど$ \cos (\theta)がデカい
球内部
さらに、球内部にも一様に分布させたい場合は、
もうひとつ一様乱数$ u_3を用意し、それの立方根をかけてやる
$ r = \sqrt[3]{u_3}
$ \vec v' = r \vec v
Observable
Observable (Webサービス)
https://gyazo.com/88b793e680db4b67bcc1999b83bf7fc7
https://observablehq.com/d/e686a6be6ecfa881
GLSL
code:glsl
vec3 uniformSphere(vec2 xi) {
float phi = xi.x * TAU;
float cosTheta = 1.0 - 2.0 * xi.y;
float sinTheta = sqrt(1.0 - cosTheta * cosTheta);
return vec3(
sinTheta * cos(phi),
sinTheta * sin(phi),
cosTheta
);
}
考え方
$ \theta を$ \left[ 0, \pi \right] の一様分布にしちゃうと、極付近に分布が偏っちゃう
ある$ \thetaが与えられたときに$ \phiを連続的に動かしていって、円周の長さがどうなるかを考えると、
$ \theta = \pi/2のときは円周の長さが$ 2 \piなのに対して、$ \thetaを$ 0(あるいは$ \pi)へと近づけていくにつれて円周の長さは短くなっていく
$ r = 2 \pi \sin(\theta)ですねえ
確率密度関数で表現すると$ f(\theta) = \frac{1}{2} \sin(\theta)になってほしい
累積分布関数にすると、$ F(\theta) = \frac{1}{2} \cos(\theta) + \frac{1}{2}
逆関数法を使って、$ U = \frac{1}{2} \cos(\hat{\Theta}) + \frac{1}{2}となってくれれば良い
じゃあ$ \cos(\theta) を$ [-1, 1] で作ってやればいいんじゃね?
(実は$ \thetaを直接求める必要がありません、なんと)
さらに、$ \sin(\theta) = \sqrt{1 - \cos^2(\theta)}と求められる
オケー
球内部
球の面積が$ 4 \pi r^2なので、
確率密度関数で$ f(r) = 3 r^2になってほしい
累積分布関数にすると、$ F(\theta) = r^3
逆関数法を使って、$ U = \hat R^3 を満たすような$ \hat R は$ \sqrt[3]{u}
オケー
sinθ? cosθ?
$ \phiをZ軸周りに回すんだから、Z軸と平行に$ 2x-1は引かなきゃじゃね?
球体に向けてX軸側 or Y軸側からスプレーを照射すると、極付近はあまり塗れず、赤道付近はたっぷり塗れる
$ \thetaが$ \pi/2のときに、たくさんサンプルがあってほしい。逆に、$ \thetaが$ 0や$ \piのときは、あまりサンプルがあってほしくない
Blackleの手法
Source: Useful Functions for Shader Live Coding
code:glsl
vec3 uniformSphere(vec3 xi) {
return normalize(tan(xi * 2.0 - 1.0));
}
tan が$ [-1, 1] の範囲でGaussian Distributionの累積分布関数の逆関数に近いことを利用
正確ではないが、ライブコーディング文脈だとモンテカルロ積分とかをしない限りは全然これで良い
https://gyazo.com/1dadca060a85fdbe54446a9eabff21fc
https://www.desmos.com/calculator/ylrnegbkz8?lang=ja
もっと正確な値が欲しい場合、Box-Muller Transformを使うと良い