DirectX12(D3D12)基础教程(二十一)—— PBR:IBL 的数学原理(3/5)漫反射积分项
创始人
2025-05-31 06:29:02

目录

    • 3.4、漫反射项的重要性采样计算
      • 3.4.1、漫反射项的二重积分形式极其近似预积分计算
      • 3.4.2、漫反射辐照度积分项的直接积分计算
      • 3.4.3、漫反射辐照度积分项的蒙特卡洛积分重要性采样计算

3.4、漫反射项的重要性采样计算

3.4.1、漫反射项的二重积分形式极其近似预积分计算

  进一步来说,因为对漫反射项积分来说 κd、c、π\kappa_d 、c、\piκd​、c、π 等参数对于指定的一点 p⃗\vec{p}p​ 来说是常数,可以提到积分外面,所以其中第一个积分可以进一步推导为:
Lod(p⃗,ωo⃗)=κdcπ∫ΩLi(p⃗,ωi⃗)n⃗⋅ωi⃗dωi⃗∵dω=sin⁡(θ)dθdϕ,n⃗⋅ωi⃗=cos⁡(θ)=κdcπ∫02π∫0π2Li(p⃗,ωi⃗)cos⁡(θ)sin⁡(θ)dθdϕ\mathrm{L}_{o_d}(\vec{p},\vec{\omega_{o}}) = \kappa_d \frac{c}{\pi} \mathop{\int}_{\Omega} \mathrm{L}_i(\vec{p},\vec{\omega_i}) \vec{n} \cdot \vec{\omega_i} d\vec{\omega_i} \\[2ex] \because d \omega = \sin(\theta) d \theta d \phi, \quad \vec{n} \cdot \vec{\omega_i} = \cos(\theta) \\[2ex] = \kappa_d \frac{c}{\pi} \int \limits_0^{2\pi} \int \limits_0^{\frac{\pi}{2}} \mathrm{L}_i(\vec{p},\vec{\omega_i}) \cos(\theta) \sin(\theta) d \theta d \phi Lod​​(p​,ωo​​)=κd​πc​∫Ω​Li​(p​,ωi​​)n⋅ωi​​dωi​​∵dω=sin(θ)dθdϕ,n⋅ωi​​=cos(θ)=κd​πc​0∫2π​0∫2π​​Li​(p​,ωi​​)cos(θ)sin(θ)dθdϕ
  上式中,角度 θ\thetaθ 就是入射光线与点 p⃗\vec{p}p​ 处法线 n⃗\vec{n}n 的夹角也就是入射角。同时在点 p⃗\vec{p}p​ 处作直角坐标系如下图所示,其中正 zzz 方向与点 p⃗\vec{p}p​ 处法线 n⃗\vec{n}n 方向一致,xxx 轴与 yyy 轴在与点 p⃗\vec{p}p​ 处法线 n⃗\vec{n}n 垂直的平面内。
在这里插入图片描述

  接着取入射光 ωi\omega_iωi​ 与点 p⃗\vec{p}p​ 处法线 n⃗\vec{n}n 所成平面与 xxx 轴间的夹角为 ϕ\phiϕ (称之为方位角),然后计算得到入射立体角微分 dω=sin⁡(θ)dωdϕd \omega = \sin(\theta) d \omega d \phidω=sin(θ)dωdϕ 最终就得到了上式中漫反射项的最终可计算的二重积分形式。

  需要注意的是,我们现在使用的模型是针对不透明物体的表面的反射,所以积分的区域是点 p⃗\vec{p}p​ 处,朝向法线方向的单位正半球,所以方位角 ϕ\phiϕ 的范围是 [0,2π][0,2\pi][0,2π],对应的天顶角 θ\thetaθ 范围就是 [0,π][0,\pi][0,π] 。
在这里插入图片描述

  这个积分项在 IBL 方法中称之为“漫反射项”。注意到其中积分部分已经完全与 ωo⃗\vec{\omega_o}ωo​​ 也即视方向无关了,所以其实这一项是可以从每一帧循环渲染计算中完全提取出来的。当然提取的前提是被照射的物体与其环境在某段固定的时间中基本不变,或者说针对一个相对固定的场景来说。

  具体来讲,对于一个固定点 p⃗\vec{p}p​ 来说,整个式子又可以被拆分成两部分去计算,一部分是针对点 p⃗\vec{p}p​ 的常数项 κdcπ\kappa_d \cfrac{c}{\pi}κd​πc​ ,这个可以被延迟到渲染循环中的物体表面 PS 中去计算,因为参数 ccc 就是物体表面点的漫反射率,也就是我们通常所说的物体表面的颜色,而 κd\kappa_dκd​ 就是控制整体能量守恒的系数,需要先计算点 p⃗\vec{p}p​ 处的 κs\kappa_sκs​ 也就是菲涅尔系数后再确定,所以整个常数项实际是与物体表面相关联的,因此就放到最终渲染循环中去。

  而第二部分积分项,其实本质上也是与物体表面的点 p⃗\vec{p}p​ 相关联的,但如果也因此而放到最终渲染循环中的话,就会导致计算量过大,而无法实现实时的 PBR 渲染。因此在这里第一个关于质量和性能的折中处理,就是我们总是假设这个积分中的点 p⃗\vec{p}p​ 就是物体的几何中心点所在的位置,然后将整个积分的计算从渲染循环中提取出来,变成一个全局预处理的计算过程,而这个预积分计算的结果就被称之为“预积分辐照度贴图”,因为整个预计算部分正好就是针对所有方向的入射光也就是入射辐照度进行积分。

  实际应用时,这需要假设我们可以提前预知点 p⃗\vec{p}p​ 的位置。对于固定的场景中的固定的物体位置来说,这本来就是已知量。这就使得预积分辐照度贴图最终完全可以在场景设计确定的阶段就计算完毕,并存储起来,在真正运行渲染时进行采样使用即可。

  在本章示例中我们只是用了最简单的 Cubemap 纹理(或者叫2D纹理数组 Texture2DArray)的方式,对这个辐照图贴图进行了存储,这样的存储方式还是比较耗费空间的。更高级的可以使用球谐函数的方式简化存储。

  在示例代码运行后的矩形框中,紧跟解算后的6副环境映射纹理之后的6副纹理就展示了预积分贴图的六个面:
在这里插入图片描述
  如果场景中的物体是经常变动位置时,而且大多数情况下,我们渲染的核心很多时候就是3D动画的场景,这时点 p⃗\vec{p}p​ 的位置就会经常变动,就使的这个预积分计算跟渲染循环,或者说跟具体的渲染帧发生了关联,因此刚才说的假设点 p⃗\vec{p}p​ 是某物体中心点的近似预积分方法就可能失效了。

  此时可以在场景中的某些按照一定规律固定排列的点 p⃗n\vec{p}_np​n​ 来作为预积分计算的点,然后在渲染循环中,根据物体位置离哪个点 p⃗n\vec{p}_np​n​ 近,就取那个点处的预积分贴图来进行漫反射项的近似计算。这样就可以在场景设计定型的时候,预计算一堆关于点 p⃗n\vec{p}_np​n​ 的漫反射辐照度预积分贴图。而这些点 p⃗n\vec{p}_np​n​ 就被称为 Probe (探针)。更进一步的,在探针技术的基础上,可以使用球谐函数等来简化预积分辐照度贴图计算结果的存储和使用。从而变成一个一次性计算(或称之为预计算)!这些话题后续教程再说。

3.4.2、漫反射辐照度积分项的直接积分计算

在这里插入图片描述

  至此其实漫反射项的二重积分形式已经可以直接编码进行计算了,在示例代码 Shader 文件: GRSD3D12Sample/GRS_IBL_Diffuse_Irradiance_Convolution_With_Integration_PS.hlsl 中就展示了直接二重积分计算的方法:

float4 PSMain(ST_GRS_HLSL_PS_INPUT pin):SV_Target
{float3 N = normalize(pin.m_v4WPos.xyz);float3 irradiance = float3(0.0f, 0.0f, 0.0f);float3 up = float3(0.0f, 1.0f, 0.0f);float3 right = normalize(cross(up, N));up = normalize(cross(N, right));N = normalize(cross(right,up));float nrSamples = 0.0f;float deltaPhi = (2.0f * PI) / 180.0f;float deltaTheta = (0.5f * PI) / 90.0f;// 积分运算,直接翻译自公式for (float phi = 0.0f; phi < 2.0f * PI; phi += deltaPhi){for (float theta = 0.0f; theta < 0.5f * PI; theta += deltaTheta){// 球坐标转换到笛卡尔坐标(切空间)float3 tangentSample = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));// 切空间转换到世界坐标空间float3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * N;//float3 sampleVec = right;irradiance += (g_texHDREnvCubemap.Sample(g_sapLinear, sampleVec).xyz * cos(theta) * sin(theta));nrSamples += 1.0f;}}irradiance = PI * irradiance * (1.0 / float(2 * nrSamples));return float4(irradiance, 1.0f);
}

  上面代码中变量 irradiance 代表最终点 p⃗\vec{p}p​ 处的所有入射光引起的入射辐照度,所以这个过程最终计算出来的预积分纹理也被称为“辐照度贴图”。

  代码中的 deltaPhi 即 dϕd\phidϕ,根据代码中的表达式,可以看出它的步长(step)是 2π180\cfrac{2\pi}{180}1802π​弧度,也就是总共计算180次、deltaTheta 即 dθd \thetadθ ,对应的其步长是 π290\cfrac{\frac{\pi}{2}}{90}902π​​弧度,总共计算 90 次。最终这使得双重循环黎曼和计算对于一个采样点 p⃗\vec{p}p​ 来说总共需要 180×90=16200180 \times 90 =16200180×90=16200 次!然而这还只是一个很粗糙的颗粒度,这从其步长设置就可以看出来。并且实际的效果也是惨不忍睹的。

  而总体上我们总共有 6×32×32=61446 \times 32 \times 32 = 61446×32×32=6144 个点,这只是低分辨率下的辐照度贴图映射的大小。在示例代码开头处我们用宏定义指定了这个大小:

#define GRS_RTA_IBL_DIFFUSE_MAP_SIZE_W  32
#define GRS_RTA_IBL_DIFFUSE_MAP_SIZE_H  32

  接着代码中主要就是用点 p⃗\vec{p}p​ 的方向用作其自身的法线,并假设向上的方向为正 y 轴方向,然后用施密特正交法计算得到了点 p⃗\vec{p}p​ 处局部切空间的三个正交坐标轴,也就是得到了切空间转换到世界坐标空间的变换矩阵,这也是一个非常重要和基础的切空间计算的简单技巧。然后用这个切空间的变换矩阵,将点 p⃗\vec{p}p​ 处局部坐标系中的立体角微分向量变换到了世界坐标中,并作为 3D 采样(Sample)操作的向量对环境映射进行采样。

  最终再一次提醒,在没有完全把握的情况下,请千万不要使用和运行GRSD3D12Sample/GRS_IBL_Diffuse_Irradiance_Convolution_With_Integration_PS.hlsl 这个 Shader 脚本,以免因计算量过大造成显卡损伤。放在本章示例中,仅仅是为了说明预计算辐照度贴图计算过程的基本原理。或者说是为了说明下基本的数值积分代码如何编写而已。

3.4.3、漫反射辐照度积分项的蒙特卡洛积分重要性采样计算

  了解完了基本的预积分辐照度贴图的计算过程之后,希望你也看了 3D数学系列之——再谈特卡洛积分和重要性采样 并明白了其中介绍的重要性采样方法。索性的是,蒙特卡洛方法可以使我们极大的减小预积分辐照度贴图的计算量,并且还能保持一定的精度。

  根据文中的公式:
p(x)=∣f(x)∣∫abf(x)dx\mathrm{p}(x) = \cfrac{|{f}(x)|}{\int \limits_a^b {f}(x) \mathrm{d}x} p(x)=a∫b​f(x)dx∣f(x)∣​
  我们继续使用瞪眼法来观察漫反射项中的积分部分:
∫02π∫0π2Li(p⃗,ωi⃗)cos⁡(θ)sin⁡(θ)dθdϕ\int \limits_0^{2\pi} \int \limits_0^{\frac{\pi}{2}} \mathrm{L}_i(\vec{p},\vec{\omega_i}) \cos(\theta) \sin(\theta) d \theta d \phi 0∫2π​0∫2π​​Li​(p​,ωi​​)cos(θ)sin(θ)dθdϕ
  虽然这是个二重积分,但是根据蒙特卡洛积分的特性,这也是可以用蒙特卡洛积分来处理的。只是需要我们选取合适的概率密度函数 p\mathrm{p}p 。通过观察我们可以发现,使用如下的概率密度函数,就可以进行重要性采样计算:
p(θ,ϕ)=ccos⁡(θ)sin⁡(ϕ)\mathrm{p}(\theta,\phi) = c \cos(\theta)\sin(\phi) p(θ,ϕ)=ccos(θ)sin(ϕ)
  首先要注意的是,因为被积函数是关于 θ,ϕ\theta,\phiθ,ϕ 的二元函数(点 p⃗\vec{p}p​ 参数相对固定对于整个积分来说相当于常数,而入射光向量 ω⃗i\vec{\omega}_iωi​ 及立体角微分都是可以用球坐标系下的 θ,ϕ\theta,\phiθ,ϕ 表达的),所以我们设计的概率密度函数也是关于相同自变量的二元函数。

  另外在函数表达式中只是提取了三角函数的因子,这主要是因为一般函数表达式中如果乘入了三角函数因子尤其是正弦或余弦函数时,函数的形状就主要会受到他们的显著影响,所以我们选择如上的概率密度函数,作为我们进一步简化积分计算的切入点。

  根据重要性采样的方法步骤,第一步就是要验证概率密度函数的“归一性”,从而还可以顺道计算出式中常数项 ccc 的值,这可以根据概率密度函数的性质要求来计算:
p(θ,ϕ)=∫02π∫0π2ccos⁡(θ)sin⁡(θ)dθdϕ=1⇒c∫02πdϕ∫0π2cos⁡(θ)sin⁡(θ)dθ=1∵dsin⁡(θ)=cos⁡(θ)dθc∫02πdϕ∫0π2sin⁡(θ)dsin⁡(θ)=1⇒c[ϕ]02π[12sin⁡2(θ)]0π2=1⇒c=1π⇒p(θ,ϕ)=1πcos⁡(θ)sin⁡(θ)\mathrm{p}(\theta,\phi) = \int \limits_0^{2\pi} \int \limits_0^{\frac{\pi}{2}} c \cos(\theta) \sin(\theta) d \theta d \phi = 1 \\[2ex] \Rightarrow c \int \limits_0^{2\pi} \mathrm{d}\phi \int \limits_0^{\frac{\pi}{2}} \cos(\theta)\sin(\theta) \mathrm{d} \theta = 1 \\[2ex] \because \mathrm{d} \sin(\theta) = \cos(\theta)\mathrm{d} \theta \\[2ex] c \int \limits_0^{2\pi} \mathrm{d}\phi \int \limits_0^{\frac{\pi}{2}} \sin(\theta) \mathrm{d} \sin(\theta) = 1 \\[2ex] \Rightarrow c \Biggl[ \phi \Biggr]_0^{2\pi} \Biggl[ \cfrac{1}{2} \sin^2(\theta) \Biggr]_0^{\frac{\pi}{2} } = 1 \\[2ex] \Rightarrow c = \cfrac{1}{\pi} \\[2ex] \Rightarrow \mathrm{p}(\theta,\phi) = \cfrac{1}{\pi} \cos(\theta)\sin(\theta) p(θ,ϕ)=0∫2π​0∫2π​​ccos(θ)sin(θ)dθdϕ=1⇒c0∫2π​dϕ0∫2π​​cos(θ)sin(θ)dθ=1∵dsin(θ)=cos(θ)dθc0∫2π​dϕ0∫2π​​sin(θ)dsin(θ)=1⇒c[ϕ]02π​[21​sin2(θ)]02π​​=1⇒c=π1​⇒p(θ,ϕ)=π1​cos(θ)sin(θ)

  因为这个概率密度函数是二元函数,而我们产生随机数需要一个变量一个变量的产生,这就需要进一步针对每个随机变量计算其边缘概率密度,从而对随机变量进行分离,此时推算如下:
∵p(θ,ϕ)=1πcos⁡(θ)sin⁡(θ)∴p(θ,ϕ)=p(θ)×p(ϕ)\because \quad \mathrm{p}(\theta,\phi) = \cfrac{1}{\pi} \cos(\theta)\sin(\theta) \\[2ex] \therefore \mathrm{p}(\theta,\phi) = \mathrm{p}(\theta)\times \mathrm{p}(\phi) ∵p(θ,ϕ)=π1​cos(θ)sin(θ)∴p(θ,ϕ)=p(θ)×p(ϕ)

  上面过程说明我们要使用的随机变量 θ,ϕ\theta,\phiθ,ϕ 是相互独立的,其实从其定义也可以看出,这两个角度变量是没有关联性的。所以就可以继续推导每一个独立随机变量的概率密度函数:
p(ϕ)=∫0π2p(θ,ϕ)dθ=∫0π21πcos⁡(θ)sin⁡(θ)dθ=[12πsin⁡2(θ)]0π2=12π\mathrm{p}(\phi) = \int \limits_0^{\frac{\pi}{2}} \mathrm{p}(\theta,\phi) \mathrm{d} \theta \\[2ex] = \int \limits_0^{\frac{\pi}{2}} \cfrac{1}{\pi} \cos(\theta)\sin(\theta) \mathrm{d} \theta \\[2ex] =\Biggl[ \cfrac{1}{2\pi} \sin^2(\theta) \Biggr]_0^{\frac{\pi}{2} } \\[2ex] = \cfrac{1}{2\pi} p(ϕ)=0∫2π​​p(θ,ϕ)dθ=0∫2π​​π1​cos(θ)sin(θ)dθ=[2π1​sin2(θ)]02π​​=2π1​

∵p(θ,ϕ)=p(θ)×p(ϕ)1πcos⁡(θ)sin⁡(θ)=p(θ)×12π⇒p(θ)=2cos⁡(θ)sin⁡(θ)\because \quad \mathrm{p}(\theta,\phi) = \mathrm{p}(\theta)\times \mathrm{p}(\phi) \\[2ex] \cfrac{1}{\pi} \cos(\theta)\sin(\theta) = \mathrm{p}(\theta) \times \cfrac{1}{2\pi} \\[2ex] \Rightarrow \mathrm{p}(\theta) = 2 \cos(\theta) \sin(\theta) ∵p(θ,ϕ)=p(θ)×p(ϕ)π1​cos(θ)sin(θ)=p(θ)×2π1​⇒p(θ)=2cos(θ)sin(θ)

  至此,我们就分析推导清楚了我们需要的随机变量的概率密度函数,接着按照重要性采样的步骤,就需要计算每个随机变量对应的概率分布函数 CDF :
CDF(θ)=∫0θ2cos⁡(x)sin⁡(x)dx=[2×12sin⁡2(x)]0θ=sin⁡2(θ)CDF(ϕ)=∫0ϕ12πdx=12πϕ\mathrm{CDF}(\theta) = \int \limits_0^{\theta} 2\cos(x) \sin(x) \mathrm{d} x \\[2ex] = \Biggl[ 2 \times \cfrac{1}{2} \sin^2(x) \Biggr]_0^{\theta} \\[2ex] = \sin^2(\theta) \\[2ex] \mathrm{CDF}(\phi) = \int \limits_0^{\phi} \cfrac{1}{2\pi} \mathrm{d} x \\[2ex] = \cfrac{1}{2\pi} \phi CDF(θ)=0∫θ​2cos(x)sin(x)dx=[2×21​sin2(x)]0θ​=sin2(θ)CDF(ϕ)=0∫ϕ​2π1​dx=2π1​ϕ
  接着继续计算 CDF 的反函数如下:
令μ=sin⁡2(θ),ν=12πϕ则:θ=arcsin⁡(μ)ϕ=2πν令 \quad \mu = \sin^2(\theta), \nu = \cfrac{1}{2\pi} \phi \quad 则: \\[2ex] \theta = \arcsin(\sqrt{\mu}) \\[2ex] \phi = 2\pi \nu 令μ=sin2(θ),ν=2π1​ϕ则:θ=arcsin(μ​)ϕ=2πν
   最后我们只需要产生两个均匀分布的伪随机序列 μ,ν\mu,\nuμ,ν 就可以利用蒙特卡洛积分公式计算辐照度积分了。那么让我们将所有推导的结果反代回蒙特卡洛积分先做一下简化:
∵∫abf(x)dx≈1N∑n=1Nf(xn)p(xn)Lod(p⃗,ωo⃗)=κdcπ∫02π∫0π2Li(p⃗,ωi⃗)cos⁡(θ)sin⁡(θ)dθdϕ∵p(θ,ϕ)=1πcos⁡(θ)sin⁡(θ)∴Lod(p⃗,ωo⃗)≈κdcπ1N∑n=1NLi(p⃗,ωi⃗)cos⁡(θ)sin⁡(θ)1πcos⁡(θ)sin⁡(θ)=κdc1N∑n=1NLi(p⃗,ωi⃗)\because \quad \int \limits_a^b {f}(x) \mathrm{d} x \approx \cfrac{1}{N} \sum \limits_{n=1}^{N} \cfrac{{f}(x_n)}{\mathrm{p}(x_n)} \\[2ex] \mathrm{L}_{o_d}(\vec{p},\vec{\omega_{o}}) = \kappa_d \frac{c}{\pi} \int \limits_0^{2\pi} \int \limits_0^{\frac{\pi}{2}} \mathrm{L}_i(\vec{p},\vec{\omega_i}) \cos(\theta) \sin(\theta) d \theta d \phi \\[2ex] \because \quad \mathrm{p}(\theta,\phi) = \cfrac{1}{\pi} \cos(\theta)\sin(\theta) \\[2ex] \therefore \quad \mathrm{L}_{o_d}(\vec{p},\vec{\omega_{o}}) \approx \kappa_d \frac{c}{\pi} \cfrac{1}{N} \sum \limits_{n=1}^{N} \cfrac{\mathrm{L}_i(\vec{p},\vec{\omega_i}) \cos(\theta) \sin(\theta)}{\cfrac{1}{\pi}\cos(\theta)\sin(\theta)} \\[2ex] = \kappa_d c \cfrac{1}{N} \sum \limits_{n=1}^{N} \mathrm{L}_i(\vec{p},\vec{\omega_i}) ∵a∫b​f(x)dx≈N1​n=1∑N​p(xn​)f(xn​)​Lod​​(p​,ωo​​)=κd​πc​0∫2π​0∫2π​​Li​(p​,ωi​​)cos(θ)sin(θ)dθdϕ∵p(θ,ϕ)=π1​cos(θ)sin(θ)∴Lod​​(p​,ωo​​)≈κd​πc​N1​n=1∑N​π1​cos(θ)sin(θ)Li​(p​,ωi​​)cos(θ)sin(θ)​=κd​cN1​n=1∑N​Li​(p​,ωi​​)
  从上面的最终推导过程我们可以看出,首先在形式上比原先的二重积分形式简单了很多,并且计算过程直接变成了 1 维求和计算,同时还自动消去了一个除以 π\piπ 的计算;其次求和部分只剩下入射光函数 Li(p⃗,ωi⃗)\mathrm{L}_i(\vec{p},\vec{\omega_i})Li​(p​,ωi​​) ,这在 IBL 中仅仅只是一个 3D Sample 操作,最终整个求和式就变成了针对环境映射的一个采样平均值。

  具体的在本章示例代码的 Shader GRSD3D12Sample/GRS_IBL_Diffuse_Irradiance_Convolution_With_Monte_Carlo_PS.hlsl 中实现如下:

float4 PSMain(ST_GRS_HLSL_PS_INPUT stPSInput) :SV_Target
{float3 N = normalize(stPSInput.m_v4WPos.xyz);float3 irradiance = float3(0.0f,0.0f,0.0f);// 计算切空间float3 Up = float3(0.0f, 1.0f, 0.0f);float fdot = dot(Up, N);if ((1.0 - fdot) > 1e-6){Up = float3(1.0f, 0.0f, 0.0f);}float3 Right = normalize(cross(Up, N));Up = normalize(cross(N, Right));N = normalize(cross(Right, Up));float theta = 0.0f;float phi = 0.0f;float2 Xi;float3 L;const uint SAMPLE_NUM = 4096u;// 蒙特卡洛积分循环for (uint i = 0u; i < SAMPLE_NUM; i++){// 产生均匀分布随机数Xi = Hammersley(i, SAMPLE_NUM);// 根据 CDF 反函数计算指定分布的随机变量phi = TWO_PI * Xi.x;theta = asin(sqrt(Xi.y));// 球坐标转换到笛卡尔坐标(切空间)L = float3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));// 切空间转换到世界坐标空间L = L.x * Right + L.y * Up + L.z * N;// 采样求和统计入射辐照度irradiance += g_texHDREnvCubemap.Sample(g_sapLinear, L).xyz;}// 取平均得到最终结果irradiance *= 1.0 / float(SAMPLE_NUM);return float4(irradiance,1.0f);
}

  代码中的过程及注释已经很清晰的说明了整个计算过程,与我们做的数学推导过程是一致的。所以最终只要理解了整个数学推算的过程,最终的代码就变得很好理解了。

  但其中还是需要注意几个细节:首先,代码中生成均匀分布随机数的方法使用了流行的 Hammersley 算法,关于这个算法,回头在另外篇章的教程中单独介绍,本教程中就不在赘述了,现在只需要知道它能够产生高质量的 2D 均匀分布伪随机序列即可,并且范围在[0,1][0,1][0,1] 之间。

  其次,整个蒙特卡洛积分循环的次数设置成了 4096 次,其实大概 2000多次的时候效果就已经很好了。但即就是使用4096次循环,也比之前的二重积分双循环运行16200次的直译算法效果好的很多,并且计算次数直接节省了到了四分之一以上。假如要使用直接数值积分算法效果达到重要性采样方法的级别,估计循环次数还需要再增进至少4倍以上,当然为了防止显卡损伤,所以还是不要去做这样的尝试了(主要是因为循环中嵌套着耗时并且费硬件的 Sample 操作)。

  最后,代码中为了做教程的目的,所以对 θ\thetaθ 角的变量做了反三角函数的运算,其实实际工程代码中,不需要做这个操作,直接将开方后的随机值作为 θ\thetaθ 角的正弦值即可,余弦值根据三角公式即可算得,这样就节省了反复计算三角函数的开销。而大多数实际的代码中确实是这样的做的,比如 UE 引擎的 Shader 代码中。

  额外需要提醒的是在著名的 [漫反射辐照 - LearnOpenGL CN](https://learnopengl-cn.github.io/07 PBR/03 IBL/01 Diffuse irradiance/) 教程中,既没有详细讲述整个数学过程,也没有推导重要性采样的过程,最后其漫反射辐照度贴图使用了计算量更大的双重循环来计算,如果不加思索的直接使用其代码,可能会造成显卡过热保护而退出,严重的情形下可能会造成显卡硬伤,所以在没有搞清楚的情况下请不要随意执行。而正确的做法是使用本章教程中所讨论的重要性采样方法来计算漫反射辐照度贴图。

相关内容

热门资讯

关于Docker逃逸 关于Docker逃逸 文章目录关于Docker逃逸前言一、判断是否为docker容器?...
在Centos上架设Zerot... Zerotier在国外,经常不好访问,Moon根服务也不是很好用。我们可...
超薄网络变压器(百兆千兆万兆)... Hqst华强盛:随着主板小型化,超薄型网络变压器越来越有集中的需求&#x...
思铂睿油耗有多少(思铂睿油耗多... 本篇文章极速百科给大家谈谈思铂睿油耗有多少,以及思铂睿油耗多少钱一公里对应的知识点,希望对各位有所帮...
描写冬天的好词好句 极速百科网... 好词: 银装素裹、白雪皑皑、玉树琼枝、寒风凛冽、冰天雪地、寒气逼人、雪中送炭、红炉暖阁、寒梅傲...
dnf怎么带人强开魔界深渊 极... 地下城与勇士(简称DNF)是由腾讯发行的手机游戏。该游戏是一款2D卷轴式横版格斗过关网络游戏,大量继...
高通670相当于骁龙多少 极速... 高通670相当于骁龙多少高通670处理器,这是一款颇受关注的产品,尤其在智能手机领域。作为美国高通公...
Qt实战技能 快捷键: 多行注释: 选中多行---ctrl+/ 打开文件或...
CentOS操作系统libc.... 使用xshell登陆Linux后查看jdk版本提示 /lib64/libc.so.6: versio...
@RequestMapping... 在享受了@RequestMapping方便的处理映射时,忍不住会开始好奇&#x...
配风景照的的简短文字,穿越时光... 光影交织中,我寻找着生活的诗意。每一张风景照,都是我对大自然的热爱与敬畏。愿你也能在繁忙的生活中,找...
计算机专业的就业方向是什么,计... 计算机专业的就业方向非常广泛,主要包括以下几个方面: 1. 软件开发:这是计算机专业最主要的就...
诗圣是谁(诗圣是谁诗仙又是谁)... 本篇文章极速百科给大家谈谈诗圣是谁,以及诗圣是谁诗仙又是谁对应的知识点,希望对各位有所帮助,不要忘了...
表示红的成语ABAB 极速百科... 表示红的成语ABAB有:红红火火。收到你的喜欢啦收到你的喜欢啦
labview程序结构for循... wx供重浩:创享日记 对话框发送:labview程序 获取完整无水印报告...
[数据结构高频面试题]用两个栈... 文章目录 一、栈实现队列的特点分析 1、1 具体分析 1、2 整体概括 二、用栈模拟队列代码的实现 ...
手写Promise源码的实现思... Promise的使用: let promise = new Promise((...
立方计算公式和方法,**标题:... 首先,立方计算公式是指一个数自乘三次的结果,即 (a3),其中 (a) 是任意实数。这个公式是计算立...
dw的意思,DW的多种含义 极... DW(Dreamweaver)是一款由Adobe公司开发的网页开发软件,主要用于网页设计和开发。它提...
曾经辉煌一时,如今光辉不再,日... 本篇文章极速百科给大家谈谈曾经辉煌一时,如今光辉不再,日产CIMA是如何衰落,以及日产车企对应的知识...
bedook祛痘效果怎么样(b... 本篇文章极速百科给大家谈谈bedook祛痘效果怎么样,以及bedook洗面奶祛痘效果怎么样对应的知识...
模糊PID控制双容水箱液位控制... 资源:双容水箱液位模糊PID控制MATLAB仿真-电子商务文档类资源-CSDN文库模糊...
Java Spring Par... 基本概念  model/entity:实体层 service:业务逻...
2023年信息与通信工程国际会... 会议简介 Brief Introduction 2023年第二届信息与通信工程国际会议(JCICE ...
蓝光钢化膜真的护眼吗 极速百科... 先说结论:不一定。 蓝光钢化膜不一定护眼。蓝光钢化膜可以减少屏幕释放的蓝光对眼睛的刺激,但是不...
迈锐宝和迈锐宝xl的区别是什么... 迈锐宝和迈锐宝XL的区别主要有以下几点: 1. 外观方面:迈锐宝XL采用了全新的家族式前脸设计...
摩托车怎么查违章,摩托车违章查... 在摩托车领域,查询违章记录通常需要以下步骤: 1. 确定证件信息:确保你拥有有效的驾驶执照和车...
物理学专业好就业吗 极速百科网... 物理专业学生的就业前景与国家政策和发展方向密切相关。如果国家大力发展物理科学研究和应用,那么物理专业...
React--》状态管理工具—... 目录 Mobx的讲解与使用 Mobx环境配置 Mobx的基本使用 Mobx计算属性的使用 Mobx监...
Python模块 一、模块 (一)定义 模块指的是Python的程序文件(源...