UE4 lightmass数据流程
构建
在编辑器中,我们可以找到build的按钮,当然,我们这里讨论的并不是构建所有的东西,而仅仅讨论在构建灯光的时候。关于构建参数,请参考光照设置
编辑器构建系统
当我们点击build的时候,我们将触发下面的函数,
|
|
其中,关于build的内容定义为
目前我们仅关心的是BUILDTYPE_Lighting,看buildLighting的分支
这里会对BSP进行特殊处理,如果场景中没有BSP的话,我们就不需要进行 geometryrebuild。之后,我们会进行BuildLighting。
我们看一下传入的数据LightingBuildOptions的填充,其是取的config里面的数据
在这里,收前强制关闭所有的texture property windows,因为这些在构建灯光时是无效的。
之后有一个回调这个目前来看并没有东西,我们暂且先不去管他。
建立构建系统
首先FStaticLightingManager,其为单例模式,用来进行管理静态光照的所有系统和子系统。其只是一个管理功能,而不是一个真正的执行build的类。
其最开始的入口就是CreateStaticLightingSystem。如果目前已经有了StaticLightingSystems那么会发生警告说
如果目前没有残存的余孽的话,我们会有进行接下来的流程。这里会有一个逻辑,如果我们的配置中没有选择bOnlyBuildVisibility,也就是是由只是构建可见的,其会在结束后build ReflectionCaptures。
之后,对整个world的所有level进行判断,看是否有lighting scenario,这个概念是说同一个level可以有不同的光照图。这里是说,这个level是否是一个lightingscenario。
我们会把需要构建的level进行添加,如果我们对应的level,我们添加空的。
我们会将每一个level添加入FStaticLightingManager内部中的真正处理的类为StaticLightingSystem其进行处理的单元是一个level,这从它的构造函数可以很明显的看出来。
在FStaticLightingManager中有一个针对class FStaticLightingSystem*的指针。
之后获取列表中StaticLightingSystems的指针,然后进行真正的构建,BeginLightmassProcess,如果成功,SendProgressNotification进行显示,如果失败DestroyStaticLightingSystems通知显示。
每一个level的构建
因此,我们进入FStaticLightingSystem,来进行每一个level的构建
-
初始化。对所有的UPrimitiveComponent,里面的VisibilityId进行赋值INDEX_NONE初始化。对所有的level,如果不是当前的PersistentLevel,那就FindStreamingLevel。并且在设置中,会有设置,如是否只build CurrentLevel,bOnlyBuildSelectedLevels等会有不同的变化。那些被Skipped的Levels会被收集。这里还会有TextureStreaming的配置的搜集。在进行load一些其他的LoadGlobalIniFile里面的东西。
-
收集灯光,剔除所有的AGeneratedMeshAreaLight(在逻辑上是自发光物体)。搜集场景中所有的ULightComponentBase存储在TArray
Lights; -
收集mesh,Gather static lighting info from BSP.Gather HLOD primitives Gather static lighting info from actors.Recurse through HLOD trees, group actors and calculate child ranges。
这里面非常重要的结构
显而易见,这里一个是有排序的,一个是没有排序的。
我们将看到FStaticLightingMapping在世界的平面和static lighting cache的一个映射。
其子类为FStaticLightingTextureMapping,其记载着对应关系。
FStaticLightingTextureMapping
-
记录着光照图的大小。SizeX,SizeY
-
记录着记录coordinate index ,LightmapTextureCoordinateIndex
而每一个FStaticLightingMapping,其实都是从每一个普通的mesh身上获取的,我们都会有获取
这里对一个Mesh来说,实际上的区别是lod
与lightmass进程交互
当我们收集了几乎所有的东西,我们将建立lightmass的进程。加载SwarmInterface.dll,并进行链接,如果链接成功,进行GatherScene,FStaticLightingSystem会有一个FLightmassProcessor指针,其会有一个导出的东西,这里会进行导出
其会添加其所需要的东西ALightmassImportanceVolume,ALightmassCharacterIndirectDetailVolume,AVolumetricLightmapDensityVolume,ULightmassPortalComponent,UAtmosphericFogComponent,GetImportanceVolumes,AddLight,添加所有的Meshes。之后,初始化massProcessor。结束
二.构建结束
如果构建结束的话,我们会得到lightmass返回的光照结果,这里有着我们更加需要的信息。
首先触发的是
UEditorEngine::UpdateBuildLighting()
FStaticLightingManager::UpdateBuildLighting()
结束后仍然要通知FStaticLightingManager
2.1ActiveStaticLightingSystem->UpdateLightingBuild();
这里仍然对每一个FStaticLightingSystem进行更新。这里对于目前处于的状态有一个判断
在结束时我们的状态应该时FStaticLightingSystem::AutoApplyingImport
调用FStaticLightingManager::ProcessLightingData();进行应用数据。
这里我们仍要会到StaticLightingSystem进行FinishLightmassProcess
FinishLightmassProcess
- CompleteDeterministicMappings
- ReassignStationaryLightChannels
- EncodeTextures(bSuccessful);
- ApplyNewLightingData(bSuccessful);
- ReportStatistics();
到了我对感兴趣的地方,明显是EncodeTextures!!!!!!!!!!!!!!!!!!!!,不光还是要介绍之前的相关细节
CompleteDeterministicMappings
会将所有的mapping对应的
Textures
这里会有两个编码,一个是FLightMap2D,一个是FShadowMap2D
只看一个即可
3渲染流程
在shader里面总共用到了三种不同的数据。
3.1LightmapDataIndex
我们首先要得到关于一个顶点到底使用了那张光照图,我们根据PrimitiveId,和LocalVF_LODLightmapDataIndex最后得到。
LightmapDataIndex =GetPrimitiveData(Intermediates.PrimitiveId).LightmapDataIndex +LocalVF_LODLightmapDataIndex;
3.1.1LightmapDataIndex
这里的LightmapDataIndex是在GPUScene里面的PrimitiveSceneData里面。这里记录的是使用的哪款lightmap.
3.1.2LocalVF_LODLightmapDataIndex
这里的东西最终是在LocalVertexFactory的uniform里面。但是其是从FStaticMeshDataType传入的,是lod的index。也就是说它的不同的lod是有不同的texture,是硬件做的,还是lightmap出来的目前还没有看。
3.2View_LightmapSceneData
float4 LightMapCoordinateScaleBias =GetLightmapData(LightmapDataIndex).LightMapCoordinateScaleBias;
之后取Bias,bias在View_LightmapSceneData身上,也就是在GPUScene里面。
在view的SetupUniformBufferParameters时,这里的LightmapDataBuffer将会传入View的里面。这里的东西是GPUScene里面的东西。里面的内容有,StaticShadowMapMasks,InvUniformPenumbraSizes,LightMapCoordinateScaleBias,ShadowMapCoordinateScaleBias,LightMapScale,LightMapAdd,LightMapAdd,LightmapVTPackedPageTableUniform,LightmapVTPackedUniform。
看其GPUSCENE里面的内容是在这里Primitive加入到Fscene里面的时候进行的add
不过这里的东西只是标记,而没有实际的赋值,真正的赋值在UpdateGPUScene
对于FLightmapSceneShaderData和FPrimitiveSceneShaderData的更新方式应该是一致的。
- PrimitiveSceneProxy->GetLCIs(LCIs);这里是直接提取Primitive的FLODInfo
- FLightmapSceneShaderData LightmapSceneData(LCIs[i],Scene.GetFeatureLevel());对于FLightmapSceneShaderData的构建
3. LightmapDataUploadBuilder.Add(LightmapDataOffset + i,&LightmapSceneData.Data[0]);
其这里的主要内容也是在LODs里面,我们更加深入来看LODs是在如何构建的
在FLODInfo的构造函数中FStaticMeshSceneProxy::FLODInfo
由于FLODInfo是根据
const FStaticMeshComponentLODInfo& ComponentLODInfo =InComponent->LODData[LODIndex];
const FMeshMapBuildData* FoundData = MeshBuildData.Find(MeshId);
是从FMeshMapBuildData中取的东西,根据MeshId
主要是 LightMap->GetInteraction(InFeatureLevel)里面的内容
在 FLightMapPendingTexture::PostEncode()时进行的
void UMapBuildDataRegistry::InvalidateStaticLighting
3.3FLightmapResourceCluster
在FLightmapResourceCluster中,有一个专门的uniformbuffer,
这个FLightmapResourceCluster存在FLightCacheInterface中。对于StaticMesh来说,其主要的地方就是FLODInfo中