3D 灯光和阴影

前言

光源发出灯光,灯光会与材质相混合,产生可见的结果。场景可以有很多不同类型的光源:

  • 来自材质本身的自发光颜色(但是无法影响附近的对象,除非进行了烘焙,或者启用了屏幕空间间接光照)。

  • 灯光节点:DirectionalLight3D、OmniLight3D、SpotLight3D。

  • Environment反射探针 中的环境光。

  • 全局光照(LightmapGIVoxelGISDFGI)。

自发光是材质的属性。更多相关内容见 标准 3D 材质与 ORM 3D 材质 教程。

参见

可以使用 3D 光影演示项目动态比较不同类型的灯光。

灯光节点

灯光节点有三种:DirectionalLight3DOmniLight3DSpotLight3D。让我们来看看灯光的通用参数:

../../_images/light_params.png

每个属性都有特定的功能:

  • Color:发光的基础颜色。

  • Energy:能量乘数。这对于使灯光饱和或使用 高动态范围光照 非常有用。

  • Indirect Energy:间接能量。用于间接光(反弹的光)的次级乘数。适用于 使用光照贴图全局照明、VoxelGI 和 SDFGI。

  • Volumetric Fog Energy:体积雾能量。用于体积雾的次级乘数。仅在启用体积雾时有效。

  • Negative: (减色)光变为减色而不是添加。对于手动补偿一些黑暗角落有时很有用。

  • Specular:镜面反射。影响受此光影响的物体中镜面反射斑点的强度。值为零时,该光变为纯漫反射光。

  • Bake Mode:设置灯光的烘焙模式。见 使用光照贴图全局照明

  • Cull Mask: (剔除遮罩)在下面选定的图层中的物体将受到此光的影响。请注意,通过这个剔除遮罩禁用的对象仍然会投射阴影。如果你不希望被禁用的物体投射阴影,请将 GeometryInstance3D上的 Cast Shadow 属性调整为所需的值。

参见

如果你希望使用真实世界的单位来配置灯光的强度和色温,请参阅 物理灯光和相机单位

灯光数量限制

在使用 Forward+ 渲染器时,Godot 采用*集群*方法进行实时照明。只要性能允许,可以添加任意数量的灯光。不过,默认情况下,当前摄像机视图中的 集群元素 上限仍为 512 个。集群元素是指全向光灯、聚光灯、 贴花反射探针。可以通过调整 “渲染 > 限制 > 群集生成器 > 最大群集元素“高级项目设置来增加此限制。

使用 Forward 移动渲染器时,每个网格资源有 8 个 OmniLight 加 8 个 SpotLight 的限制。此外,在当前相机视图中可渲染的 OmniLight 和 SpotLight 数量限制为 256 个。这些限制目前无法更改。

使用兼容渲染器时,每个网格资源最多可渲染 8 个 OmniLight 加 8 个 SpotLight。在高级项目设置中,可以通过调整 “渲染 > 限制 > OpenGL > 最大可渲染光源数“和/或 “渲染 > 限制 > OpenGL > 单对象最大光源数“来增加该限制,但代价是会影响性能并导致较长的着色器编译时间。也可以降低该限制,以减少着色器编译时间并略微提高性能。

在所有的渲染方法中,最多可以同时显示 8 个 DirectionalLight。但是,每增加一个启用阴影的 DirectionalLight,都会降低每个 DirectionalLight 的有效阴影分辨率。这是因为所有灯光共享方向阴影图集。

如果超过了渲染限制,灯光就会在摄像机移动过程中跳进跳出,这可能会分散注意力。在灯光节点上启用 Distance Fade 有助于减少这一问题,同时还能提高性能。将网格分割成更小的部分也会有所帮助,尤其是关卡几何体(这也能提高剔除效率)。

如果你需要渲染的灯光数量超过了给定渲染后端所能提供的数量,请考虑使用 baked lightmaps,并将灯光的烘焙模式设置为 Static。这样就可以完全烘焙光照,从而加快渲染速度。你也可以使用任何全局光照技术的自发光材料来替代在大范围内发出光线的光节点。

阴影贴图

灯光可以可选地投射阴影. 这使它们具有更好的真实感(光线不会照到被遮挡的区域), 但它会带来更大的性能开销. 有一个通用阴影参数列表, 每个参数也有一个特定的功能:

  • Enabled: 选中以启用此灯光下的阴影贴图。

  • Opacity: (不透明度)被遮挡的区域会因该不透明度系数而变暗。默认情况下,阴影是完全不透明的,但是可以更改此设置,以使阴影对于给定的光线来说是半透明的。

  • Bias:(偏置)当此参数太小时,阴影会打在物体自己身上。当太大时,阴影会与物体本体分开。请调整到最适合你的状态。

  • Normal Bias:当此参数太小时,阴影会打在物体自己身上。当太大时,阴影会与物体本体分开。请调整到最适合你的状态。

  • Transmittance Bias:(透射率偏置)当此参数太低时,启用透射率的材质上,阴影会打在物体自己身上。如果太高,阴影将不会影响始终启用透射率的材质。请调整到最适合你的状态。

  • Reverse Cull Face:反转表面剔除,当阴影贴图使用反转表面剔除渲染时,在某些场景表现更好。

  • Blur: (模糊)倍增此灯光的阴影模糊半径。它适用于传统阴影贴图和接触硬化阴影( Angular DistanceSize 大于 0.0 的灯光)。数值越大,阴影越柔和,对于移动的物体来说,在时间上也会显得更加稳定。增加阴影模糊的缺点是,它会使用于滤波的颗粒图案更加明显。另请参阅 阴影过滤模式

../../_images/lights_and_shadows_blur.webp

调整阴影偏置

下图是调整偏置的图像。默认值适用于大多数情况,但通常来说,它取决于几何的大小和复杂程度。

如果给定灯光的 Shadow BiasShadow Normal Bias 设置得太低,阴影就会 “涂抹 “到物体上。这将导致光线的预期外观变暗,称为 阴影失真 (shadow acne):

../../_images/lights_and_shadows_acne.webp

另一方面,如果给定光线的 Shadow BiasShadow Normal Bias 设置得太高,阴影可能看起来与物体脱节。这被称为 阴影悬浮 (peter-panning):

../../_images/lights_and_shadows_peter_panning.webp

一般来说,增加 Shadow Normal Bias 比增加 Shadow Bias 更可取。增大 Shadow Normal Bias 不会像增大 Shadow Bias 那样导致更多的阴影悬浮,但仍能有效解决大多数阴影失真问题。增加 Shadow Normal Bias 的缺点是会使某些物体的阴影看起来更薄。

任何偏置问题都可以通过 提高阴影贴图分辨率 来解决,尽管这可能会导致性能下降。

备注

调整阴影贴图设置是一门艺术,没有 “放之四海而皆准”的设置。为了获得最佳的视觉效果,你可能需要针对每个灯光使用不同的阴影偏置值。

关于外观变化的注意事项:在光源上启用阴影时,请注意,与在兼容性渲染器中未启用阴影渲染时的光源外观相比,光源的外观可能会发生变化。由于较旧移动设备的限制,阴影是使用多通道渲染方法实现的,因此带有阴影的光源在 sRGB 空间中渲染,而不是在线性空间中。渲染空间的这种变化有时会极大地改变光源的外观。为了获得与未启用阴影的光源相似的外观,你可能需要调整光源的能量设置。

平行光

这是最常见的光线类型, 代表了很远的光源(如太阳). 它也是计算中最便宜的光, 应该尽可能使用(虽然它不是计算起来最便宜的阴影贴图, 但这点稍后再说).

平行光模拟覆盖整个场景的无限数量的平行光线。平行光节点由指示光线方向的大箭头表示。但是,节点的位置根本不会影响照明,它可以在任何地方。

../../_images/light_directional.png

每个表面的正面被光线照射,而其他部分则保持黑暗。与大多数其他类型的光不同,平行光没有特定的参数。

平行光还提供 Angular Distance (角度距离)属性,该属性确定光的角度大小(以度为单位)。 将其增加到 0.0 以上将使阴影在距离光源更远的地方变得更柔和,同时也会影响太阳在程序天空材质中的外观。 这称为 接触硬化( contact-hardening ) 阴影(也称为 PCSS)。

作为参考,从地球看太阳的角距离约为 0.5。这种阴影的性能消耗资源较高,因此如果在启用阴影的灯光上将此值设置为高于 0.0,请查看 PCSS 建议 中的建议。

方向光阴影贴图

为了计算阴影贴图,从覆盖整个场景(或最大距离)的正交角度渲染场景(仅深度)。但是,这种方法存在一个问题,因为靠近相机的物体接收到的低分辨率阴影可能看起来是块状的。

为了解决这个问题,一种名为平行分层阴影贴图 (Parallel Split Shadow Maps, PSSM) 的技术被使用。该技术将视锥体分割成 2 或 4 个区域,每个区域都有自己的阴影贴图。这允许靠近观察者的较小区域拥有与远处巨大区域相同的阴影分辨率。当为 DirectionalLight3D 启用阴影时,默认阴影模式为 4 分裂的 PSSM。在物体足够大以出现在所有四个分裂区域中的场景中,这会导致绘图调用增加。具体来说,这样的物体将被总共渲染五次:每个阴影分裂区一次,以及最终场景渲染一次。这会影响性能,因此理解此行为对于优化场景和管理性能预期非常重要。

../../_images/lights_and_shadows_pssm_explained.webp

有了它, 阴影变得更加详细:

../../_images/lights_and_shadows_directional_mode.webp

为了控制PSSM, 暴露了许多参数:

../../_images/lights_and_shadows_directional_shadow_params.webp

每个分割距离都是相对于相机最远处进行控制的(如果大于 0 ,则为阴影 Max Distance(最大距离) )。 0.0 是眼睛位置, 1.0 是阴影在一定距离处结束的位置。分割介于两者之间。默认值通常效果很好,但一般会调整第一个分割数值,以便为近处对象提供更多细节(比如第三人称游戏中的角色)。

请务必根据场景需要设置阴影的 Max Distance 。最大距离越小,阴影效果越好,性能也越高,因为阴影渲染中需要包含的物体越少。你还可以调整 Fade Start 来控制远处阴影淡出距离的强度。对于 Max Distance 完全覆盖任何给定摄像机位置的场景,可以将 Fade Start 增加到 1.0 ,以防止阴影在远处渐变。在 Max Distance 没有完全覆盖场景的场景中,不应该这样做,因为阴影会在远处突然消失。

有时,一个分割与下一个之间的过渡看起来很糟糕。要解决此问题,可以打开 Blend Splits (混合分割)选项,牺牲细节和性能以换取更平滑的过渡:

../../_images/blend_splits.png

Shadow > Normal Bias 参数可用于修复当对象垂直于光线时自阴影的特殊情况。唯一的缺点是它会使阴影变得更薄。在大多数情况下,在增加 Shadow > Bias 之前,请考虑增加 Shadow > Normal Bias

最后,如果未细分网格的大型对象出现了阴影缺失的情况,可以调整 Pancake Size(压平区大小)属性来修复。只有在发现阴影缺失与阴影偏置问题无关时,才可以更改此值。

全向光

全向光是一种点光源,可在所有方向上发射光,直至给定的半径。

../../_images/light_omni.png

在现实生活中,光衰减是个和距离成反比的函数,这意味着全向光没有半径。这是一个问题,因为这意味着计算几个全向光会变得很困难。

为了解决这个问题,引入了 Range (范围)参数和衰减函数。

../../_images/light_omni_params.png

这两个参数允许调整其在视觉上的工作方式, 以便找到美学上令人愉悦的结果.

../../_images/light_attenuation.png

OmniLight3D 中还有一个 Size 参数。增大该值会使光线减弱得更慢,在远离投射者时阴影会显得更模糊。这可以在一定程度上模拟区域灯光。这就是 接触硬化 (contact-hardening)阴影(也称为 PCSS)。这种阴影的成本较高,因此在启用阴影的灯光上将此值设置为高于 0.0 时,请查看 PCSS 建议 中的建议。

../../_images/lights_and_shadows_pcss.webp

全向光阴影贴图

全向光的阴影贴图相对简单。需要考虑的主要问题是用于渲染它的算法。

全向光阴影可以渲染为 Dual Paraboloid (双抛物面)或 Cube (方形)映射。 Dual Paraboloid 的渲染速度很快,但可能会导致变形,而 Cube 更正确但速度较慢。默认值为 Cube ,但可以考虑将其改为 Dual Paraboloid ,以让光线不会产生太大的视觉差异。

../../_images/lights_and_shadows_dual_parabolid_vs_cubemap.webp

如果渲染的对象大部分是不规则且细分的,那么 Dual Paraboloid (双抛物线)通常就足够了。无论怎么说,由于这些阴影被缓存在阴影图集中(后面会详细介绍), 对于大多数场景而言,它可能不会对性能产生影响。

启用阴影功能的全向灯光可以使用投影。投影纹理会将灯光的颜色乘以纹理上给定点的颜色。因此,一旦分配了投影纹理,灯光通常会显得更暗;你可以增加 Energy 来弥补这一点。

全方位光投影纹理需要特殊的 360° 全景贴图,类似于 PanoramaSkyMaterial 纹理。

通过下面的投影纹理,可以得到以下结果:

../../_images/lights_and_shadows_omni_projector_example.webp ../../_images/lights_and_shadows_omni_projector.webp

小技巧

如果你已获得立方体贴图形式的全方位投影图像,你可以使用 这个基于网络的转换工具 将它们转换为单一的全景图像。

聚光

聚光与全向光类似,但是它们只发光到锥形(或“截断”)中。它们可用于模拟手电筒、车灯、反射器、聚光灯等。这种类型的光也会向其指向的相反方向衰减。

聚光和 OmniLight3D**共用相同的 Range(范围)Attenuation(衰减)Size(大小),并添加了两个额外参数:

  • Angle: (角度)光线的光圈角度。

  • Angle Attenuation: (角度衰减)锥形衰减,有助于柔化锥形边界。

聚光灯阴影贴图

聚光灯的阴影贴图参数与全方位光源相同。由于只需渲染一个阴影纹理(而不是渲染 6 个面,或在双 Parabolid 模式下渲染 2 个面),因此渲染聚光灯阴影贴图的速度明显快于全方位光源。

启用阴影功能的光线可以使用投影。投影纹理会将灯光的颜色乘以纹理上给定点的颜色。因此,一旦分配了投影纹理,灯光通常会显得更暗;你可以增加 Energy 来弥补这一点。

与全向光投影不同,聚光灯投影纹理不需要遵循特殊格式就能看起来正确无误。它的映射方式类似于 decal

通过下面的投影纹理,可以得到以下结果:

../../_images/lights_and_shadows_spot_projector_example.webp ../../_images/lights_and_shadows_spot_projector.webp

备注

广角聚光灯的阴影质量会低于窄角聚光,因为阴影贴图会分布在更大的表面上。角度大于 89 度时,聚光阴影将完全停止工作。如果你需要更宽的灯光阴影,请使用全向光。

阴影图集

与具有自己的阴影纹理的平行光不同,全向光和聚光被分配了阴影图集的槽位。该图集可以在高级项目设置( 渲染 > 灯光和阴影 > 位置阴影 )中进行配置。

这个分辨率适用于整个阴影图集。该图集分为四个象限:

../../_images/lights_and_shadows_shadow_quadrants.webp

每个象限可以细分,分配任意数量的阴影贴图。以下是默认细分方式:

../../_images/lights_and_shadows_shadow_quadrants2.webp

阴影图集分配空间如下:

  • 最大阴影贴图尺寸(未使用细分时)代表屏幕尺寸(或更大)的灯光。

  • 细分(较小的贴图)表示距离视图较远并且比例较小的灯光的阴影。

每一帧, 以下过程被应用于所有光:

  1. 检查灯光是否在正确大小的插槽上. 如果没有, 重新渲染它并将其移动到更大/更小的插槽.

  2. 检查影响阴影贴图的任何对象是否已更改. 如果是的话, 重新渲染光线.

  3. 如果上述情况均未发生, 则不执行任何操作, 阴影保持不变.

如果一个象限中的槽位满了,光线会被推回到更小的槽位中,取决于大小和距离。如果所有象限中的所有槽位都已满,则某些灯光即使启用了阴影,也将无法渲染阴影。

默认的阴影分配策略最多可以渲染 88 盏灯,并在相机锥体中启用阴影(4 + 4 + 16 + 64):

  1. 第一象限也是最精细的象限,可以存储 4 个阴影。

  2. 第二象限可存储 4 个其他阴影。

  3. 第三象限可存储 16 个阴影,但细节较少。

  4. 第四象限也是细节最少的象限,可存储 64 个阴影,但细节更少。

每个象限使用较多的阴影数量可以支持启用阴影的更多灯光,同时还能提高性能(因为阴影将以较低的分辨率为每个灯光渲染)。不过,增加每个象限的阴影数量的代价是降低阴影质量。

在某些情况下,你可能想要使用不同的分配策略。例如,在自上而下看的游戏中,所有灯光的大小都大致相同,你可能希望将所有象限设置为具有相同的细分,以便所有灯光都具有相似质量级别的阴影。

平衡性能与质量

阴影渲染是 3D 渲染性能方面的重要议题。做出正确的选择非常重要,这样才能避免制造出瓶颈。

平行光的阴影质量设置可以在运行时调整,调用合适的 RenderingServer 方法即可。

位置光(全向灯/聚光灯)的阴影质量设置可以在运行时调整,设置根 Viewport 即可。

阴影贴图大小

高阴影分辨率会带来更清晰的阴影,但会付出很大的性能代价。还需要注意的是,更清晰的阴影并不总是更逼真。在大多数情况下,应将其保持在默认值 4096,或将低端 GPU 的默认值降至 2048

如果在减小阴影贴图大小后,位置阴影变得过于模糊,可以通过调整 shadow atlas 象限以包含较少的阴影来解决。这样就能以更高的分辨率渲染每个阴影。

阴影过滤模式

这里可以选择多种阴影贴图质量设置。默认的**Soft Low**在性能和质量之间取得了很好的平衡,适用于有细节纹理的场景,因为纹理细节有助于使颜色抖动的纹路不那么明显。

不过,在纹理细节较少的项目中,颜色抖动的纹路可能会更加明显。要隐藏这种纹路,可以启用 时间抗锯齿(TAA)AMD FidelityFX Super Resolution 2.2 (FSR2)快速近似抗锯齿(FXAA) 或将阴影滤镜质量提高到 Soft Medium 或更高。

Soft Very Low 设置会自动减少阴影模糊,使低采样数产生的伪影不那么明显。相反,Soft HighSoft Ultra 设置会自动增加阴影模糊,以更好地利用增加的样本数。

../../_images/lights_and_shadows_filter_quality.webp

16 位与 32 位

默认情况下,Godot 使用 16 位深度纹理进行阴影贴图渲染。在大多数情况下,我们都建议使用这种方式,因为它的性能更好,而且质量也不会有明显差异。

如果禁用**16 Bits**,则将使用 32 -bits深度纹理。这可以减少大型场景和启用阴影的大型灯光中的伪影。不过,这种差异通常几乎不明显,但却会带来显著的性能损失。

灯光/阴影的距离淡出

OmniLight3D 和 SpotLight3D 提供了一些能够隐藏远距离灯光的属性。如果是在大型场景中,并且存在几十盏灯,就能够显著提升性能。

  • Enabled:(启用)控制是否启用距离淡入淡出( LOD 的一种形式)。 光线将在 Begin + Length 内淡出,之后它将被剔除并且根本不会发送到着色器。 使用它可以减少场景中活动灯光的数量,从而提高性能。

  • Begin:(开始)光线开始消失时距相机的距离(以 3D 单位表示)。

  • Shadow:(阴影)阴影开始消失时距相机的距离(以 3D 单位表示)。 与光线相比,这可用于更快地淡出阴影,从而进一步提高性能。 仅当为灯光启用阴影时才可用。

  • Length: (长度)光线和阴影淡出的距离(以 3D 单位表示)。 光线在这段距离内慢慢变得更加透明,最后完全不可见。 值越高,淡出过渡越平滑,这样的配置在相机快速移动时更合适。

PCSS 建议

百分比接近软阴影 (Percentage-closer soft shadows,PCSS) 会提供更真实的阴影贴图外观,半影( penumbra )大小根据光源(caster)与接收阴影的表面之间的距离而变化。 这会带来很高的性能成本,特别是对于平行光而言。

为了避免性能问题,建议:

  • 仅在给定时间里使用少量启用了 PCSS 阴影的灯光。 这种效果通常在大而明亮的灯光下最为明显。 较微弱的辅助光源通常不会从使用 PCSS 阴影中获益。

  • 为用户提供禁用 PCSS 阴影的设置。 在平行光上,这可以通过在脚本中将 DirectionalLight3D 的 light_angular_distance 属性设置为 0.0 来完成。 对于位置光源,这可以通过在脚本中将 OmniLight3D 或 SpotLight3D 的 light_size 属性设置为 0.0 来完成。

投影器过滤模式

投影的渲染方式也会对性能产生影响。 渲染> 纹理 > 光投影器 > 过滤 高级项目设置可让你控制投影纹理的过滤方式。 Nearest/Linear 不使用 mipmap,这会使得渲染速度更快。 然而,投影在远处看起来会有颗粒感。 Nearest/Linear Mipmaps 在远处看起来会更平滑,但从倾斜角度观看时投影会看起来模糊。 这可以通过使用 Nearest/Linear Mipmaps Anisotropic 来解决,这是最高质量的模式,但也是消耗最大的。

如果你的项目具有像素艺术风格,请考虑将过滤器设置为 Nearest 的值之一,以便投影使用最近邻过滤(nearest-neighbor filtering)。 否则,请继续使用 Linear