
阴影是让 3D 场景显得真实可信的关键细节之一,却也最容易出现明显的渲染瑕疵。本次更新让太阳光阴影更加稳定、纯净且易于调校,并将同样的改进带到了聚光灯、点光源和面光源中。
简而言之:
- 稳定的级联。 方向光阴影在摄像机移动或旋转时不再闪烁。
- 更智能的偏移。 以阴影贴图纹素为单位的单一设置,适用于从最近到最远的级联。
- 无缝光源。 点光源消除了立方体贴图接缝,并且聚光灯、点光源和面光源共享新的偏移模型。
- 更多控制。 可选择 1 到 4 个级联,控制其分割和适配方式,以及远处级联的更新频率。
- 更好的调试。 直接在视口中查看每个级联和纹素密度。
本文内容
阴影为何重要
从场景中移除阴影,一切都会显得漂浮不定。物体失去与地面的联系,深度更难辨认,光照也显得平淡。好的阴影能告诉你物体的位置、大小以及光源的方向。
糟糕的阴影同样显眼。摄像机移动时边缘爬行、表面上出现条纹,或者阴影与投射它的物体分离,都会立刻打破真实感。这正是本次更新要解决的问题。
实时阴影是如何工作的?
真实的阴影源于光线被遮挡,但实时渲染器并不追踪光线。它使用一种更经济且非常有效的技巧,称为阴影贴图:
- 从光源视角观察。 我们从光源的视角渲染场景,但只存储每个像素到最近表面的距离。结果就是阴影贴图。
- 从摄像机视角观察。 对于屏幕上的每个像素,我们计算出它在光源视角下的位置,并将其距离与阴影贴图记录的值进行比较。
- 做出判断。 如果记录中有更靠近光源的物体,则该像素处于阴影中。否则,它被照亮。
问题在于,阴影贴图是一张固定分辨率的纹理。可以把它想象成从光源处拍摄的一张低分辨率场景照片。几乎每一种阴影技术,都是在想方设法充分利用这有限的分辨率。
光线追踪和路径追踪阴影呢?
阴影贴图是一种近似方法,因此人们自然会问:为什么不直接进行光线追踪呢?在支持硬件光线追踪的 GPU 上,我们可以做到这一点:从每个可见点向光源发射一条射线,如果中途有物体遮挡,该点就处于阴影中。由于不存在纹理分辨率不足的问题,因此不会出现块状伪影、阴影失真(shadow acne)或阴影脱离(peter-panning)现象,软阴影也能精准呈现。路径追踪则更进一步,它会追踪大量在场景中反弹的随机光路,使阴影、反射和间接光照都由同一过程计算得出。Evergine 的路径追踪器示例展示了这种效果。
代价是性能。清晰的图像需要每帧每个像素发射多条光线,通常还需配合降噪器。场景需要一个加速结构,且该结构必须随物体移动而重建。此外,光线向各个方向散射,相比将三角形光栅化到纹理中,这对GPU内存的压力要大得多。即使是高端桌面GPU也会谨慎分配光线预算,而大多数手机、平板和低端设备根本没有硬件光线追踪功能。
这就是阴影贴图仍是实时阴影主力军的原因:它们成本低、性能可预测,且几乎能在所有设备上运行。在硬件允许的情况下,光线追踪是绝佳选择,但阴影贴图确保了场景在其他所有地方都能运行,这也是我们持续投入改进它的原因。
太阳难题与级联
方向光代表太阳,而太阳照亮万物。单个阴影贴图必须覆盖整个可见世界,因此一张拉伸覆盖数百米的纹理,会在玩家眼前产生块状阴影,而那里恰恰是最需要细节的地方。
经典的解决方案是级联阴影贴图。我们根据与摄像机的距离,将视野分割成多个切片:离你近的一个小切片,其后一个更大的切片,依此类推。每个切片拥有自己的阴影贴图,因此近处的阴影清晰,远处的则更粗糙。远处的物体在屏幕上很小,所以你通常不会注意到。
Evergine此前已使用级联技术。本次新版本旨在让它们表现更佳。
旧方法的不足之处
有三个问题反复出现:
- 闪烁。 每个级联都依据摄像机方向定位,因此当摄像机转动时,阴影贴图的网格会在世界中滑动。阴影边缘会爬行和闪烁。
- 阴影失真与阴影悬浮。 由于阴影贴图是采样点组成的网格,表面可能会错误地遮挡自身并产生条纹(即阴影失真,shadow acne)。常见的解决方法是添加一个微小的偏移(bias),但单一的固定值无法兼顾所有级联。偏移量太小会在掠射角处留下条纹,太大则会导致阴影与投射物分离(即阴影脱离,peter-panning),在远处的级联中尤为明显。
- 可调空间有限。 级联数量固定为四个,且对其分布方式的控制有限。
新特性
不再闪烁的级联
现在,阴影空间仅取决于光源的方向,而非摄像机的朝向。每个级联还会对齐到阴影贴图的纹素网格,其大小仅以约2%的小步长变化。因此,移动或旋转摄像机不再改变哪个纹素覆盖世界的哪个部分,闪烁现象消失了。
理解场景的偏移值
偏移(Bias)现在在世界空间中应用,并以**阴影贴图纹素(texel)**为单位进行度量。新增的两个属性 ShadowDepthBias 和 ShadowNormalBias 会根据每个级联的纹素大小以及阴影滤波器的柔和度自动缩放。现在,仅需一项设置即可从最近的级联到最远的级联都保持合理表现,从而消除掠射角处的阴影失真条纹(acne bands)以及远处的阴影悬浮现象。
聚光灯、点光源和面光源
基于像素的 Bias 模型不仅限于太阳光。聚光灯、点光源和面光源现在也使用它,因此 ShadowDepthBias 和 ShadowNormalBias 在所有光源类型上含义相同。在此过程中,我们修复了两个问题:
- 背向光源投影轴的表面上出现的阴影失真环(acne rings)。
- **点光源上的可见接缝。**点光源使用六张阴影贴图,对应立方体的每个面,而滤波器过去会读取边缘的错误数据。现在每个面的渲染角度略大于 90 度,并带有 8 像素的边距,因此相邻面会重叠,接缝随之消失。此修复仅针对点光源。
可调节的级联
方向光现在公开了质量与性能之间的权衡选项:
- **级联数量:**从 1 到 4,每个光源仅分配其所需的阴影贴图。
- **分割模式:**自动模式混合了对数分布和均匀分布,由
GammaDistribution控制。手动模式允许您决定每个级联的结束位置。 - **适配模式:**稳定(默认)将每个级联适配到其切片周围的球体,因此像素大小永不改变。紧密则适配到一个包围盒,像素大小约减小 20%,但代价是相机旋转时会出现一些闪烁。
- **选择模式:**按视图深度为每个像素选择级联,或选择包含该像素的第一个(最清晰)级联,以便更多像素使用高分辨率贴图。
- **级联混合与阴影衰减:**相邻级联可以相互混合,阴影可以在达到最大阴影距离之前平滑淡出,而非突然截断。
- **远级联更新间隔:**远处的阴影变化缓慢,因此远级联可以每 N 帧重新渲染一次。当相机和光源保持在容差范围内时,引擎会保留缓存的贴图,从而降低阴影渲染通道的开销。
**注意:**当一个光源被多个相机渲染时,更新间隔无效。
更好的调试体验
光源调试模式现在会显示每个级联的颜色(对于点光源,则显示每个立方体面的颜色),以及一个 8x8 阴影贴图像素的棋盘格,让您可以精确地看到屏幕上阴影的密度。聚光灯也拥有了自己的 DebugMode。
我们还修复了相关问题:级联分割距离现在使用正确的公式,近裁剪面为零的正交相机不再产生无效分割,并且使用多个投射阴影的方向光渲染多个相机时,不再导致光源相互覆盖彼此的阴影贴图。后端一致地处理深度钳制,因此级联前方的物体不再被裁剪。
查看效果对比
拖动滑块,在相同的场景和相机位置下对比旧版与新版阴影系统的效果。
入门指南
您可以在编辑器或代码中调整所有参数。以下数值是说明性的起点,请根据您的场景进行调整:
// Get the directional light of your sun entity
var sun = sunEntity.FindComponent<DirectionalLight>();
sun.IsShadowEnabled = true;
sun.ShadowDistance = 100; // How far from the camera shadows are rendered
sun.ShadowCascadeCount = 3; // Between 1 and 4
// Keep cascades stable, and fade shadows out smoothly
sun.CascadeFitMode = DirectionalLight.CascadeFitModes.Stable;
sun.ShadowFadeFraction = 0.1f;
sun.CascadeBlendFraction = 0.1f;
// Bias is measured in shadow map texels
sun.ShadowDepthBias = 1;
sun.ShadowNormalBias = 1;
这将启用一个带有三个稳定级联的投射阴影太阳光,级联之间平滑过渡,并在阴影最大距离处柔和淡出。如果看到阴影失真(shadow acne),请稍微调高偏移(bias)值;如果阴影看起来与投射物分离,请调低它们。
从先前版本升级
一些更改会影响现有项目:
ShadowBias现已过时并被忽略。请使用ShadowDepthBias和ShadowNormalBias。它们以像素为单位度量,因此旧值不会直接沿用。- 如果您的自定义着色器调用了
DirectionalLightShadow、PointLightShadow或SpotLightShadow,它们现在接收的是几何法线,而非NoL。 - 如果自定义着色器读取
LightProperties.CascadeSplits,请注意它现在是一个包含级联边界的float4。第一个值是阴影开始的位置,接下来的三个值是前三个级联结束的位置。
立即试用并分享你的想法
准备好让您的场景拥有更稳定、更干净的阴影了吗?更新 Evergine,打开一个带有方向光的场景,并尝试新选项。
- 浏览 Evergine 文档 以获取完整的光照和阴影参考。
- 探索 Evergine 示例,在真实场景中查看光照和阴影效果。
- 发现问题或有想法?请在 Evergine 反馈仓库 中告诉我们。
我们很想听听您的想法。
