这是这个想法的粗略草图。您选择一个象限进行采样,例如右侧的一个。
首先,从 -pi/4 到 pi/4 采样角度
float a = -MathUtils.PI/4.0f + MathUtils.PI/2.0 * MathUtils.random(0.f,1.f);
float c = MathUtils.cos(a);
float s = MathUtils.sin(a);
其次,求最小半径。从 (0,0) 以角度 a 射出的光线将与象限线在 x=1 处相交至少
float rmin = 1.0f / c;
float rmax = Math.sqrt(2.0f);
从rmin 到rmax = sqrt(2) 的样本,考虑到对于平面,您对半径的平方进行采样,然后使用 sqrt(),对于 3d 空间,您对半径的立方进行采样,然后使用 cbrt()。
float r2 = rmin*rmin + (rmax*rmax-rmin*rmin)*MathUtils.random(0.f,1.f);
float r = Math.sqrt(r);
float x = r * c;
float y = r * s;
现在,我们构造的 (x,y) 是这样一种方式,它保证在圆下方的右象限和 x=1 线的右侧。
要覆盖所有四个象限,只需对要将点移动到哪个象限进行采样
float q = MathUtils.random(0.f,1.f);
if (q < 0.25f) // top quadrant
return (y, x);
if (q < 0.5f) // left quadrant
return (-x, y);
if (q < 0.75f) // bottom quadrant
return (y, -x);
return (x,y); // right quadrant
请多多包涵 - 我的 Java 已经很生疏了,我没有办法测试代码。
在 3D 情况下,您必须处理两个角度、立方半径、八个八分圆而不是四个象限,但一般逻辑是相同的
更新
我错了,我建议的采样会导致点分布不均匀。
来自 PDF:
PDF(phi, r) = S_(-\pi/4)^\phi d\phi S_1/cos(\phi)^\sqrt(2) r dr
我们可以得到我们必须使 \phi 采样不均匀。不幸的是,从
U(0,1) 到采样 \phi 需要求解非线性方程
\pi/2 (0.5*(\phi/\pi/4 + 1) - U(0,1)) = 0.5*(tan(phi) + 1) - U(0,1)
所以算法是:
- 样本 U(0,1)
- 通过求解上述方程找到合适的 \phi
- 找到较低的
R边界
- 样本R
绘制这个非线性函数的快速代码(抱歉,用 Python 编写)
import numpy as np
import matplotlib.pyplot as plt
def f(phi, ksi):
c = 0.5 * np.pi
c_2 = 0.5 * c
left = c * (0.5 * (phi/c_2 + 1.0) - ksi)
rght = (0.5 * (np.tan(phi) + 1.0) - ksi)
return left - rght
nof_points = 41
phi = np.linspace(-0.25*np.pi, 0.25*np.pi, nof_points)
y0_00 = f(phi, 0.00)
y0_25 = f(phi, 0.25)
y0_50 = f(phi, 0.50)
y0_75 = f(phi, 0.75)
y0_99 = f(phi, 1.00)
plt.plot(phi, y0_00, 'ro', phi, y0_25, 'b+', phi, y0_50, 'gx', phi, y0_75, 'm.', phi, y0_99, 'y^')
plt.show()
并为 U(0,1) 的五个值绘制函数(代码中的 ksi)
可以重新安排采样,使r 采样是非线性的,但它表现出相同的问题 - 需要求解具有多项式和三角函数部分的非线性方程
更新二
为了记录,如果你想先采样r,那么它必须从非线性方程的解中采样:
r2 sec-1(r) - sqrt(r2 - 1) = U(0,1)*(\pi/2 - 1)
在区间 [1...sqrt(2)]
求解后发现采样r,\phi可以在r允许的区间内均匀采样:[-cos-1(1/r) ... + cos-1(1/r)]