游戏引擎随笔 0x34:UE5 Lumen 源码解析(六)Importance Sampling 篇

https://zhuanlan.zhihu.com/p/531442379

游戏引擎随笔 0x34:UE5 Lumen 源码解析(六)Importance Sampling 篇

丛越

游戏引擎的自我修养

44 人赞同了该文章

在《UE5 Lumen 源码解析》系列的前面几篇中已经将 Lumen 的光照准备部分基本讲解完毕,接下来应该进入 Lumen 的 Final Gather Pipeline 部分,包括 Screen\World Space Probe、Radiance Cache 等内容,不过基于 Monte Carlo 的实时全局光照一个重要组件是重要性采样(Importance Sampling),这是保证在满足实时性能的同时提供高质量 GI 的关键因素,也是 Lumen 的重要组成部分,并且在后续 Final Gather 的流程中嵌入了 Importance Sampling 流程,因此为了后续更好的讲解和理解,在进入 Final Gather 之前本篇先来讲解 Lumen 中的 Importance Sampling。

原理

Importance Sampling 本质上是在样本数一定的情况下增加有效样本,减少方差,对于基于 Monte Carlo 的实时全局光照来说就是对 Tracing 的重定向,使光线尽可能朝向有效 BRDF 以及光照方向追踪,这样就可以在不增加光线预算的情况下降噪,加快收敛速度,提升 GI 质量。为了便于后续讲解,我们先来看看 Lumen 中的光线生成机制。

八面体光线生成

《原理篇》中我们讲到 Lumen 是一个基于 Probe 的实时 GI 系统,因此是从 Probe 发出 Ray\Cone,每个 Probe 都会 Trace 一定数量的光线,这个数量由 CVars GLumenScreenProbeTracingOctahedronResolution 控制,默认为 8,即每个 Probe 可以进行 8x8=64 次 Tracing,64 条光线。

这些光线是以在 Probe 为球心的球面上进行分布的,为了使分布均匀及便于存储访问,Lumen 采用八面体法来实现球面方向与 2D 纹理空间坐标进行转换,这样可以将所有 Probe 的 Tracing 存储在一个 2D Texture Atlas 中,其中每个 Probe 的所有 Tracing 对应 Atlas 上的一块正方形 Texel 区域,其中每个 Texel 即对应 Probe 的一个 Tracing(Ray),如下图所示:

有了这样的映射关系,具体在 Compute Shader 的实现中,可以将每个 Probe 对应一个 2 维 Thread Group,Thread Group 的线程数为 Probe 的 Tracing 数量,例如 [8,8,1],这样就可简单的用 Shader 的系统语义值 SV_GroupThreadID 作为 Probe Altas Texels 坐标,再用这个坐标用八面体法计算出在球面上的 Tracing 方向。

结构化重要性采样

Lumen 采用了一种称为结构化重要性采样(Structured Importance Sampling)的机制,其核心思想是将 Probability Density Function(下简称 PDF)高的样本分配给低 PDF 的样本,并且将高 PDF 样本进行细分(Subdivision)作为超采样,从而形成层次化结构,如下图所示,一个高 PDF 的 Tracing 被细分为 4 个:

为了将 Structured Importance Sampling 与前面所说的八面体光线生成的机制整合,Lumen 增加了一个间接层,这是一个名为 Lumen.ScreenProbeGather.RayInfosForTracing 的 Render Dependency Graph(RDG)2D Texture,用于存储每个 Probe Tracing 对应的八面体 Texel 坐标信息,这其中包括了需要重定向的 Tracing。当某个 Tracing(Octahedral Texel)被重定向时,只需修改这个 Tracing 的细分的八面体 Texels 坐标以及细分的 Level 即可,对于没有重定向的 Texel,其八面体 Texel 坐标即是当前 Texel 坐标。如下图所示:

PDF

回顾下 Monte Carlo 积分形式的渲染方程:

其中 为 , 为入射光照(Incident Radiance),传统上会对 Monte Carlo 积分的这两项进行重要性采样,Lumen 也是如此。对于 BRDF,Lumen 使用三阶球谐将每个 Probe 周围有效采样点的 Normal 投影到 SH 上,然后累加起来计算 SH 系数均值作为 Screen Probe 的 SH PDF,最后在生成光线时用 Tracing 的方向与 Probe 的 SH PDF 点积计算出每个 Tracing 的 PDF。

而对于 Incident Radiance 的 PDF 由于实时性能需求不能如传统方法对光源进行采样,而是通过重投影到上一帧的 Screen Space Radiance Cache 并平均四个相邻的 Screen Probe 作为 PDF,因此这样只是近似值并不准确,但由于 BRDF 是精确的,最终会结合 BRDF PDF 综合判断作为 Tracing 重定向的依据。下文会根据源码详细阐述 BRDF 和 Incident Radiance 的 PDF 计算过程以及重定向判断机制。

生成 PDF

Lumen 的 Importance Sampling 的源码结构比较清晰,基于 GPU Driven。CPU 端逻辑都集中在 LumenScreenProbeImportanceSampling.cpp 中,基本都是 Shader 和 RDG 资源和参数的配置代码,其中 GenerateBRDF_PDF 是 生成 BRDF PDF 的入口函数,在 Final Gather 的流程中被调用。GenerateImportanceSamplingRays 函数中有两个 Pass,一个是生成 Incident Radiance PDF 的 ComputeLightingPDF Pass,一个是生成结构化重要性采样数据的 GenerateRays Pass。GPU 端源码都在 LumenScreenProbeImportanceSampling.usf Shader 中,由于是 GPU Driven,因此 PDF 生成的核心逻辑都在 Shader 中,下面主要是根据 Shader 源码进行讲解。

BRDF PDF

如上所述生成每个 Screen Probe 的 BRDF 的三阶球谐 PDF,此步骤在放置 World Space Probe (在之后的《Radiance Cache 篇》会详细阐述)之后执行。Pass 信息如下:

  • Pass 名称:ComputeBRDF_PDF
  • Pass 类型:Compute
  • Shader 函数:ScreenProbeComputeBRDFProbabilityDensityFunctionCS
  • 线程组大小:8x8
  • 线程组数量:Screen Probe 数量

实际上这一步其实并没有真正生成 BRDF PDF,而是生成了每个 Probe 的 BRDF 的三阶 SH 系数,在后续的 GenerateRays Pass 中才使用这个 SH 计算真正的 PDF。主要思路是以 Screen Probe 为中心采样周围一定数量的 Pixel,计算对 Probe 的影响权重,如果有影响则将 Pixel 的 BRDF 转换为三阶 SH,最后累计所有对 Probe 有影响的 SH 系数,加权平均得到 Probe 的 BRDF 的 PDF,最终输出到名为 Lumen.ScreenProbeGather.BRDFProbabilityDensityFunctionSH 的 RDG Buffer 中,为了优化,存储格式为 float16。

每 Thread Group 对应一个 Screen Space Probe,每 Thread Group 有 8x8=64 个线程,每线程根据当前 GroupThreadId 计算 Pixel Offset,在当前 Probe 周围采样 G-Buffer,判断 Probe 是否与 Pixel 所在平面很接近,如果是则根据 Pixel 的 Normal 生成三阶 SH 基函数的 9 个系数,存储在 LDS 中,并累计 SH 数量。源码如下:

上述算法示意图如下所示,其中黄色圆圈表示 Probe,蓝色箭头为 Probe 所有的 Tracing 方向,虚线为 Sample 所在的 Plane,绿色圆圈代表对 Probe 有影响的 Sample(与 Probe 几乎共面),红色则代表没有影响的 Sample,黑色箭头为 Sample Normal:

接下来将 LDS 中所有 SH 基函数系数累加求和,最终将这些 SH 系数和再除以 SH 数量得到归一化的 PDF。注意这里不是一个线程进行求和,而是整个 Thread Group 的所有线程并行求和,具体算法如下:

每循环处理 4 个 SH 系数和,循环结束时将 4 个 SH 系数和存储到 LDS 对应的偏移位置中,这样每线程累加 4 个 SH 系数,所有线程并行执行,如下图所示:

注意在每次循环结束时还会判断是否还有可以处理的 SH 系数,如果有则继续循环处理。例如如果当前 Thread Group 有 64 个 SH 系数,则由线程 0~15 将 64 个 SH 每 4 个一组求和写入到 16 个 LDS 中,第二次循环则由线程 0~4 将 16 个 SH 系数和每 4 个一组求和写入到 4 个 LDS 中,第三次循环则只有线程 0 将 4 个 SH 系数求和写入到 1 个 LDS 中,这样就完成了所有 SH 的并行求和,代码如下:

完成了所有 SH 求和之后,最终将当前 Probe 的 BRDF SH 9 个系数和求平均后写入到 Lumen.ScreenProbeGather.BRDFProbabilityDensityFunctionSH 中。需要说明的是,在写入时也是并行执行,由每组前 9 个线程负责写入线程索引对应的 SH 系数,并且在写入前将这些 SH 系数和除以 SH 数量以得到归一化的 PDF。

BRDFProbabilityDensityFunctionSH 除了用于计算 BRDF PDF 之外,在  ScatterScreenProbeBRDFToRadianceProbes Pass 中还用于将 BRDF SH 扩散到 8 个 World Radiance Probe 中作为每个 Probe BRDF 的参数化系数,在后续的《Radiance Cache 篇》中会详细阐述。

Lumen 还实现了一套不使用 SH 的 BRDF PDF 计算机制,可以通过 BRDF_PDF_SPHERICAL_HARMONIC 宏控制是否启用,出于篇幅限制,这里就不进行源码解析,只阐述算法机制。前面部分与 SH 方式类似,选出与 Probe 共面的有效采样点,使用每个 Tracing 方向与有效采样点的法线点积作为 PDF,遍历所有采样点求出最大值作为当前 Tracing 的 PDF,保存在名为 Lumen.ScreenProbeGather.BRDFProbabilityDensityFunction RGD Texture 中,格式为 PF_R16F。在生成光线时直接从中加载每个 Tracing 对应的 PDF 进行计算。但是由于需要为所有 Probe 的所有 Tracing 都记录 PDF,因此这种方式所需的存储空间要比 SH PDF 方式大很多,以 64 个 Tracing 为例,SH 方法每个 Probe 仅需存储 9 个 16bit 系数,而这种方式每个 Probe 需要 64 个 16bit,所需内存比 SH 方法大了 7 倍。从上述可以看出,与 SH 方法相比本质上都是使用 Tracing 的方向与采样点法线点积计算 BRDF PDF,不同之处在于 SH 方式是计算 Probe 的 BRDF SH 的均值,且计算真正的 PDF 是在之后进行;而非 SH 方法则是先计算出 PDF,并选出最大值作为后续的判断依据。下面是 SH 和非 SH 方式的效果图对比,可以明显看出在噪点、漏光方面,SH 方式明显比非 SH 更好。

非 BRDF SH

BRDF SH

非 BRDF SH

BRDF SH

Incident Radiance PDF

计算每个 Screen Probe 的所有 Tracing 的 Incident Radiance PDF,Pass 信息如下:

  • Pass 名称:ComputeLightingPDF
  • Pass 类型:Compute
  • Shader 函数:ScreenProbeComputeLightingProbabilityDensityFunctionCS
  • 线程组大小:4x4、8x8 或 16x16,由 CVars GLumenScreenProbeTracingOctahedronResolution 控制,默认为 8x8。
  • 线程组数量:Screen Probe 数量

将当前帧 Screen Probe 重投影到上一帧,在当前 Screen Probe 周围范围内随机访问上一帧的 Neighbor Probes,判断每个 Neighbor Probe 是否对当前 Probe 有影响(共面),如果有则采样 Neighbor Probes 的 Radiance 作为 PDF。

这部分功能可开关,由 CVars GLumenScreenProbeImportanceSampleIncomingLighting 控制,另外 CVars GLumenScreenProbeImportanceSampleProbeRadianceHistory 用于控制是否采样上一帧 Radiance Cache 作为判断依据。最终 Incident Radiance 的PDF 输出到名为 Lumen.ScreenProbeGather.LightingProbabilityDensityFunction 的 RDG 2D Texture 中,大小为所有 Screen Probe 数量 x 每个 Probe 的 Tracing 数量,格式为 PF_R16F。

每 Thread Group 对应一个 Screen Probe,根据线程组 ID 计算出 Screen Probe 的屏幕坐标,并获得 Probe 对应的 Depth,以此计算出 Probe 的当前帧的 NDC 坐标,用此坐标重投影到前一帧得到 HistoryScreenUV,并判断是否超出重要性采样历史边界:

如果没有超出边界,则对 HistoryScreenUV 使用 Hammersley Points 生成低差异序列进行 Jitter,获取相对于 HistoryScreenUV 在左上方向的 0~15 个像素之间随机抖动的屏幕坐标,以此作为 History Probe 的起始坐标,这样会在以当前 History Probe 为中心的 9 个 Probe 中随机选择相邻的 4 个,其效果是每个 Thread(Sample)可能使用不同的 Probes 进行插值,从而达到进一步降噪,使结果更加平滑的目的。如下图所示,蓝色为 History Probe 所在位置,对于每个 Probe 的 64 个 Tracing 来说,在绿色的 9 个 Neighbor Probe 范围内随机选择 4 个进行插值,如下图所示:

代码如下:

接下来获取当前帧 Screen Probe 的 Normal, 构建 Probe 所在的 Plane,然后遍历四个随机选择的 Neighbor Probes,计算 Neighbor Probe 与 Screen Probe Plane 的距离,如果距离很则认为对当前 Screen Probe 有影响则 History Weight 为 1,否则为 0,然后根据当前线程计算 Neighbor Probe 的采样坐标,从前一帧的 Screen Space Radiance Cache 中采样 Radiance,乘以前面计算的 History Weight 作为最终 Radiance 并累加起来,同时累计 History Weight。代码如下:

接下来除以累计权重得到 Lighting,并计算透明值,这个值决定接下来是否采样当前帧的 Radiance Cache。如果累计权重大于等于 4,透明度为 0,说明所有 4 个 Neighbor Probes 都对当前 Screen Probe 都有影响,无需采样当前帧 Radiance Cache,否则说明至少有一个 Neighbor Probe 没有影响,需要采样当前帧 Radiance Cache 作为 Lighting 的补充。透明度计算如下红框内代码所示:

接下来是对 Radiance Cache 的采样,首先根据当前 Screen Probe 的屏幕位置获取需要采样对应的 Probe 坐标,这里同样也使用了 Hammersley Points 来随机 Jitter,这样每个 Thread 可能会对应到不同的 Probe 坐标。然后对这个坐标使用 Blue Noise 再次进行 Jitter,得到带有随机偏移的 Probe 像素中心点坐标,再用此坐标加上当前线程对应的偏移,除以 Probe 的八面体分辨率(每维度的 Tracing 数量),就得到了八面体的 ProbeUV:

如果透明度不为 0,则对 Radiance Cache 通过 Cone Tracing 进行采样,这需要计算 Cone 的方向和 Cone Half Angle,首先根据 ProbeUV 通过八面体法计算出球面方向,然后根据 Tracing 数量计算出均匀分布在球面上的 Cone Half Angle,先调用 GetRadianceCacheCoverage 函数判断 Cone Tracing 方向是否有效覆盖,如果有效则调用 SampleRadianceCacheInterpolated 采样 Radiance Cache,然后乘上透明值,等同于于乘上了采样 Radiance Cache 的权重,然后累加到 Lighting,否则直接将 Lighting 设置为 1:

GetRadianceCacheCoverage 和  SampleRadianceCacheInterpolated 函数属于 Radiance Cache 机制,为了不引入更多的复杂性,便于讲解,本篇不进行源码解析,之后的《Radiance Cache 篇》会详细讲解源码机制。

接下来使用 OctahedralSolidAngleLUT 函数(下文详细阐述)计算 ProbeUV 对应八面体在球面上的 Solid Angle,在八面体上的 Texel 不同 Solid Angle 也不同。再计算 Lighting 的亮度值乘上 Solid Angle,由于 Lighting 是采样自 Radiance Cache,也就是 Radiance,而 Radiance 定义是单位面积单位立体角接收到的能量,因此乘上 Solid Angle 就是在单位面积内接收到的能量,即 Irradiance,由于当 PDF 完全正比于被积函数时,方差为 0,因此将 Irradiance 直接作为 PDF 使用,最后由当前线程作为索引将 PDF 写入到 LDS 中。

接下来使用并行求和统计出在 LDS 中存储的所有 PDF 的总和,算法与前面阐述的 BRDF PDF 并行 SH 求和相同,不再赘述。最后由每个线程负责将当前线程的 PDF 写入到 Lumen.ScreenProbeGather.LightingProbabilityDensityFunction 中,注意还要除以 PDF 总和,这样存储的值为 0~1 之间的归一化的值,同时还增加了防止除零错误的错误。

Octahedral Solid Angle LUT 生成机制

最后说一下前面讲到的 OctahedralSolidAngleLUT 函数。顾名思义这个函数通过一张 Octahedral Solid Angle LUT 查询每个八面体坐标对应的球面方向的立体角,由于 LUT 是固定分辨率,因此需要根据八面体分辨率进行缩放,例如 LUT 为 16x16,八面体分辨率为 8x8,Solid Angle 需要放大 2x2=4 倍,代码如下:

Octahedral Solid Angle LUT 是一张名为 OctahedralSolidAngleTexture 的 RGD 2D Texture,格式为 PF_R16F,分辨率大小由 CVars GLumenOctahedralSolidAngleTextureSize 控制,默认为 16。LUT 可视化如下图,由于每个八面体 Texel 在球面的分布不同,Solid Angle 大小也不同:

LUT 由 Compute Shader 生成,Shader 源码在 LumenScreenProbeGather.usf 的 OctahedralSolidAngleCS 函数。每线程计算一个八面体 Texel 对应的 Solid Angle,调用 OctahedralSolidAngle 函数,写入到 LUT 中:

OctahedralSolidAngle 函数计算 Solid Angle 的机制是根据八面体 Texel 坐标计算出 Texel 的四个方向,作为球面四边形的顶点,从微分角度看可认为是球面上的正方形,即由 2 个球面三角形组成,然后调用 ComputeSphericalExcess 函数计算每个三角形的 Spherical Excess,即 Solid Angle,相加即得到这个球面四边形即八面体 Texel 的 Solid Angle。示意图如下:

源码如下:

ComputeSphericalExcess 函数使用 https://en.wikipedia.org/wiki/Spherical_trigonometry#Area_and_spherical_excess 中描述的算法,通过三个单位长度的球面三角形顶点向量来计算 Spherical Excess:

生成结构化重要性采样数据

这一步的 Pass 名称起的有误导性,并非实际生成光线,而是根据 BRDF PDF 和 Incident Radiance PDF 生成结构化重要性采样数据,即前面原理中所述的中间层信息。Pass 信息如下:

  • Pass 名称:GenerateRays
  • Pass 类型:Compute
  • Shader 函数:ScreenProbeGenerateRaysCS
  • 线程组大小:4x4、8x8 或 16x16,由 CVars GLumenScreenProbeTracingOctahedronResolution 控制,默认为 8x8。
  • 线程组数量:Screen Probe 数量

每 Thread Group 处理一个 Screen Probe,每线程处理一个 Tracing,根据 BRDF PDF 和 Incident Radiance PDF 生成结构化重要性采样数据,并将结果保存在名为 Lumen.ScreenProbeGather.RayInfosForTracing 的 RDG 2D Texture 中,Texture 大小为总计的 Screen Probe 数量(包含 Uniform 和 Adaptive 放置的 Screen Probe)乘以每个 Probe 的 Tracing 光线的数量,格式为 UINT16。

BRDF PDF

首先根据 Thread Group ID 获取当前 Probe Index,以此获取 BRDF PDF 的 9 个 SH 系数。再根据当前组内线程 ID 计算八面体对应的平面 UV 坐标,转换成当前 Tracing 的世界空间方向,用此方向计算三阶 SH 基函数的 9 个系数,与 Probe 的 BRDF SH 系数进行点积,即得到当前 Tracing 方向 PDF。

如果没有使用 BRDF SH 机制,则只需简单的采样之前  Lumen.ScreenProbeGather.BRDFProbabilityDensityFunction 获取每个 Tracing 的 PDF 即可,如代码中灰色部分所示。

Incident Radiance PDF

接下来是获取 Incident Radiance PDF,如果启用,则根据 Probe 索引以及当前 Tracing 坐标从 LightingProbabilityDensityFunction 中获取 Incident Radiance 的 PDF,使用 Probe Tracing 的数量进行缩放,再与前面得到的 BRDF PDF 相乘作为最终 PDF,由于 Incident Radiance 通过前一帧的 Radiance Cache 重投影插值计算得来,因此不够精确,不能作为 PDF Culling 依据,因此为了处理这种情况,当这条光线的 BRDF PDF 有效时,即使 2 个 PDF 相乘的结果很小,也认为是有效 PDF。最后将 Tracing 坐标和 PDF Pack 为 uint2 并写入当前 Tracing 索引对应的名为 RaysToRefine 的 LDS 中。这部分代码如下:

PDF 排序

当所有 Tracing 的 PDF 完成写入 LDS 后,将 LDS 中的 Tracing 按照 PDF 升序排序,目的是为下一步细化(Refine)处理准备。这里进行的是并行排序,由每线程遍历 LDS 中所有 Tracing 的 PDF,将当前 Tracing 写入到所有小于当前 Tracing 之后,如果 PDF 相同,则按照 Tracing 索引降序排序,注意这里不是原地写入 LDS,而是偏移到所有 Tracing 之后写入,因此 LDS 是双倍大小。这样当所有线程执行完成之后,LDS 从偏移之处存储的自然就是按照 PDF 升序排序的结果。代码如下:

细化(Refine)

这一步是生成结构化重要性采样数据的核心逻辑,算法思想是将小于一定阈值 PDF 的 Tracing 细化匹配到大 PDF 的 Tracing 上,PDF 越小匹配到的 PDF 就越大,具体流程如下:

  1. 由于 PDF 已经按升序排序,因此每三个光线为一组进行细化,以三个中最大的 PDF 作为判断依据。
  2. 判断三个中最大 PDF 是否小于 MinPDFToTrace 阈值,这个阈值由 CVars GVarLumenScreenProbeImportanceSamplingMinPDFToTrace 决定,默认为 0.1。
  3. 如果上一步成立,则将对应的更大 PDF 的光线坐标重新分配给当前线程对应的光线并细化。细化方法与 Mipmap 机制相同,将重新分配的光线坐标放大一倍,即 Mipmap 降低一级,同时根据当前线程索引设置三个光线在新 Mipmap 中的局部坐标,分别为(1,0)、(0,1)、(1,1),而(0,0)就是被匹配到的光线,这样就形成了一个 Quad。最后将对应的 Level 降级,并且为防止重复匹配,将 PDF 设置为 0。
  4. 累计被细分光线匹配到的光线数量,这是为了修改被匹配到的光线信息所使用,具体在下面详述。

上述流程代码如下,红框标出流程序号:

接下来再修改被匹配到的光线信息。根据上面第 4 步得到的被细分光线匹配到的光线数量,将当前线程对应的被匹配到的光线信息中的 Level 降级(因为分辨率更高),同时设置 PDF 为 0。如果将被匹配的光线的局部坐标看作(0,0)的话,其实本质上就是与上面三个光线共同构成了细分的 2x2 Quad 结构。代码如下:

最后,每个线程将光线对应的八面体 Texel 坐标和 Mip Level 写入,这样在后续的步骤中可以根据这两个数据生成 Probe Tracing 方向,代码如下:

其中 WriteRay 将 Texel 坐标和 Level Pack 为 16 bit uint 存储到 Lumen.ScreenProbeGather.RayInfosForTracing 中,代码如下:

使用结构化重要性采样数据

结构化重要性采样数据在 GetProbeTracingUV Shader 函数中使用,这个函数在 Screen Probe Tracing 时被调用,返回八面体 UV 和 Cone Angle,用于生成 Ray\Cone Tracing 的方向,因此也就实现了 Tracing 的重定向。当启用 STRUCTURED_IMPORTANCE_SAMPLING 宏时,会采样前面生成的结构化重要性采样数据 Lumen.ScreenProbeGather.RayInfosForTracing,获取每个光线对应的八面体 Texel 坐标和 Mip Level,根据 Level 计算八面体 Tracing 的分辨率(MipSize),级别越低分辨率越大,Level 数量由 CVars GLumenScreenProbeImportanceSamplingNumLevels 决定,默认为 1,即有 0 和 1 两个 Level。然后根据 Tracing 对应的八面体 Texel 和 MipSize 计算八面体 UV,这中间还加上纹素中心点的扰动。由于是 Cone Tracing,因此需要计算 Cone Half Angle,这里是根据 MipSize 来计算,MipSize 代表一个维度的 Texel 数量,每个 Texel 代表一个光线,因此总计光线数量为 ,除以这个数量既是每个光线的均值,由于计算的是 Half Angle,因此余弦值的范围在 0~1,且在这个范围内余弦值越大角度越小,因此用 1 减去这个值就是在半球上均匀分布的余弦值,再通过反余弦函数就可以计算出 Cone Half Angle,这样可以在球面上生成无重叠且均匀大小的 Cone,如下图所示:

这部分代码如下图红框所示:

结构化重要性采样数据的另一个用途在后续的 CompositeTraces Pass 中用于根据重定向后的 Tracing 方向和 Level 计算 Solid Angle,在后续的文章中会详细进行阐述。

编辑于 2022-07-10 17:03

游戏引擎

Lumen

UE5

​赞同 44​​3 条评论

​分享

​喜欢​收藏​申请转载

3 条评论

​切换为时间排序

写下你的评论...

发布

  • 知乎用户IP 属地中国香港 · 07-15

    大佬啥时候更新

    ​赞​回复​踩​ 举报

  • 知乎用户IP 属地上海 · 07-11

    "明显看出在噪点、漏光方面,SH 方式明显比非 SH 更好", 卧槽,我歇菜了,我是真看不出来

    ​赞​回复​踩​ 举报

  • 无知小白回复知乎用户IP 属地上海 · 07-15

    看那些黑白斑和漏光条的大小,使用SH后,会被一定的低频化。

    ​赞​回复​踩​ 举报

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 您可以使用Lumen来构建API和Microservices,以及快速构建和维护您的应用程序后端。具体操作方法可以参考官方文档,可以了解更多关于如何使用Lumen的信息:https://lumen.laravel.com/docs/5.6 ### 回答2: UE5 Lumen是虚幻引擎5中全新的全局光照系统,它能够提供卓越的视觉效果和更高质量的实时光照渲染。下面我将简要介绍UE5 Lumen的一些使用方法。 首先,启用Lumen需要确保项目使用的是虚幻引擎5。在项目设置中,可以找到“Renderer Settings(渲染设置)”并将全局光照模式切换为“Lumen”。 使用Lumen之后,您可以通过调整Lumen的各种参数和属性来获得期望的光照效果。在虚幻编辑器中,可以通过在“World Settings(世界设置)”面板中找到Lumen相关的设置。可以调整光源的影响范围、默认反射率、灯光传播速度等。 此外,Lumen还通过使用Virtual Shadow Maps(虚拟阴影贴图)的方式提供了一个方便的动态阴影解决方案。这些虚拟阴影贴图将在运行时生成,可以覆盖较大范围的场景并提供更高质量的动态阴影效果。 Lumen还提供了自适应全局光照(ALGI)功能,这意味着光照会根据场景中的物体进行实时计算,以提供更真实的光照效果。在项目中启用ALGI后,可以在“World Settings”面板中的Lumen设置下找到对应选项。 最后,Lumen还支持与虚幻引擎5中的其他特性和工具的集成。例如,您可以将LumenLumen的虚拟光源、物理基础解算器和其他虚幻引擎5功能一起使用,以获得更出色的效果。 总之,UE5 Lumen是虚幻引擎5中一款非常强大和灵活的全局光照系统。通过了解和使用Lumen相关的设置和功能,开发者可以在项目中获得更真实、更高质量的光照效果。 ### 回答3: UE5 Lumen是一项用于实时光线追踪的技术,以下是关于如何使用UE5 Lumen的相关信息。 首先,要使用UE5 Lumen,您需要打开UE5编辑器并创建一个新的项目或打开现有的项目。然后,您需要确保您的项目使用的是UE5版本,因为Lumen仅在UE5中可用。 一旦项目打开,您可以通过导航到“项目设置”>“引擎”>“渲染器设置”来启用Lumen。在这里,您可以找到一些Lumen相关的选项以及不同的属性进行调整。这些属性包括光线传输的距离、光照强度、间接光照的质量等等。 在设置完Lumen的属性后,您可以在场景中开始使用Lumen。您可以在场景中添加光源,并通过调整其属性来控制光照的参数,例如光照强度、颜色等等。 Lumen将根据这些光源的位置和属性来计算场景的光照效果。 另外,您还可以使用Lumen的虚拟光源功能。虚拟光源是一种模拟真实光源的方法,允许您创建没有直接光照但会产生间接光照效果的区域。您可以在场景中将虚拟光源放置在需要间接光照的区域,并调整其属性以实现所需的效果。 值得注意的是,Lumen需要消耗较多的计算资源,因此在使用Lumen时需要权衡计算性能和图形质量。您可以在场景中选择性地启用Lumen,以确保在处理复杂场景时获得平衡的性能和效果。 总的来说,UE5 Lumen是一项强大的实时光线追踪技术,可以为UE5项目带来真实而逼真的光影效果。通过在项目设置中启用Lumen,并合理调整光源的属性,您可以在场景中使用Lumen并在渲染时体验到其带来的改进。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值