构建

在编辑器中,我们可以找到build的按钮,当然,我们这里讨论的并不是构建所有的东西,而仅仅讨论在构建灯光的时候。关于构建参数,请参考光照设置 @think.ouyang欧阳江

编辑器构建系统

当我们点击build的时候,我们将触发下面的函数,

FEditorBuildUtils::EditorBuild();

//其会调用StopRenderingThread();

if (GUseThreadedRendering)
{
    StartRenderingThread();
}  

其中,关于build的内容定义为

目前我们仅关心的是BUILDTYPE_Lighting,看buildLighting的分支

这里会对BSP进行特殊处理,如果场景中没有BSP的话,我们就不需要进行 geometry rebuild。

之后,我们会进行BuildLighting。

我们看一下传入的数据LightingBuildOptions的填充,其是取的config里面的数据

在这里,收前强制关闭所有的texture property windows,因为这些在构建灯光时是无效的。

之后有一个回调这个目前来看并没有东西,我们暂且先不去管他。

建立构建系统

首先FStaticLightingManager,其为单例模式,用来进行管理静态光照的所有系统和子系统。其只是一个管理功能,而不是一个真正的执行build的类。

其最开始的入口就是CreateStaticLightingSystem。如果目前已经有了StaticLightingSystems那么会发生警告说

如果目前没有残存的余孽的话,我们会有进行接下来的流程。这里会有一个逻辑,如果我们的配置中没有选择bOnlyBuildVisibility,也就是是由只是构建可见的,其会在结束后build ReflectionCaptures。

之后,对整个world的所有level进行判断,看是否有lighting scenario,这个概念是说同一个level可以有不同的光照图。这里是说,这个level是否是一个lighting scenario。

UE4光照系统.pptx 中有介绍过。

我们会把需要构建的level进行添加,如果我们对应的level,我们添加空的。

我们会将每一个level添加入FStaticLightingManager内部中的真正处理的类为StaticLightingSystem其进行处理的单元是一个level,这从它的构造函数可以很明显的看出来。

在FStaticLightingManager中有一个针对class FStaticLightingSystem*的指针。

之后获取列表中StaticLightingSystems的指针,然后进行真正的构建,BeginLightmassProcess,如果成功,SendProgressNotification进行显示,如果失败DestroyStaticLightingSystems通知显示。

每一个level的构建

因此,我们进入FStaticLightingSystem,来进行每一个level的构建

  1. 初始化。对所有的<UPrimitiveComponent>,里面的VisibilityId进行赋值INDEX_NONE初始化。对所有的level,如果不是当前的PersistentLevel,那就FindStreamingLevel。并且在设置中,会有设置,如是否只build CurrentLevel,bOnlyBuildSelectedLevels等会有不同的变化。那些被Skipped的Levels会被收集。这里还会有TextureStreaming的配置的搜集。在进行load一些其他的LoadGlobalIniFile里面的东西。

  2. 收集灯光,剔除所有的AGeneratedMeshAreaLight(在逻辑上是自发光物体)。搜集场景中所有的ULightComponentBase存储在TArray<ULightComponentBase*> Lights;

  3. 收集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

  1. 记录着光照图的大小。SizeX,SizeY

  2. 记录着记录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

ActiveStaticLightingSystem->UpdateLightingBuild();

这里仍然对每一个FStaticLightingSystem进行更新。这里对于目前处于的状态有一个判断

在结束时我们的状态应该时FStaticLightingSystem::AutoApplyingImport

调用FStaticLightingManager::ProcessLightingData();进行应用数据。

这里我们仍要会到StaticLightingSystem进行FinishLightmassProcess

FinishLightmassProcess

  1. CompleteDeterministicMappings

  2. ReassignStationaryLightChannels

  3. EncodeTextures(bSuccessful);

  4. ApplyNewLightingData(bSuccessful);

  5. ReportStatistics();

到了我对感兴趣的地方,明显是EncodeTextures!!!!!!!!!!!!!!!!!!!!,不光还是要介绍之前的相关细节

CompleteDeterministicMappings

会将所有的mapping对应的

Textures

这里会有两个编码,一个是FLightMap2D,一个是FShadowMap2D

只看一个即可

渲染流程

再shader里面总共用到了三方数据

  1. LightmapDataIndex

我们首先要得到关于一个顶点到底使用了那张光照图,我们根据PrimitiveId,和LocalVF_LODLightmapDataIndex最后得到。

LightmapDataIndex = GetPrimitiveData(Intermediates.PrimitiveId).LightmapDataIndex + LocalVF_LODLightmapDataIndex;

  1. LightmapDataIndex

这里的LightmapDataIndex是在GPUScene里面的PrimitiveSceneData里面。这里记录的是使用的哪款lightmap.

b. LocalVF_LODLightmapDataIndex

这里的东西最终是在LocalVertexFactory的uniform里面。但是其是从FStaticMeshDataType传入的,是lod的index。也就是说它的不同的lod是有不同的texture,是硬件做的,还是lightmap出来的目前还没有看。

  1. View_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的更新方式应该是一致的。

  1. PrimitiveSceneProxy->GetLCIs(LCIs);这里是直接提取Primitive的FLODInfo

  2. FLightmapSceneShaderData LightmapSceneData(LCIs[i], Scene.GetFeatureLevel());

    对于FLightmapSceneShaderData的构建

  1. 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

  1. FLightmapResourceCluster

在FLightmapResourceCluster中,有一个专门的uniformbuffer,

这个FLightmapResourceCluster存在FLightCacheInterface中

对于StaticMesh来说,其主要的地方就是FLODInfo中

  1. PrecomputedLightingBuffer