Lumen虚拟渲染技术特性杂谈
Lumen虚拟渲染技术特性杂谈
Lumen全局动态光照包括Lumen的特性,包含间接光照明、天空光、自发光照明、软硬阴影、反射等,本问将更加详细地介绍其技术特性。
首先需要阐明的是,Lumen是综合使用了多种技术的结合体,而非单一技术的运用。比如,Lumen默认使用有符号距离场(SDF)的软光追,但是当硬件光线追踪被启用时,可以在支持的显卡上实现更高的质量。
下面将Lumen涉及的主要技术点罗列出来。
表面缓存(Surface Cache)
Lumen会为场景表面的附近生成自动化参数,被称为表面缓存(Surface Cache),表面缓存用于快速查询场景中射线命中点的光照。Lumen会为每个网格从多角度捕捉材质属性,这些捕捉位置被称为Cards,是逐网格被离线生成的。通过控制台参数r.Lumen.Visualize.CardPlacement 1可以查看Lumen Cards的可视化效果:
上:正常渲染画面;下:Lumen Card可视化。
Nanite加速了网格捕捉,用于保持Surface Cache与三角形场景同步。特别是高面数的网格,需要使用Nanite来获得高效捕捉。
当Surface Cache被材质属性填充后,Lumen计算这些表面位置的直接和间接照明。这些更新在多个帧上摊销,为许多动态灯光和多反弹的全局照明提供有效的支持。
只有内部简单的网格可以被支持,如墙壁、地板和天花板,它们应该各自使用单独的网格,而不应该合成一个大网格。
屏幕追踪(Screen Tracing)
Lumen的特点是先对屏幕进行追踪(称为屏幕追踪或屏幕空间追踪),如果没有击中,或者光线经过表面后,就使用更可靠的方法。
使用屏幕追踪的缺点是,它极大地限制了艺术家的控制,导致只适用于间接照明,如Indirect lighting Scale、Emissive Boost等光照属性。
件光线追踪首先使用屏幕追踪,然后再使用其它开销更大的追踪选项。如果屏幕追踪被禁用于GI和反射,将会看见只有Lumen场景。屏幕跟踪支持任何几何类型,并有助于掩盖Lumen场景和三角形场景之间的不匹配现象。
使用r.Lumen.ScreenProbeGather.ScreenTraces 0|1开启或关闭屏幕追踪,以查看场景的对比效果:
上:开启了Lumen屏幕追踪的效果;下:关闭Lumen屏幕追踪的效果。可知在反射上差别最明显,其次是部分间接光。
Lumen光线追踪
Lumen支持两种光线追踪模式:
1、软件光线追踪。可以在最广泛的硬件和平台上运行。
2、硬件光线追踪。需要显卡和操作系统支持。
- 软件光线追踪
Lumen默认使用依赖有向距离场的软件光线追踪,这意味着可以运行于支持SM5的硬件上。
需要在工程设置中开启生成网格距离场(Generate Mesh Distance Fields),UE5默认已开启。
渲染器会合并网格的距离场到一个全局距离场(Global Distance Field)以加速追踪。默认情况下,Lumen追踪每一个网格距离场的前两米的准确性,其它距离的射线则使用合并的全局距离场。如果项目需要精确控制Lumen软光追,则可以在项目设置中使用的软件光线追踪模式的方法:
细节追踪(Detail Tracing)是默认的追踪方法,可以利用单独的网格距离场来达到高质量的GI(前两米才使用,其它距离用全局距离场)。全局追踪(Global Tracing)利用全局距离场来快速追踪,但会损失一定的画质效果。
网格距离场会根据摄像机在世界的移动而动态流式加载或卸载。它们会被打包成一个图集(Atlas),可以通过控制台命令r.DistanceFields.LogAtlasStats 1输出信息:
由于Lumen的软光追的质量非常依赖网格距离场,所以关注网格距离场的质量可以提升Lumen的GI效果。下图是现实网格距离场和全局距离场的菜单:
下面两图分别是网格距离场和全局距离场可视化:
但是,软件光线追踪存在着诸多限制,主要有:
- 几何物体限制:
- Lumen场景只支持静态网格、实例化静态网格、层级实例化静态网格(Hierarchical Instanced Static Meshe)。
- 不支持地貌几何体,因此它们没有间接反射光。未来将会支持。
- 材质限制:
- 不支持世界位置偏移(WPO)。
- 不支持透明物体,视Masked物体为不透明物体。
- 距离场数据的构建基于静态网格资产的材质属性,而不是覆盖的组件(override component)。意味着运行时改变材质不会影响到Lumen的GI。
- 工作流限制:
- 软件光线追踪要求层级是由模块组成。墙壁、地板和天花板应该是独立的网格。较大的网格(如山)将有不良的表现,并可能导致自遮挡伪阴影。
- 墙壁应大于10厘米,以避免漏光。
- 距离场的分辨率依赖静态网格导入时的设置,如果压缩率过高,将得不到高质量的距离场数据。
- 距离场无法表达很薄的物体。
上面已经阐述完Lumen的软件光追,下面继续介绍其硬件光追。
- 硬件光线追踪
硬件光线追踪比软件光线追踪支持更大范围的几何物体类型,特别是它支持追踪蒙皮网格。硬件光线追踪也能更好地获得更高的画面质量:它与实际的三角形相交,并有选择地来评估光线击中点的照明,而不是较低质量的Surface Cache。
然而,硬件光线追踪的场景设置成本很高,目前还无法扩展到实例数超过10万的场景。动态变形网格(如蒙皮网格)也会导致更新每一帧的光线追踪加速结构的巨大成本,该成本与蒙皮三角形的数量成正比。
对于使用Nanite的静态网格,硬件光线追踪为了渲染效率,只能在静态网格编辑器设置中Nanite的Proxy Triangle Percent生成的代理网格(Proxy Mesh)上操作。这些Proxy Mesh可以通过控制台命令r.Nanite 0|1来开关可视化:
上:全精度细节的三角形网格;下:对应的Nanite代理网格。
屏幕追踪用于掩盖Nanite渲染的全精度三角形网格和Lumen射线追踪的代理网格之间的不匹配。然而,在某些情况下,不匹配太大而无法掩盖。上面两图就是因为Proxy Triangle Percent数值太小,导致了自阴影的瑕疵。
Lumen只有在满足以下条件时才启用硬件光线追踪:
- 工程设置里开启了Use Hardware Ray Tracing when available和Support Hardware Ray Tracing。
- 工程运行于支持的操作系统、RHI和显卡。目前仅以下平台支持硬件光追:
- 带DirectX 12的Windows10。
- PlayStation 5。
- Xbox系列S / X。
- 显卡必须NVIDIA RTX-2000系列及以上,或者AMD RX 6000系列及以上。
6.5.1.4 Lumen其它说明
Lumen场景运行于摄像机附近的世界,而不是整个世界,实现了大世界和流数据。Lumen依赖于Nanite的LOD和多视图光栅化来快速捕捉场景,以维护Surface Cache,并控制所有操作以防止出现错误。Lumen不需要Nanite来操作,但是在没有启用Nanite的场景中,Lumen的场景捕捉会变得非常慢。如果资产没有良好的LOD设置,这种情况尤其严重。
Lumen的Surface Cache覆盖了距离摄像头200米的位置。在此之后的范围,只有屏幕追踪对于全局照明是开启的。
此外,Lumen还存在其它限制:
- Lumen全局光照不能和光照图(Lightmap)一起使用。未来,Lumen的反射应该被扩展到和Lightmap中使用全局照明,这将进一步提升渲染质量。
- 植物还不能被很好地支持,因为严重依赖于下采样渲染和时间滤波器。
- Lumen的最后收集(Final Gather)会在移动物体周围添加显著的噪点,目前仍在积极开发中。
- 透明材质还不支持Lumen反射。
- 透明材质没有高质量的动态GI。
以下是Lumen相关的调试或可视化信息:
上:正常画面;中:Lumen Scene可视化;下:Lumen GI可视化。
当然,除了以上出现的几个可视化选项,实际上Lumen还有很多其它可视化控制命令:
r.Lumen.RadianceCache.Visualize
r.Lumen.RadianceCache.VisualizeClipmapIndex
r.Lumen.RadianceCache.VisualizeProbeRadius
r.Lumen.RadianceCache.VisualizeRadiusScale
r.Lumen.ScreenProbeGather.VisualizeTraces
r.Lumen.ScreenProbeGather.VisualizeTracesFreeze
r.Lumen.Visualize.CardInterpolateInfluenceRadius
r.Lumen.Visualize.CardPlacement
r.Lumen.Visualize.CardPlacementDistance
r.Lumen.Visualize.CardPlacementIndex
r.Lumen.Visualize.CardPlacementOrientation
r.Lumen.Visualize.ClipmapIndex
r.Lumen.Visualize.ConeAngle
r.Lumen.Visualize.ConeStepFactor
r.Lumen.Visualize.GridPixelSize
r.Lumen.Visualize.HardwareRayTracing
r.Lumen.Visualize.HardwareRayTracing.DeferredMaterial
r.Lumen.Visualize.HardwareRayTracing.DeferredMaterial.TileDimension
r.Lumen.Visualize.HardwareRayTracing.LightingMode
r.Lumen.Visualize.HardwareRayTracing.MaxTranslucentSkipCount
r.Lumen.Visualize.MaxMeshSDFTraceDistance
r.Lumen.Visualize.MaxTraceDistance
r.Lumen.Visualize.MinTraceDistance
r.Lumen.Visualize.Stats
r.Lumen.Visualize.TraceMeshSDFs
r.Lumen.Visualize.TraceRadianceCache
r.Lumen.Visualize.VoxelFaceIndex
r.Lumen.Visualize.Voxels
r.Lumen.Visualize.VoxelStepFactor
ShowFlag.LumenGlobalIllumination
ShowFlag.LumenReflections
ShowFlag.VisualizeLumenIndirectDiffuse
ShowFlag.VisualizeLumenScene
此外,还有很多控制命令,以下显示部分命令:
r.Lumen.DiffuseIndirect.Allow
r.Lumen.DiffuseIndirect.CardInterpolateInfluenceRadius
r.Lumen.DiffuseIndirect.CardTraceEndDistanceFromCamera
r.Lumen.DirectLighting
r.Lumen.DirectLighting.BatchSize
r.Lumen.DirectLighting.CardUpdateFrequencyScale
r.Lumen.HardwareRayTracing
r.Lumen.HardwareRayTracing.PullbackBias
r.Lumen.IrradianceFieldGather
r.Lumen.IrradianceFieldGather.ClipmapDistributionBase
r.Lumen.IrradianceFieldGather.ClipmapWorldExtent
r.Lumen.MaxConeSteps
r.Lumen.MaxTraceDistance
r.Lumen.ProbeHierarchy
r.Lumen.ProbeHierarchy.AdditionalSpecularRayThreshold
r.Lumen.ProbeHierarchy.AntiTileAliasing
r.Lumen.RadianceCache.DownsampleDistanceFromCamera
r.Lumen.RadianceCache.ForceFullUpdate
r.Lumen.RadianceCache.NumFramesToKeepCachedProbes
r.Lumen.Radiosity
r.Lumen.Radiosity.CardUpdateFrequencyScale
r.Lumen.Radiosity.ComputeScatter
r.Lumen.Radiosity.ConeAngleScale
r.Lumen.Reflections.Allow
r.Lumen.Reflections.DownsampleFactor
r.Lumen.Reflections.GGXSamplingBias
r.Lumen.Reflections.HardwareRayTracing
r.Lumen.Reflections.HardwareRayTracing.DeferredMaterial
r.Lumen.Reflections.HierarchicalScreenTraces.UncertainTraceRelativeDepthThreshold
r.Lumen.Reflections.MaxRayIntensity
r.Lumen.Reflections.MaxRoughnessToTrace
r.Lumen.Reflections.RoughnessFadeLength
r.Lumen.Reflections.ScreenSpaceReconstruction
r.Lumen.Reflections.ScreenTraces
r.Lumen.Reflections.Temporal
r.Lumen.Reflections.Temporal.DistanceThreshold
r.Lumen.Reflections.Temporal.HistoryWeight
r.Lumen.Reflections.TraceMeshSDFs
r.Lumen.ScreenProbeGather
r.Lumen.ScreenProbeGather.AdaptiveProbeAllocationFraction
r.Lumen.ScreenProbeGather.AdaptiveProbeMinDownsampleFactor
r.Lumen.ScreenProbeGather.DiffuseIntegralMethod
r.Lumen.ScreenProbeGather.DownsampleFactor
r.Lumen.ScreenProbeGather.FixedJitterIndex
r.Lumen.ScreenProbeGather.FullResolutionJitterWidth
r.Lumen.ScreenProbeGather.GatherNumMips
r.Lumen.ScreenProbeGather.GatherOctahedronResolutionScale
r.Lumen.ScreenProbeGather.HardwareRayTracing
r.Lumen.ScreenProbeGather.ImportanceSample.ProbeRadianceHistory
r.Lumen.ScreenProbeGather.MaxRayIntensity
r.Lumen.ScreenProbeGather.OctahedralSolidAngleTextureSize
r.Lumen.ScreenProbeGather.RadianceCache
r.Lumen.ScreenProbeGather.RadianceCache.ClipmapDistributionBase
r.Lumen.ScreenProbeGather.ReferenceMode
r.Lumen.ScreenProbeGather.ScreenSpaceBentNormal
r.Lumen.ScreenProbeGather.ScreenTraces
r.Lumen.ScreenProbeGather.ScreenTraces.HZBTraversal
r.Lumen.ScreenProbeGather.SpatialFilterHalfKernelSize Experimental
r.Lumen.ScreenProbeGather.SpatialFilterMaxRadianceHitAngle
r.Lumen.ScreenProbeGather.Temporal
r.Lumen.ScreenProbeGather.Temporal.ClearHistoryEveryFrame
r.Lumen.ScreenProbeGather.TraceMeshSDFs
r.Lumen.ScreenProbeGather.TracingOctahedronResolution
r.Lumen.TraceMeshSDFs
r.Lumen.TraceMeshSDFs.Allow
r.Lumen.TranslucencyVolume.ConeAngleScale
r.Lumen.TranslucencyVolume.Enable
r.Lumen.TranslucencyVolume.EndDistanceFromCamera
r.LumenParallelBeginUpdate
r.LumenScene.CardAtlasAllocatorBinSize
r.LumenScene.CardAtlasSize
r.LumenScene.CardCameraDistanceTexelDensityScale
r.LumenScene.CardCaptureMargin
r.LumenScene.ClipmapResolution
r.LumenScene.ClipmapWorldExtent
r.LumenScene.ClipmapZResolutionDivisor
r.LumenScene.DiffuseReflectivityOverride
r.LumenScene.DistantScene
r.LumenScene.DistantScene.CardResolution
r.LumenScene.FastCameraMode
r.LumenScene.GlobalDFClipmapExtent
r.LumenScene.GlobalDFResolution
r.LumenScene.HeightfieldSlopeThreshold
r.LumenScene.MaxInstanceAddsPerFrame
r.LumenScene.MeshCardsCullFaces
r.LumenScene.MeshCardsMaxLOD
r.LumenScene.NaniteMultiViewCapture
r.LumenScene.NumClipmapLevels
r.LumenScene.PrimitivesPerPacket
r.LumenScene.RecaptureEveryFrame
r.LumenScene.Reset
r.LumenScene.UploadCardBufferEveryFrame
r.LumenScene.VoxelLightingAverageObjectsPerVisBufferTile
r.SSGI.AllowStandaloneLumenProbeHierarchy
r.Water.SingleLayer.LumenReflections
Lumen相关的控制台指令达到上百个,由此可知Lumen渲染的复杂度有多高!!
6.5.2 Lumen渲染基础
本节将阐述Lumen相关的基础概念和类型。
6.5.2.1 FLumenCard
FLumenCard就是上一小节提及的Card,是FLumenMeshCards的基本组成元素。
// Engine\Source\Runtime\Renderer\Private\Lumen\LumenSceneData.h
// Lumen卡片类型。
class FLumenCard
{
public:
FLumenCard();
~FLumenCard();
// 世界空间的包围盒.
FBox WorldBounds;
// 旋转信息.
FVector LocalToWorldRotationX;
FVector LocalToWorldRotationY;
FVector LocalToWorldRotationZ;
// 位置.
FVector Origin;
// 局部空间的包围盒.
FVector LocalExtent;
// 是否可见.
bool bVisible = false;
// 是否处于远景.
bool bDistantScene = false;
// 所在图集的信息.
bool bAllocated = false;
FIntPoint DesiredResolution;
FIntRect AtlasAllocation;
// 朝向
int32 Orientation = -1;
// 在可见列表的索引.
int32 IndexInVisibleCardIndexBuffer = -1;
// 所在的FLumenMeshCards的Card列表的索引.
int32 IndexInMeshCards = -1;
// 所在的FLumenMeshCards的索引.
int32 MeshCardsIndex = -1;
// 分辨率缩放.
float ResolutionScale = 1.0f;
// 初始化
void Initialize(float InResolutionScale, const FMatrix& LocalToWorld, const FLumenCardBuildData& CardBuildData, int32 InIndexInMeshCards, int32 InMeshCardsIndex);
// 设置变换数据
void SetTransform(const FMatrix& LocalToWorld, FVector CardLocalCenter, FVector CardLocalExtent, int32 InOrientation);
void SetTransform(const FMatrix& LocalToWorld, const FVector& LocalOrigin, const FVector& CardToLocalRotationX, const FVector& CardToLocalRotationY, const FVector& CardToLocalRotationZ, const FVector& InLocalExtent);
// 从图集(场景)中删除.
void RemoveFromAtlas(FLumenSceneData& LumenSceneData);
int32 GetNumTexels() const
{
return AtlasAllocation.Area();
}
inline FVector TransformWorldPositionToCardLocal(FVector WorldPosition) const
{
FVector Offset = WorldPosition - Origin;
return FVector(Offset | LocalToWorldRotationX, Offset | LocalToWorldRotationY, Offset | LocalToWorldRotationZ);
}
inline FVector TransformCardLocalPositionToWorld(FVector CardPosition) const
{
return Origin + CardPosition.X * LocalToWorldRotationX + CardPosition.Y * LocalToWorldRotationY + CardPosition.Z * LocalToWorldRotationZ;
}
};
6.5.2.2 FLumenMeshCards
FLumenMeshCards是计算Surface Cache的基本元素,也是构成Lumen Scene的基本单元。它最多可存储6个面(朝向)的FLumenCard信息,每个朝向可存储0~N个FLumenCard信息(由NumCardsPerOrientation指定)。
// Engine\Source\Runtime\Renderer\Private\Lumen\LumenMeshCards.h
class FLumenMeshCards
{
public:
// 初始化.
void Initialize(
const FMatrix& InLocalToWorld,
const FBox& InBounds,
uint32 InFirstCardIndex,
uint32 InNumCards,
uint32 InNumCardsPerOrientation[6],
uint32 InCardOffsetPerOrientation[6])
{
Bounds = InBounds;
SetTransform(InLocalToWorld);
FirstCardIndex = InFirstCardIndex;
NumCards = InNumCards;
for (uint32 OrientationIndex = 0; OrientationIndex < 6; ++OrientationIndex)
{
NumCardsPerOrientation[OrientationIndex] = InNumCardsPerOrientation[OrientationIndex];
CardOffsetPerOrientation[OrientationIndex] = InCardOffsetPerOrientation[OrientationIndex];
}
}
// 设置变换矩阵.
void SetTransform(const FMatrix& InLocalToWorld)
{
LocalToWorld = InLocalToWorld;
}
// 局部到世界的矩阵.
FMatrix LocalToWorld;
// 局部包围盒.
FBox Bounds;
// 第一个FLumenCard索引.
uint32 FirstCardIndex = 0;
// FLumenCard数量.
uint32 NumCards = 0;
// 6个朝向的FLumenCard数量.
uint32 NumCardsPerOrientation[6];
// 6个朝向的FLumenCard偏移.
uint32 CardOffsetPerOrientation[6];
};