球面均匀采样
球面均匀采样:为什么是 cos(\theta)=1-2v
来源:UE4
MonteCarlo.ush中的UniformSphereSample,用于 PRT/Surfel 探针采样时在球面上均匀地撒方向。
前言
在图形学里,"在球面上均匀采样一个方向"是最基础的操作之一:环境光积分、球谐函数拟合、Surfel 分布、蒙特卡洛估计……都离不开它。
UE4 的 MonteCarlo.ush 给出的标准实现非常短:
float3 UniformSphereSample(float u, float v)
{
const float C_PI = 3.14159265359f;
float phi = 2.0 * C_PI * u;
float cosine_theta = 1.0 - 2.0 * v;
float sine_theta = sqrt(1.0 - cosine_theta * cosine_theta);
float x = sine_theta * cos(phi);
float y = sine_theta * sin(phi);
float z = cosine_theta;
return float3(x, y, z);
}
最容易让人困惑的是这一行:
float cosine_theta = 1.0 - 2.0 * v;
为什么不是 \theta=\pi v,而是先算出一个"余弦"?本文从数学上把这件事讲清楚。
先明确目标:什么叫"均匀"
球面上均匀,指的是:落在任一小片曲面上的概率,等于该片面积占球总面积(4\pi)的比例:
关键词是面积,不是角度。这一点是全部推导的源头——很多人直觉上想让极角 \theta 均匀分布,但那不是面积均匀,后面会看到它会导致采样点在两极堆积。
第一步:球面面积微元
对球面做参数化,\theta 为极角,从北极 0 到南极 \pi;\varphi 为方位角,从 0 到 2\pi:
在 (\theta, \varphi) 处取一小块:\theta 方向宽 d\theta,\varphi 方向宽 d\varphi。这一小块到竖直转轴的距离(即所在纬度环的半径)是 r=sin(\theta),于是这块的"横向弧长"是 sin(\theta)d\varphi,"纵向弧长"是 d\theta:
注意 sin(\theta) 这个因子:赤道处(\theta=\frac{\pi}{2})环带最宽,两极附近(\theta\rightarrow 0 或 \pi)环带收缩成一个点。同样大小的 d\theta,在不同纬度对应的真实面积完全不同。
第二步:\theta 的概率密度必须是 sin(\theta)/2
我们要的是 \theta 的边缘分布。把面积微元代进均匀定义,再对 \varphi 积分(\varphi 从 0 到 2\pi):
所以:
为什么必须是它? 因为只有当 p(\theta)\propto sin(\theta) 时,概率密度里的 sin(\theta) 才会和面积微元里的 sin(\theta) 相消,使单位面积的概率密度处处等于常数 \frac{1}{4\pi}——这才是球面均匀。
验证归一化(概率为1):
反面教材:如果天真地取 \theta=\pi v(即 p(\theta)=\frac{1}{\pi},\theta 均匀),那么单位面积的概率密度为:
两极处面积趋近于零、概率却不衰减,采样点全部涌向两极。采样出来的方向分布会明显"扎堆",任何依赖均匀性的积分估计都会有系统性偏差。
第三步:逆变换采样(Inverse Transform Sampling)
现在已知 \theta 应服从密度 p(\theta)=\frac{sin(\theta)}{2},如何用 [0,1] 均匀随机数 v 生成它?标准答案是逆变换采样。
求累积分布函数(CDF):
令 F(\theta)=v,反解出 \theta:
这正是那一行代码。而且代码里不需要真的算 \theta=arccos(1-2v)——因为后面只会用到:
保留 cos(\theta) 直接参与运算即可,省掉一次反三角函数。
而方位角 \varphi 没有这个问题:绕轴旋转的环带面积与 \varphi 无关,\varphi 天然均匀,直接取:
一个漂亮的直觉:z 均匀 \Leftrightarrow 面积均匀
cos(\theta) 不是别的,正是方向向量的 z 坐标。所以 cos(\theta)=1-2v 等价于:
让 z 在 [-1, 1] 上均匀取值。
为什么这样就均匀了?想象用一组水平平面以等间距 dz 把球切成薄片——高度 z 处那片的球面面积是:
环带半径 sin(\theta) 乘上周长 2\pi,恰好与 z 无关。球面在 z 轴上的"投影"是均匀的:每一层等厚的球壳切片面积完全相同。所以 z 均匀分布 \Leftrightarrow 面积均匀分布。这也是阿基米德球冠定理的现代版:球冠面积只取决于它的高,不取决于它的张角。
回到完整代码
把上面三步合起来看 CSMain 的调用链:
// [0,1] 均匀随机数(两路独立噪声)
float u = rand(xy * 1.0);
float v = rand(xy * 2.0);
// u → 方位角 φ(天然均匀)
// v → 极角余弦 cosθ(逆变换采样,补偿 sinθ 面积权重)
float3 dir = UniformSphereSample(u, v);
u 和 v 地位并不对称:u 直接映射角度,v 必须先经过 \frac{1-cos(\theta)}{2} 这个 CDF 的"整形",才能抵消球面面积随纬度的变化。
总结
| 量 | 分布 | 原因 |
|---|---|---|
| \varphi(方位角) | 均匀于[0, 2\pi] | 环带面积与\varphi 无关 |
| \theta(极角) | 密度\frac{sin(\theta)}{2} | 环带面积\propto sin(\theta) |
| cos(\theta)(即 z) | 均匀于[-1, 1] | 等厚切片面积恒为2\pi dz |
一句话记住:
球面均匀采样 = 方位角均匀 + 极角余弦均匀。 cos(\theta)=1-2v 就是逆变换采样对 p(\theta)=\frac{sin(\theta)}{2} 求逆后的解析解。
参考
- Unreal Engine 4,
MonteCarlo.ush—UniformSphereSample - PBRT (Physically Based Rendering), 第 13 章 — Monte Carlo Integration / Transforming between distributions
- Archimedes' Hat-Box Theorem — 球冠面积与 z 的线性关系