lumen在渲染的时候会走几个步骤:
第一是BeginUpdateLumenSceneTasks对场景的距离场体素重建
第二是UpdateLumenScene更新lumen的场景以及包括用nanite提出并且上传lumen的card
第三是RenderLumenSceneLighting获取lumen光照的直接光与间接光
第四是RenderLumenSceneVisualization可视化探针的获取与设置,体素步进与辐射度设置。
第五是RenderDiffuseIndirectAndAmbientOcclusion中对lumen的探针执行计算获取探针的光照来叠加。
RenderLumenSceneLighting主要是lumen计算直接光与间接光的地方,主要通过体素以及有号距离场来计算光照效果。
1.初始化光照的uav,srv,buffer
2.meshcard层面的剔除
3.间接光照参数
4.合并lumen场景光照需要的
5.lumen直接光照计算
6.合入漫反射信息,为后续间接光使用
7.过滤颜色
8.计算体素和有号距离场的间接光照可见对象
9.计算lumen的半透明光照效果
LumenSceneLighting.cpp中。
RenderLumenSceneLighting
只有FDeferredShadingSceneRenderer::Render才有RenderLumenSceneLighting。
如果lumen可视化的对象有开启,以下开启间接参数写入card中来更新这一帧的直接照明 。
有下面几步,
init主要是初始化QuadAllocator,QuadDataBuffer,QuadAllocatorUAV,QuadDataUAV
这一步主要是创建uav,srv或buffer给到后面使用的。
这里主要注册了三个buffer,分别是VisibleCardsIndexBuffer,CardsToRenderIndexBuffer,CardsToRenderHashMapBuffer。然后通过GraphBuilder.AddPass给到computeshader“/Engine/Private/Lumen/LumenSceneLighting.usf”的CullCardsToShapeCS.
里面把在视锥内的对象。
在CullCardsToShapeCS中主要是根据索引中的lumen的数据是否在范围内以及是否灯光刷新频率内。如果是则可以进行更新。执行InterlockedAdd并标记mesh的cardid到RWQuadData[QuadDataInsertIndex]。
这里是通过FComputeShaderUtils::AddPass给到computeshader“InitializeCardScatterIndirectArgsCS”主要是初始化lumen间接光照的一些参数。
这些参数有两种模式,一种是RECT_LIST_TOPOLOGY,他就会记录RWCardIndirectArgs里面的数据为VertexCountPerInstance, InstanceCount, StartVertexLocation, StartInstanceLocation。如果另一种模式则RWCardIndirectArgs记录为IndexCount, NumInstances, StartIndex, BaseVertexIndex, FirstInstance。
这一步是通过GraphBuilder.AddPass通过光栅化的方式DrawQuadsToAtlas。他会执行到FLumenCardLightingEmissive这里,他会有顶点和片元着色。顶点用FRasterizeToCardsVS实现,片元用LumenCardLightingInitializePS实现的。主要是合并场景光照作用。
RasterizeToCardsVS这里主要做的就是转坐标,从lumen的card的数据转到屏幕坐标,最终输出两个,一个是屏幕坐标OutPosition,一个是mesh的card的vs到ps的数据CardInterpolants
LumenCardLightingInitializePS这里就是简单的根据顶点着色的AtlasCoord采样RadiosityAtlas。
会执行到FDeferredShadingSceneRenderer::RenderDirectLightingForLumenScene,主要是计算直线光的颜色。
他会分平行光和其他光源的区别。如果是平行光则执行RenderDirectLightIntoLumenCards。然后看是否支持光线追踪,如果支持他会用光追FLumenDirectLightingHardwareRayTracingData。然后拿所有的可见光判断如果是直线光则RenderDirectLightIntoLumenCards,如果不是则先加入到GatheredLocalLights。
在RenderDirectLightIntoLumenCards中,会拿到虚拟纹理id(VirtualShadowMapId),
然后判断是否支持光追阴影bLumenUseHardwareRayTracedShadow,支持则执行RenderHardwareRayTracedShadowIntoLumenCards,具体到LumenSceneDirectShadowingHardwareRaytracing.cpp这里执行光追。
如果不支持光追则用网格的距离场来获取光照效果CullMeshSDFsForLightCards。
如果不是平行光则所有光源及id先执行CardScatterContext.CullCardsToShape,也就最终执行到FCullCardsToShapeCS这里来剔除不可渲染的对象。然后执行RenderDirectLightIntoLumenCards,也就是执行到类似平行光的光照里面去做光照和阴影然后渲染直接光照。
在CullDistanceFieldObjectsForLight里面,实际上这里面只对有号距离场的阴影处理。首先通过FCullObjectsForShadowCS,也就是执行CullObjectsForShadowCS,对阴影剔除。
然后是ScatterObjectsToShadowTiles执行FShadowMeshSDFObjectCull,通过tile来把对应的shadow不可达的对象剔除。
然后执行FLumenCardDirectLighting来计算card的直接光照结果。执行LumenCardDirectLightingPS这个shader来执行片元着色,在里面计算光照衰减,以及其他各种光源,比如面光,虚拟纹理阴影,体积阴影,最终以辐照度的形式返回颜色。
这里会执行FLumenCardBlendAlbedoPS通过computeshader“LumenSceneLighting.usf”中的LumenCardBlendAlbedoPS合入漫反射效果。为后续间接光使用。
这个执行到FDeferredShadingSceneRenderer::PrefilterLumenSceneLighting中。
通过computeshader执行到"/Engine/Private/Lumen/LumenSceneLighting.usf"的LumenCardPrefilterLightingPS,通过mrt执行三个颜色的赋值,OutLighting,OutColor1,OutColor2。分别是父级光照信息,直接辐照度颜色,间接辐照度颜色。
这里是关于体素光照的计算,执行到FDeferredShadingSceneRenderer::ComputeLumenSceneVoxelLighting。他创建两个texture,一个是体素的光照颜色,一个是体素的可见性图。
然后执行UpdateVoxelVisBuffer,他有个ClipmapsToUpdate,也就是标记了要更新的体素区域,然后先执行FUploadVoxelLightingUpdateBoundsParameters更新区域
然后是FClearIndirectAgrBuffersCS清理间接光的参数,FBuildUpdateGridTilesCS获取需要更新的当前clipmap中的块,这里在BuildUpdateGridTilesCS中通过UpdateBoundsIndex的几个索引然后在距离场中找是否距离小于等于0,如果是则说明在地下,记录该点。
FClearVisBuffer清理块中的可见对象,FCullToVoxelClipmapCS对clipmap区域内的对象剔除,在CullToVoxelClipmapCS也是通过距离场来查看对象边缘是否距离场可达
FSetupVoxelTracesCS用距离判断体素步进的对象,这里通过SetupVoxelTracesCS来通过ObjectIndex来看整个检查框DFObjectBounds是否在距离场内
FVoxelTraceCS开始步进,会利用距离场来步进,执行到LumenVoxelLighting.usf的VoxelTraceCS,其中会执行到RayTraceSingleMeshSDF对网格执行距离场步进,保存到RWVoxelVisBuffer里面是可见对象列表。这里就是关键的体素光照的步进。这里的VisBufferTextureCoord就是可见的。
执行完UpdateVoxelVisBuffer后就是VoxelizeVisBuffer,然后用FClearVoxelLightingClipmapCS来清理原本的体素光照的clipmap,然后执行FCompactVisBufferCS获取可见性对象,然后执行FSetupVisBufferShadingCS设置可见对象的参数设置,FVisBufferShadingCS最终执行可见对象的着色。
这里的颜色计算SampleLumenMeshCardsWithWeight中会拿到xyz三个轴的权重并且对三个轴进行进行采样SampleLumenCard。另外如果有距离场光照还会执行一次距离场的SampleLumenCard,最终整合颜色到Lighting中
SampleLumenCard中对颜色采样会通过三张贴图之一确定,一个是FinalLightingAtlas,一个是IrradianceAtlas,一个是IndirectIrradianceAtlas,最终跟不透明贴图OpacityAtlas根据权重混合。
这里计算半透明渲染的全局光照,要有透明度还会用前一帧和当前帧做混合来减少锯齿。
执行FTranslucencyLightingCS来计算半透明光照,他是用圆锥个数的步进来进行光照的,执行ConeTraceVoxels来步进,这里得出的结果是透明度和步进次数以及历史距离,如果有开启ENABLE_DYNAMIC_SKY_LIGHT也就是动态天光还会加上动态天光到颜色中。并且会加上球谐光照AddSH(Lighting, MulSH(SHBasisFunction(WorldConeDirection), TraceResult.Lighting * SampleWeight));
然后用历史光照和当前光照混合到结果RWTranslucencyGI1中,并且保存颜色到RWTranslucencyGINewHistory1方便下次历史颜色混合。
最终把lumen计算出来的所有光照信息加入到view中。