在单位半球的表面上快速均匀分布的随机点

cub*_*war 21 c++ random math geometry

我试图在单位球体的表面上生成均匀的随机点,用于蒙特卡罗射线追踪程序.当我说均匀时,我的意思是这些点相对于表面积均匀分布.我目前的方法是计算指向正z轴并基于xy平面的半球上的均匀随机点.

半球上的随机点表示漫射灰色发射器的热辐射发射方向.

当我使用以下计算时,我获得了正确的结果:

注意:dsfmt*将返回0到1之间的随机数.

azimuthal = 2*PI*dsfmt_genrand_close_open(&dsfmtt);
zenith = asin(sqrt(dsfmt_genrand_close_open(&dsfmtt)));

// Calculate the cartesian point
osRay.c._x = sin(zenith)*cos(azimuthal); 
osRay.c._y = sin(zenith)*sin(azimuthal);
osRay.c._z = cos(zenith);
Run Code Online (Sandbox Code Playgroud)

然而,这是非常缓慢的,并且分析表明它占用了大部分的运行时间.因此,我找到了一些替代方法:

Marsaglia 1972年的拒绝方法

do {
   x1 = 2.0*dsfmt_genrand_open_open(&dsfmtt)-1.0;
   x2 = 2.0*dsfmt_genrand_open_open(&dsfmtt)-1.0;
   S = x1*x1 + x2*x2;
} while(S > 1.0f);


osRay.c._x = 2.0*x1*sqrt(1.0-S);
osRay.c._y = 2.0*x2*sqrt(1.0-S);
osRay.c._z = abs(1.0-2.0*S);
Run Code Online (Sandbox Code Playgroud)

分析笛卡尔坐标计算

azimuthal = 2*PI*dsfmt_genrand_close_open(&dsfmtt);
u = 2*dsfmt_genrand_close_open(&dsfmtt) -1;
w = sqrt(1-u*u);

osRay.c._x = w*cos(azimuthal);
osRay.c._y = w*sin(azimuthal);
osRay.c._z = abs(u);
Run Code Online (Sandbox Code Playgroud)

虽然最后两种方法比第一种方法运行的时间更快,但当我使用它们时,我得到的结果表明它们不是在球体表面上产生均匀的随机点,而是给出了有利于赤道的分布.

此外,最后两种方法给出相同的最终结果,但我确定它们是不正确的,因为我正在与分析解决方案进行比较.

我发现的每个参考都表明这些方法确实产生了均匀的分布但是我没有达到正确的结果.

我的实现中是否有错误,或者我在第二和第三种方法中错过了一个基本想法?

Ale*_* C. 15

在单位球上生成均匀分布的最简单方法(无论其大小是什么)是绘制独立的正态分布并对结果向量进行归一化.

实际上,例如在维度3中,e ^( - x ^ 2/2)e ^( - y ^ 2/2)e ^( - z ^ 2/2)= e ^( - (x ^ 2 + y ^ 2 + z ^ 2)/ 2)因此联合分布通过旋转是不变的.

如果你使用快速正态分布生成器(Ziggurat或比例制服)和快速归一化程序(google为"快速反平方根"),这是很快的.不需要超越函数调用.

而且,Marsaglia在半球上并不均匀.你将在赤道附近有更多的点,因为半球上的2D光盘上的对应点不是等距的.最后一个似乎是正确的(但我没有进行计算以确保这一点).