[图形学]在半球面上均匀采样和cos加权采样

一、简介

本文介绍了如何在半球表面上进行半球面上的均匀采样半球面上的cos加权采样
在介绍半球面上的cos加权采样时,介绍了 在球面上采样(方法一)和在投影圆内采样(方法二)两种方法。
并且给出了相关公式推导和python代码实现。

二、在半球上采样

0.预备知识

1).球面坐标系与笛卡尔坐标系

在半球面上采样时,常使用球面坐标系。先采样球面坐标系下的坐标参数 ( θ , ϕ ) (\theta,\phi) (θ,ϕ),然后转换为笛卡尔坐标系参数 ( x , y , z ) (x,y,z) (x,y,z)。两个坐标系下的参数示意图如下:

笛卡尔坐标系示意图:
笛卡尔坐标系示意图

球面坐标系示意图:
球面坐标系
球面坐标系转为笛卡尔坐标系的公式如下:
x = c o s ( ϕ ) × s i n ( θ ) y = c o s ( θ )                  z = s i n ( ϕ ) × s i n ( θ ) (1) x = cos(\phi) \times sin(\theta) \\ y = cos(\theta) \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \ \\ z = sin(\phi) \times sin(\theta)\tag{1} x=cos(ϕ)×sin(θ)y=cos(θ)                z=sin(ϕ)×sin(θ)(1)
在单位球面坐标系上的立体角微元等于:
d ω = d A r 2 = d A (2) \rm{d}\omega = \frac{\rm{d}A}{r^2}=\rm{d}A \tag{2} dω=r2dA=dA(2)
在单位球面上的面积微元 d A \rm{d}A dA等于:
d A = r 2 × s i n ( θ ) d θ d ϕ = s i n ( θ ) d θ d ϕ (3) \rm{d}A=r^2\times sin(\theta)\rm{d}\theta\rm{d}\phi=sin(\theta)\rm{d}\theta\rm{d}\phi \tag{3} dA=r2×sin(θ)dθdϕ=sin(θ)dθdϕ(3)

2).逆变换采样

逆变换采样是伪随机数采样的一种基本方法,在已知任意概率分布函数(probability distribution function, PDF)累计分布函数(cumulative distribution function, CDF)时,可以从该PDF中生成随机样本。本文只简单介绍逆变换采样的步骤和示例,对其推导过程和原理不做过多介绍。

使用逆变换采样方法的步骤如下:
  • (1).根据已知的PDF函数 f ( x ) = y f(x)=y f(x)=y,计算对应的CDF函数 F ( X ) F(X) F(X)
  • (2).根据 CDF函数 F ( X ) F(X) F(X) 求出其反函数 F − 1 ( Y ) F^{-1}(Y) F1(Y)
  • (3).在[0,1]上均匀采样随机变量 u u u
  • (4).那么 F − 1 ( u ) F^{-1}(u) F1(u)即为满足PDF函数 f ( x ) = y f(x)=y f(x)=y的随机变量。
示例:

已知PDF函数为 f ( x ) = 1 2 ( x ) exp ⁡ ( − ( x ) ) , x ≥ 0 f(x)=\frac{1}{2\sqrt(x)}\exp(-\sqrt(x)), x\geq 0 f(x)=2( x)1exp(( x)),x0,求采样得到满足 f ( x ) f(x) f(x)分布的随机变量。

  • (1).PDF为 f ( x ) = 1 2 ( x ) exp ⁡ ( − ( x ) ) , x ≥ 0 f(x)=\frac{1}{2\sqrt(x)}\exp(-\sqrt(x)), x\geq 0 f(x)=2( x)1exp(( x)),x0,那么CDF为 F ( X ) = 1 − e x p ( − ( X ) ) F(X)=1-exp(-\sqrt(X)) F(X)=1exp(( X))
  • (2).CDF对应的逆函数为 F − 1 ( Y ) = ( l o g ( 1 − Y ) ) 2 F^{-1}(Y)=(log(1-Y))^2 F1(Y)=(log(1Y))2
  • (3).在[0,1]上均匀采样随机变量 u u u
  • (4).那么随机变量 x ′ = F − 1 ( u ) = ( l o g ( 1 − u ) ) 2 x'=F^{-1}(u)=(log(1-u))^2 x=F1(u)=(log(1u))2即满足目标PDF函数;

1.在半球面上均匀采样 uniform sample

对于半球面上的均匀采样,PDF应与微元面积成正比,那么对于 ϕ \phi ϕ θ \theta θ的累计密度函数应该为:
C D F ( ϕ ) = ∫ 0 ϕ ∫ 0 π / 2 s i n ( θ ) d θ d ϕ ∫ 0 2 π ∫ 0 π / 2 s i n ( θ ) d θ d ϕ = ∫ 0 ϕ ∫ 0 π / 2 s i n ( θ ) d θ d ϕ 2 π = ϕ 2 π C D F ( θ ) = ∫ 0 2 π ∫ 0 θ s i n ( θ ) d θ d ϕ ∫ 0 2 π ∫ 0 π / 2 s i n ( θ ) d θ d ϕ = ∫ 0 2 π ∫ 0 θ s i n ( θ ) d θ d ϕ 2 π = 1 − c o s ( θ ) (4) CDF(\phi)=\frac{\int_{0}^{\phi}\int_{0}^{\pi/2}sin(\theta)\rm{d}\theta\rm{d}\phi}{\int_{0}^{2\pi}\int_{0}^{\pi/2}sin(\theta)\rm{d}\theta\rm{d}\phi}=\frac{\int_{0}^{\phi}\int_{0}^{\pi/2}sin(\theta)\rm{d}\theta\rm{d}\phi}{2\pi}=\frac{\phi}{2\pi} \\ CDF(\theta)=\frac{\int_{0}^{2\pi}\int_{0}^{\theta}sin(\theta)\rm{d}\theta\rm{d}\phi}{\int_{0}^{2\pi}\int_{0}^{\pi/2}sin(\theta)\rm{d}\theta\rm{d}\phi}=\frac{\int_{0}^{2\pi}\int_{0}^{\theta}sin(\theta)\rm{d}\theta\rm{d}\phi}{2\pi}=1-cos(\theta) \tag{4} CDF(ϕ)=02π0π/2sin(θ)dθdϕ0ϕ0π/2sin(θ)dθdϕ=2π0ϕ0π/2sin(θ)dθdϕ=2πϕCDF(θ)=02π0π/2sin(θ)dθdϕ02π0θsin(θ)dθdϕ=2π02π0θsin(θ)dθdϕ=1cos(θ)(4)
对应的逆函数分别为:
C D F − 1 ( ϕ ′ ) = 2 π × ϕ ′ C D F − 1 ( θ ′ ) = a r c c o s ( 1 − θ ′ ) (5) CDF^{-1}(\phi') = 2\pi \times \phi' \\ CDF^{-1}(\theta') = arccos(1-\theta') \tag{5} CDF1(ϕ)=2π×ϕCDF1(θ)=arccos(1θ)(5)
之后在[0,1]之间均匀采样随机变量 u 1 , u 2 u1,u2 u1,u2,然后令:
ϕ = C D F − 1 ( u 1 ) = 2 π × u 1 θ = C D F − 1 ( u 2 ) = a r c o s ( 1 − u 2 ) (6) \phi = CDF^{-1}(u1)=2\pi \times u1 \\ \theta = CDF^{-1}(u2)=arcos(1-u2) \tag{6} ϕ=CDF1(u1)=2π×u1θ=CDF1(u2)=arcos(1u2)(6)
根据上式计算得到的 ( ϕ , θ ) (\phi,\theta) (ϕ,θ)就满足在半球面上按照面积均匀分布。

下面是采样python代码和结果(需要注意代码中笛卡尔坐标系的Y轴和Z轴做了交换):

代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def uniform_sample_hemisphere(n_samples):
    points = []
    for _ in range(n_samples):
        u1 = np.random.rand()
        u2 = np.random.rand()
        
        phi = 2 * np.pi * u1   # 计算方位角
        theta = np.arccos(1.0-u2)  # 计算极角

        # 转换到笛卡尔坐标系
        x = np.sin(theta) * np.cos(phi)
        y = np.sin(theta) * np.sin(phi)
        z = np.cos(theta)

        points.append((x, y, z))
    return np.array(points)


# 生成样本
n_samples = 5000
samples = uniform_sample_hemisphere(n_samples)

# 3D 可视化
fig = plt.figure(figsize=(12, 6))

# 绘制 3D 采样
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(samples[:, 0], samples[:, 1], samples[:, 2], alpha=0.6, s=1)
ax1.set_xlabel('X axis')
ax1.set_ylabel('Y axis')
ax1.set_zlabel('Z axis')
ax1.set_title('Uniform Sampling on Hemisphere')

# 绘制 x-y 平面映射
ax2 = fig.add_subplot(122)
ax2.scatter(samples[:, 0], samples[:, 1], alpha=0.6, s=1)
ax2.set_xlabel('X axis')
ax2.set_ylabel('Y axis')
ax2.set_title('Projection onto XY Plane')
ax2.axis('equal')

plt.tight_layout()
plt.show()
结果:

球面均匀采样

2.在半球面上的cos加权采样:方法一

对于半球面上的cos加权采样,PDF应与 微元面积 × c o s ( θ ) 微元面积\times cos(\theta) 微元面积×cos(θ)成正比,即在 θ \theta θ越小的区域采样概率越大,在 θ \theta θ越大的区域概率越小,那么对于 ϕ \phi ϕ θ \theta θ的累计密度函数应该为:
C D F ( ϕ ) = ∫ 0 ϕ ∫ 0 π / 2 c o s ( θ ) s i n ( θ ) d θ d ϕ ∫ 0 2 π ∫ 0 π / 2 c o s ( θ ) s i n ( θ ) d θ d ϕ = ∫ 0 ϕ ∫ 0 π / 2 c o s ( θ ) s i n ( θ ) d θ d ϕ π = ϕ / 2 π = ϕ 2 π C D F ( θ ) = ∫ 0 2 π ∫ 0 θ c o s ( θ ) s i n ( θ ) d θ d ϕ ∫ 0 2 π ∫ 0 π / 2 c o s ( θ ) s i n ( θ ) d θ d ϕ = ∫ 0 2 π ∫ 0 θ c o s ( θ ) s i n ( θ ) d θ d ϕ π = π × s i n 2 ( θ ) π = s i n 2 ( θ ) = 1 − c o s 2 ( θ ) (7) CDF(\phi)=\frac{\int_{0}^{\phi}\int_{0}^{\pi/2}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}{\int_{0}^{2\pi}\int_{0}^{\pi/2}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}=\frac{\int_{0}^{\phi}\int_{0}^{\pi/2}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}{\pi}=\frac{\phi/2}{\pi}=\frac{\phi}{2\pi} \\ CDF(\theta)=\frac{\int_{0}^{2\pi}\int_{0}^{\theta}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}{\int_{0}^{2\pi}\int_{0}^{\pi/2}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}=\frac{\int_{0}^{2\pi}\int_{0}^{\theta}cos(\theta)sin(\theta)\rm{d}\theta\rm{d}\phi}{\pi}=\frac{\pi \times sin^{2}(\theta)}{\pi} = sin^{2}(\theta)=1-cos^2(\theta) \tag{7} CDF(ϕ)=02π0π/2cos(θ)sin(θ)dθdϕ0ϕ0π/2cos(θ)sin(θ)dθdϕ=π0ϕ0π/2cos(θ)sin(θ)dθdϕ=πϕ/2=2πϕCDF(θ)=02π0π/2cos(θ)sin(θ)dθdϕ02π0θcos(θ)sin(θ)dθdϕ=π02π0θcos(θ)sin(θ)dθdϕ=ππ×sin2(θ)=sin2(θ)=1cos2(θ)(7)
对应的逆函数分别为:
C D F − 1 ( ϕ ′ ) = 2 π × ϕ ′ C D F − 1 ( θ ′ ) = a r c c o s ( 1 − θ ′ ) (8) CDF^{-1}(\phi') = 2\pi \times \phi' \\ CDF^{-1}(\theta') = arccos(\sqrt{1-\theta'}) \tag{8} CDF1(ϕ)=2π×ϕCDF1(θ)=arccos(1θ )(8)
之后在[0,1]之间均匀采样随机变量 u 1 , u 2 u1,u2 u1,u2,然后令:
ϕ = C D F − 1 ( u 1 ) = 2 π × u 1 θ = C D F − 1 ( u 2 ) = a r c o s ( 1 − u 2 ) (9) \phi = CDF^{-1}(u1)=2\pi \times u1 \\ \theta = CDF^{-1}(u2)=arcos(\sqrt{1-u2}) \tag{9} ϕ=CDF1(u1)=2π×u1θ=CDF1(u2)=arcos(1u2 )(9)
根据上式计算得到的 ( ϕ , θ ) (\phi,\theta) (ϕ,θ)就满足在半球面上按照 微元面积 × c o s ( θ ) 微元面积\times cos(\theta) 微元面积×cos(θ)加权分布。

下面是采样python代码和结果(需要注意代码中笛卡尔坐标系的Y轴和Z轴做了交换):

代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D


def cos_weighted_sample_hemisphere(n_samples):
    points = []
    for _ in range(n_samples):
        u1 = np.random.rand()
        u2 = np.random.rand()
        phi = 2 * np.pi * u1   # 计算方位角
        theta = np.arccos(np.sqrt(1 - u2))  # 计算极角

        # 转换到笛卡尔坐标系
        x = np.sin(theta) * np.cos(phi)
        y = np.sin(theta) * np.sin(phi)
        z = np.cos(theta)

        points.append((x, y, z))
    return np.array(points)


# 生成样本
n_samples = 5000
samples = cos_weighted_sample_hemisphere(n_samples)

# 3D 可视化
fig = plt.figure(figsize=(12, 6))

# 绘制 3D 采样
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(samples[:, 0], samples[:, 1], samples[:, 2], alpha=0.6, s=1)
ax1.set_xlabel('X axis')
ax1.set_ylabel('Y axis')
ax1.set_zlabel('Z axis')
ax1.set_title('Cos-Weighted Sampling on Hemisphere')

# 绘制 x-y 平面映射
ax2 = fig.add_subplot(122)
ax2.scatter(samples[:, 0], samples[:, 1], alpha=0.6, s=1)
ax2.set_xlabel('X axis')
ax2.set_ylabel('Y axis')
ax2.set_title('Projection onto XY Plane')
ax2.axis('equal')

plt.tight_layout()
plt.show()

结果:

半球面cos加权采样-方法一

3.在半球上的cos加权采样:方法二

另一种对于半球面上的cos加权采样的方法如下:
直接在X-Z平面的圆内均匀采样点,然后将采样的点映射到上方的球面上,如下如图所示:
圆面上采样示意图

接下来将证明本方法与在半球上使用cos加权采样:方法二的等效性。
我们将目标放在X-Z平面上的圆平面内,在该圆面内可以使用极坐标 ( ϕ , r ) (\phi,r) (ϕ,r)表示任意一点。
在圆面内面积微元 d A \rm{d}A dA为:
d A = r ∗ d r d ϕ (10) \rm{d}A=r*\rm{d}r\rm{d}{\phi} \tag{10} dA=rdrdϕ(10)
在圆平面内均匀采样一点,那么采样点对应的半径 r r r C D F ( r ) CDF(r) CDF(r)等于:
C D F ( r ) = ∫ 0 2 π ∫ 0 r r d r d ϕ ∫ 0 2 π ∫ 0 1 r d r d ϕ = 2 π ∗ r 2 / 2 π = r 2 (11) CDF(r)=\frac{\int_{0}^{2\pi}\int_{0}^{r}r\rm{d}r\rm{d}\phi}{\int_{0}^{2\pi}\int_{0}^{1}r\rm{d}r\rm{d}\phi}=\frac{2\pi*r^{2}/2}{\pi}=r^2 \tag{11} CDF(r)=02π01rdrdϕ02π0rrdrdϕ=π2πr2/2=r2(11)
即,假如按照上述方法得到的采样点的坐标 ( ϕ , r ) (\phi,r) (ϕ,r)中的随即变量 r r r,一定满足其 C D F ( r ) = r 2 CDF(r)=r^2 CDF(r)=r2
根据上图可知在X-Z平面圆面上的 r r r就等于 s i n ( θ ) sin(\theta) sin(θ),这说明只要我们按照在X-Z平面上的圆平面内均匀采样一点,采样点 ( ϕ , r ) (\phi,r) (ϕ,r),就能保证采样点坐标的 r r r满足 C D F ( r ) = r 2 CDF(r)=r^2 CDF(r)=r2

因为: C D F ( θ ) = P D F ( Θ ≤ θ ) = P D F ( R ≤ s i n ( θ ) ) CDF(\theta)=PDF(\Theta\leq\theta)=PDF(R\leq sin(\theta)) CDF(θ)=PDF(Θθ)=PDF(Rsin(θ))
又因为: r = s i n ( θ ) r=sin(\theta) r=sin(θ)
因此: P ( R ≤ s i n ( θ ) ) = P ( R ≤ r ) = C D F ( r ) = r 2 = s i n 2 ( θ ) P(R\leq sin(\theta))=P(R\leq r)=CDF(r)=r^2=sin^2(\theta) P(Rsin(θ))=P(Rr)=CDF(r)=r2=sin2(θ)
即: C D P ( θ ) = s i n 2 ( θ ) CDP(\theta)=sin^{2}(\theta) CDP(θ)=sin2(θ)
即映射点 ( ϕ , θ ) (\phi,\theta) (ϕ,θ)中的坐标值 θ \theta θ,满足 C D F ( θ ) = s i n 2 ( θ ) CDF(\theta)=sin^{2}(\theta) CDF(θ)=sin2(θ),而这正是公式(7)中我们想要找到一个随机变量 θ \theta θ
同理可得,使用该方法得到的映射点坐标 ϕ \phi ϕ也满足公式(7)中 C D F ( ϕ ) = ϕ 2 π CDF(\phi)=\frac{\phi}{2\pi} CDF(ϕ)=2πϕ

python代码如下所示:

代码:
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D

# 生成 cos 加权采样的函数
def cos_weighted_sample(n_samples):
    points = []

    for _ in range(n_samples):
        u1 = np.random.rand()
        u2 = np.random.rand()

        r = np.sqrt(u1)  # 均匀采样半径
        phi = 2 * np.pi * u2   # 计算方位角  # 均匀采样角度
        x = r * np.cos(phi)
        y = r * np.sin(phi)
        z = np.sqrt(1 - x**2 - y**2)
        if (x*x+y*y <= 1.0):  # 只保留圆内的点
            points.append((x, y, z))
    return np.array(points)

# 生成样本
n_samples = 5000
samples = cos_weighted_sample(n_samples)

# 3D 可视化
fig = plt.figure(figsize=(12, 6))

# 绘制 3D 采样
ax1 = fig.add_subplot(121, projection='3d')
ax1.scatter(samples[:, 0], samples[:, 1], samples[:, 2], alpha=0.6, s=1)
ax1.set_xlabel('X axis')
ax1.set_ylabel('Y axis')
ax1.set_zlabel('Z axis')
ax1.set_title('Cos-Weighted Sampling on Hemisphere')

# 绘制 x-y 平面映射
ax2 = fig.add_subplot(122)
ax2.scatter(samples[:, 0], samples[:, 1], alpha=0.6, s=1)
ax2.set_xlabel('X axis')
ax2.set_ylabel('Y axis')
ax2.set_title('Projection onto XY Plane')
ax2.axis('equal')

plt.tight_layout()
plt.show()
结果:

半球面cos加权采样-方法二

三、参考

[1].Sampling the hemisphere
[2].Cosine-Weighted采样算法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值