虚幻引擎静态光照烘焙和渲染的流程原理机制源码剖析

Viewed 40

首发:虚幻引擎静态光照烘焙和渲染的流程原理机制源码剖析
说明

  • 本文源自对项目中遇到的“静态方向光下静态物体的阴影表现异常”的实际问题的调查和处理。
  • 本文主要总结、记录和分享静态方向光(directionalLight )下(标注为)静态(static)物体的阴影的渲染和烘焙的相关源码、原理和流程。
  • 本文尝试解答如下问题,并对其它相关问题提供参考。
  • 静态阴影的烘焙流程及“根据磁盘上的 Mesh 网格资源最终得到渲染显示在屏幕上的阴影效果”的全流程。
  • 光照贴图(Lightmap,以下统一用英文)跟静态阴影的关联,它是怎么生成的,如何被使用的,其中有什么数据,各像素各通道表示什么,如何跟 Mesh 网格等资源关联起来的等。
  • 附带记录相关调查分析所需的对 Lightmass 和 Shader 的调试方法。
  • 本文展示的源码和堆栈均基于虚幻引擎 UE 4.27.2 ,大部分原理机制和流程逻辑原则上对 UE5 也有参考意义。

背景问题

在实际项目开发中,遇到了如下具体问题:在我们自定义的地形中,放置一个静态方向光及一个静态Cube。在编辑器下,烘焙光照之后(Build Light Only),阴影显示异常,跟非静态光(如 Stationary )下的表现(主要是位置)偏差较大。

扩展阅读

本文也会涉及到如下知识,相关的关键部分会在本文直接阐述,无需读过这些内容或对相关部分有很深了解。但如对相关知识有兴趣想更深入研究,欢迎参考阅读。

概念简述

  • 静态阴影

    • 静态光源(Static Lights) 是在运行时完全无法更改或移动的光源。这些光源仅在Lightmap中计算,一旦处理完,对性能没有进一步影响。
    • 在三种光源可移动性中,静态光源的质量中等、可变性最低、性能成本最低。
    • 由于静态光源仅使用Lightmap,它们需要在游戏前进行阴影烘焙。这意味着,它们不能让移动(动态)对象产生阴影。如果要光照的对象也是静态的,就能够产生面积(接触)阴影。
    • 静态光源的主要应用对象是移动平台上的低性能设备。
    • 参见官方文档:静态光源
  • Lightmap

    • Lightmap ,译为光照贴图,顾名思义它是一种贴图。静态光源下的静态物体主要基于 Lightmap 来渲染显示阴影效果,也仅当使用烘焙(或预计算)光照来光照静态网格体时,才需要Lightmap。后文会详细介绍 Lightmap 的内容、生成流程及与阴影之间的关联等。
    • 更多介绍参见:理解虚幻引擎中的光照贴图
  • Lightmass

    • Lightmass,官方翻译 全局光照。Lightmass 是 Unreal 提供的静态全局照明解决方案 ,主要用于创建具有复杂光交互作用的光照图,例如区域阴影和漫反射。它用于预计算具有固定和静止运动性的光源的照明贡献部分。
    • 更多介绍请参阅:CPU Lightmass全局光照
    • 后文会提到,默认情况下 Lightmass 是一个不依赖引擎的程序,用于创建 Lightmap 等场景烘焙信息,最后通过 Swarm 把结果传输回编辑器。
  • Swarm

    • 根据你的开发环境,渲染大型和开放世界场景可能会花费大量的时间,因为计算光照、阴影和几何体可能非常昂贵。有几种方法可以减少项目的构建时间,比如升级你的硬件以超出我们的推荐规格,或者使用任务分配系统,这就是 Unreal Swarm 的用途,减少执行昂贵计算所需的时间,比如高质量静态全局照明求解。
    • Unreal Swarm 是一种通用的应用程序和任务分配系统,由两种应用程序类型组成,一种是分配构建任务的协调程序,另一种是利用主机系统资产完成分配任务的代理程序。
    • 编辑器和Lightmass之间的通信由 Swarm Agent 处理,它管理本地的照明构建,也可以将照明构建分发到远程机器。
    • 更多介绍和设置请参阅:Unreal Swarm
    • 主要用于在编辑器和 Lightmass 之间传输烘焙数据(如包含 Lightmap 的 FMeshMapBuildData 等),后文提到光照烘焙过程中 Swarm 的作用和位置。

调试准备

工欲善其事,必先利其器。默认情况下,光照烘焙的数据和过程表现不直观,不方便查看数据,无法debug,操作耗时。为提高效率,有必要把相关调试工作准备好,以便高效开发、调试和验证。

相关代码优化的关闭

  • 为了尽可能提高性能,有的代码或模块默认会开启代码优化,表现之一是无法断点或断点无法被命中。此时可尝试如下方法关闭代码优化。
  • 把相关模块的代码优化方式( OptimizeCode )设置为不优化( CodeOptimization.Never ),此为以下2种方法的前置设置。
  • 如对于 Lightmass 模块来说,需要修改 UnrealLightmass.Build.cs ,在其中添加 OptimizeCode = CodeOptimization.Never;

  • 有的函数被显示标记为 FORCEINLINE 了,如需对其 debug ,需要将相关宏注释掉。

  • 如果还是没法命中断点,请在文件头尾添加相关宏:#pragma optimize("", off) 、 #pragma optimize("", on) 。


Lightmap 的查看

如果需要查看光照烘焙之后的 Lightmap ,有如下方法。

  • 在引擎中直接查看
    • World Settings -> Lightmass -> Lightmaps



  • 在 RenderDoc 中截帧查看

Shader 的 Debug

如果需要在渲染调试工具(如 RenderDoc )中调试 Shader 或看 Shader 源码,需要开启引擎中 Shader 相关调试开关。

  • 打开 Engine\Config\ConsoleVariables.ini ,把如下3行配置的注释取消即可。
r.ShaderDevelopmentMode=1
r.Shaders.Optimize=0
r.Shaders.KeepDebugInfo=1

Lightmass 光照烘焙过程的 Debug

默认情况下,UnrealLightmass 模块的代码不可调试,如需对光照烘焙的过程如 UnrealLightmass 中的代码进行断点调试,需要做一些设置。

  • 在运行项目工程的UE编辑器的控制台输入 LIGHTMASSDEBUG (不区分大小写)。

  • 点击 Build -> Build Lighting Only ,开始烘焙光照。

  • 等待右下角弹出 Building Lighting : 0% 。

  • 开个新的 UE4 解决方案(如用 Visual Studio 新打开 UE4.sln )。
  • 把 UnrealLightmass 设置为 Startup Projects , Solution Configurations 选 Development Editor 或 Debug Editor 等允许调试的配置。

  • 在需要断点的地方设置断点,按下 F5,即可开始调试,同时编辑器界面会继续烘焙光照。

  • 注意 LIGHTMASSDEBUG 不带参数,加参数也没用。每次输入 LIGHTMASSDEBUG ,都会改变其值,请留意控制台的日志,确认当前的值。

Texel 的调试

可以对烘焙之后的静态网格体上的Texel进行 debug ,效果之一如下(Cube 为默认的白色 Cube ,上边的贴花一样的东西是点击后显示的调试信息)。这个功能还有其它作用,也可扩展自己的调试信息。

  • 在 Engine\Source\Programs\UnrealLightmass\Private\Lighting\Lighting.h 中把 ALLOW_LIGHTMAP_SAMPLE_DEBUGGING 的默认值改为 1 。

  • 在 Engine\Config\BaseLightmass.ini 中设置 bCompressLightmaps=False

  • 在控制台中输入 r.TexelDebugging 1

  • 编译 UnrealLightmass 。
  • 编辑器下,按住 T,在已构建的静态网格体上选一个位置点击。

Lightmass 日志的查看

默认情况下,UnrealLightmass 模块中添加的日志不输出。如需输入,需要修改设置。

  • 在 UnrealLightmass.Target.cs 中把 GlobalDefinitions.Add("ALLOW_LOG_FILE=0"); 改成 GlobalDefinitions.Add("ALLOW_LOG_FILE=1"); ,之后编译 UnrealLightmass Project 即可。

  • 日志文件存储路径:Engine\Saved\Swarm\SwarmCache\Logs


原理流程

从静态阴影所需资源 Lightmap 中的数据的来源倒推,可推出静态阴影的渲染和光照烘焙的全流程。本文为阅读理解方便,流程从来源开始,正序叙述。先说概要,再说详细流程。

流程概要

  • 点击光照烘焙,开启烘焙静态光照
  • 需注意前置条件,即场景有静态光照射下的静态物体。
  • 从静态网格等资源上获取基础数据,组建光照数据
  • 烘焙时根据 Mesh 的顶点等数据,结合光照,计算并更新 FTextureMappingStaticLightingData
  • 基于光照数据计算和生成Lightmap,存到文件
  • 根据 FTextureMappingStaticLightingData 创建和更新 FMeshMapBuildData (及其中的Lightmap),序列化到本地。
  • 从文件中拿到并序列化出Lightmap并组装、传输给 Shader
  • 从磁盘里的 FMeshMapBuildData 中拿到 Lightmap ,传递给 Shader 。
  • Shader 解析Lightmap,计算、渲染显示阴影
  • Shader 拿到 Lightmap 后解析、计算,迭代到颜色,渲染显示到屏幕。

详细流程

烘焙时根据 Mesh 的顶点等数据,结合光照,计算并更新 FTextureMappingStaticLightingData

  • 通过在编辑器下点击“Build Lighting Only”等操作,触发光照烘焙。调用 FStaticLightingManager::CreateStaticLightingSystem 创建 FStaticLightingSystem ,并执行 FStaticLightingSystem 的 BeginLightmassProcess 来开启 Lightmass 进程。

[UE4Editor-UnrealEd.dll] FStaticLightingSystem::BeginLightmassProcess() StaticLightingSystem.cpp:697
[UE4Editor-UnrealEd.dll] FStaticLightingManager::CreateStaticLightingSystem(const FLightingBuildOptions &) StaticLightingSystem.cpp:310
[UE4Editor-UnrealEd.dll] UEditorEngine::BuildLighting(const FLightingBuildOptions &) StaticLightingSystem.cpp:2514
//编辑器下点击 “Build Lighting Only”触发,更多堆栈略。
  • 在 BeginLightmassProcess 中,会通过 GatherStaticLightingInfo 来收集要处理的所有静态照明信。其会触发 FLandscapeStaticLightingMesh 的构造函数,在其中初始化 FTransform LocalToWorld 等数据,如根据 ULandscapeComponent 的 ComponentToWorld 初始化 LocalToWorld 。

[UE4Editor-Landscape.dll] FLandscapeStaticLightingMesh::FLandscapeStaticLightingMesh(ULandscapeComponent *,const TArray<ULightComponent *,TSizedDefaultAllocator<32> > &,int,int,float,int) LandscapeLight.cpp:159
[UE4Editor-Landscape.dll] ULandscapeComponent::GetStaticLightingInfo(FStaticLightingPrimitiveInfo &,const TArray<ULightComponent *,TSizedDefaultAllocator<32> > &,const FLightingBuildOptions &) LandscapeLight.cpp:695
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::GatherStaticLightingInfo(bool,bool) StaticLightingSystem.cpp:1163
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::BeginLightmassProcess() StaticLightingSystem.cpp:697
//更多堆栈略。
  • BeginLightmassProcess 接下来还会通过 InitiateLightmassProcessor 运行 Lightmass 处理器的初始导出代码,其会把必要的数据存到 Lightmass::FLandscapeStaticLightingMeshData LandscapeInstanceMeshData 等 struct 中,如从上文提到的 FLandscapeStaticLightingMesh* LandscapeLightingMesh 中拿到的 FTransform LocalToWorld 的数据的 ToMatrixWithScale 版本等,也可以在这里添加自己想要的数据。最后通过 Swarm.WriteChannel 写到 Swarm 中传递给 Lightmass 进程。

[UE4Editor-UnrealEd.dll] FLightmassExporter::WriteLandscapeInstances(int) Lightmass.cpp:1911
[UE4Editor-UnrealEd.dll] FLightmassExporter::WriteToChannel(FLightmassStatistics &,FGuid &) Lightmass.cpp:708
[UE4Editor-UnrealEd.dll] FLightmassProcessor::InitiateExport() Lightmass.cpp:2897
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::InitiateLightmassProcessor() StaticLightingSystem.cpp:2196
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::BeginLightmassProcess() StaticLightingSystem.cpp:768
[UE4Editor-UnrealEd.dll] FStaticLightingManager::CreateStaticLightingSystem(const FLightingBuildOptions &) StaticLightingSystem.cpp:310
[UE4Editor-UnrealEd.dll] UEditorEngine::BuildLighting(const FLightingBuildOptions &) StaticLightingSystem.cpp:2514
//更多略
  • 在 Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsThreadLoop 的时候触发 FStaticLightingSystem::CacheIrradiancePhotonsTextureMapping ,在单个纹理映射上缓存辐照度光子。它会调用 RasterizeToSurfaceCacheTextureMapping 栅格化到表面缓存纹理映射

UnrealLightmass.exe!Lightmass::FStaticLightingSystem::RasterizeToSurfaceCacheTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, bool bDebugThisMapping, Lightmass::FTexelToVertexMap & TexelToVertexMap) Line 283	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 150	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsThreadLoop(int ThreadIndex, bool bIsMainThread) Line 1655	C++
//更多略...
  • RasterizeToSurfaceCacheTextureMapping 通过 FLandscapeStaticLightingMesh::GetTriangle 调用 FLandscapeStaticLightingMesh::GetStaticLightingVertex ,在其中利用 Swarm 发过来的数据(如上文的 LocalToWorld 及其它数据)对 FStaticLightingVertex& OutVertex 进行初始化和更新。再把拿到的顶点数据赋值给 FStaticLightingInterpolant 。
  • FStaticMeshStaticLightingMesh

UnrealLightmass.exe!Lightmass::GetStaticLightingVertex(const Lightmass::FStaticMeshVertex & InVertex, const FMatrix & LocalToWorld, const FMatrix & LocalToWorldInverseTranspose, bool bIsSplineMesh, const Lightmass::FSplineMeshParams & SplineParams, Lightmass::FStaticLightingVertex & OutVertex) Line 187	C++
UnrealLightmass.exe!Lightmass::FStaticMeshStaticLightingMesh::GetTriangle(int TriangleIndex, Lightmass::FStaticLightingVertex & OutV0, Lightmass::FStaticLightingVertex & OutV1, Lightmass::FStaticLightingVertex & OutV2, int & ElementIndex) Line 218	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::RasterizeToSurfaceCacheTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, bool bDebugThisMapping, Lightmass::FTexelToVertexMap & TexelToVertexMap) Line 283	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 150	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsThreadLoop(int ThreadIndex, bool bIsMainThread) Line 1655	C++
//更多略...
  • FLandscapeStaticLightingMesh

UnrealLightmass.exe!Lightmass::FLandscapeStaticLightingMesh::GetStaticLightingVertex(int VertexIndex, Lightmass::FStaticLightingVertex & OutVertex) Line 88	C++
UnrealLightmass.exe!Lightmass::FLandscapeStaticLightingMesh::GetTriangle(int TriangleIndex, Lightmass::FStaticLightingVertex & OutV0, Lightmass::FStaticLightingVertex & OutV1, Lightmass::FStaticLightingVertex & OutV2, int & ElementIndex) Line 111	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CalculateTexelCorners(const Lightmass::FStaticLightingMesh * Mesh, Lightmass::FTexelToCornersMap & TexelToCornersMap, int UVIndex, bool bDebugThisMapping) Line 3728	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::RasterizeToSurfaceCacheTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, bool bDebugThisMapping, Lightmass::FTexelToVertexMap & TexelToVertexMap) Line 275	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 150	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsThreadLoop(int ThreadIndex, bool bIsMainThread) Line 1655	C++
//更多略...


  • 不同类型的 Mesh 网格会使用不同但都继承于 FStaticLightingMesh 的类型来计算静态光照

  • RasterizeToSurfaceCacheTextureMapping 随后光栅化多个子纹理像素样本并把结果做线性组合。使用贴图的纹理坐标通道对三角形进行栅格化,调用 FTriangleRasterizer TexelMappingRasterizer 的 DrawTriangle 绘制顶点和三角面。

  • 在 FTriangleRasterizer DrawTriangle 的时候,在 FTriangleRasterizer 中,会对顶点做一些调整,比如找出 top 和 bottom 顶点并排序、计算边缘梯度等。


  • 之后调用 FStaticLightingRasterPolicy::ProcessPixel ,设置 FTexelToVertex& TexelToVertex 。比如利用刚才 Interpolant.Vertex 中的 WorldPosition 等来计算 TexelToVertex.WorldPosition ,还有切线、法线等。

UnrealLightmass.exe!Lightmass::FTexelToVertex::SetWorldPosition(FVector4 InWorldPosition) Line 21	C++
UnrealLightmass.exe!Lightmass::FStaticLightingRasterPolicy::ProcessPixel(int X, int Y, const Lightmass::FStaticLightingInterpolant & Interpolant, bool BackFacing) Line 37	C++
UnrealLightmass.exe!Lightmass::FTriangleRasterizer<Lightmass::FStaticLightingRasterPolicy>::DrawTriangleTrapezoid(const Lightmass::FStaticLightingInterpolant & TopMinInterpolant, const Lightmass::FStaticLightingInterpolant & DeltaMinInterpolant, const Lightmass::FStaticLightingInterpolant & TopMaxInterpolant, const Lightmass::FStaticLightingInterpolant & DeltaMaxInterpolant, float TopMinX, float DeltaMinX, float TopMaxX, float DeltaMaxX, float MinY, float MaxY, bool BackFacing) Line 136	C++
UnrealLightmass.exe!Lightmass::FTriangleRasterizer<Lightmass::FStaticLightingRasterPolicy>::DrawTriangle(const Lightmass::FStaticLightingInterpolant & I0, const Lightmass::FStaticLightingInterpolant & I1, const Lightmass::FStaticLightingInterpolant & I2, const FVector2D & P0, const FVector2D & P1, const FVector2D & P2, bool BackFacing) Line 91	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::RasterizeToSurfaceCacheTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, bool bDebugThisMapping, Lightmass::FTexelToVertexMap & TexelToVertexMap) Line 320	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 150	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CacheIrradiancePhotonsThreadLoop(int ThreadIndex, bool bIsMainThread) Line 1653	C++
//更多略...
  • 在 FStaticLightingSystem::ProcessTextureMapping 的时候,FStaticLightingSystem::CalculateDirectAreaLightingTextureMapping 时,通过 FTexelToVertexMap& TexelToVertexMap 获取 FTexelToVertex& TexelToVertex。通过 TexelToVertex.GetVertex() ,从 FTexelToVertex 中拿到 WorldPosition 等数据创建和初始化 FStaticLightingVertex,赋值 FStaticLightingVertex CurrentVertex ,


UnrealLightmass.exe!Lightmass::FTexelToVertex::GetVertex() Line 56	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CalculateDirectAreaLightingTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, Lightmass::FStaticLightingMappingContext & MappingContext, Lightmass::FGatheredLightMapData2D & LightMapData, Lightmass::FShadowMapData2D * & ShadowMapData, const Lightmass::FTexelToVertexMap & TexelToVertexMap, bool bDebugThisMapping, const Lightmass::FLight * Light, const bool bLowQualityLightMapsOnly) Line 1729	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 873	C++
//更多略...
  • 根据 CurrentVertex 的 WorldPosition 等计算 FLinearColor LightIntensity,再通过 CalculatePointLighting 得到 FGatheredLightSample DirectLighting,再通过 FGatheredLightMapSample 的 AddWeighted 来设置 FGatheredLightMapSample& CurrentLightSample 中的 SHVector 。


[Inline Frame] UnrealLightmass.exe!FLinearColor::operator+=(const FLinearColor &) Line 115	C++
[Inline Frame] UnrealLightmass.exe!Lightmass::TGatheredLightSample<2>::AddWeighted(const Lightmass::TGatheredLightSample<2> &) Line 50	C++
[Inline Frame] UnrealLightmass.exe!Lightmass::FGatheredLightMapSample::AddWeighted(const Lightmass::TGatheredLightSample<2> &) Line 147	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::CalculateDirectAreaLightingTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping, Lightmass::FStaticLightingMappingContext & MappingContext, Lightmass::FGatheredLightMapData2D & LightMapData, Lightmass::FShadowMapData2D * & ShadowMapData, const Lightmass::FTexelToVertexMap & TexelToVertexMap, bool bDebugThisMapping, const Lightmass::FLight * Light, const bool bLowQualityLightMapsOnly) Line 1744	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 877	C++
//更多略...
  • ProcessTextureMapping 随后调用 FGatheredLightMapData2D::ConvertToLightmap2D , 其利用 FGatheredLightSample 的 SHVector 、IncidentLighting 等数据对 FLightSample NewSample 的 Coefficients 等进行初始化和更新。



UnrealLightmass.exe!Lightmass::FGatheredLightMapData2D::ConvertToLightmap2D(bool bDebugThisMapping, int PaddedDebugX, int PaddedDebugY) Line 79	C++
UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 970	C++
//更多略...
  • 基于 ConvertToLightmap2D 生成 FLightMapData2D* ConvertedLightMap 后,将其赋值给 FLightMapData2D* FinalLightmapData 。

根据 FTextureMappingStaticLightingData 创建和更新 FMeshMapBuildData (及其中的Lightmap)

  • FStaticLightingSystem::ProcessTextureMapping 中创建和初始化的 FTextureMappingStaticLightingData& LightingData

UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 908	C++
//更多略...
  • 基于 LightingData 初始化和更新 FStaticLightingSystem::ProcessTextureMapping 时的 FGatheredLightMapData2D PaddedLightMapData

UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 967	C++
//更多略...
  • 基于 PaddedLightMapData 初始化和更新 FStaticLightingSystem::ProcessTextureMapping 时的 FLightMapData2D* FinalLightmapData

UnrealLightmass.exe!Lightmass::FStaticLightingSystem::ProcessTextureMapping(Lightmass::FStaticLightingTextureMapping * TextureMapping) Line 971	C++
//更多略...
  • 基于 FLightMapData2D* FinalLightmapData 初始化和更新 TList* StaticLightingLink->Element.LightMapData


  • 基于 TList* StaticLightingLink 在这里写 Lightmass::FLightmassSolverExporter::ExportResults ,TList*

UnrealLightmass.exe!Lightmass::FLightmassSolverExporter::ExportResults(Lightmass::FTextureMappingStaticLightingData & LightingData, bool bUseUniqueChannel) Line 417	C++
//更多略...
  • 在 FLightmassSolverExporter::ExportResults 中,FLightMapData2D::Compress 的时候,在 QuantizeLightSamples 中根据 FLightSampleData 的 Coefficients 更新 OutAdd 、OutMultiply 和 OutLightSamples 等,赋值给 FLightMapData2D 的 Add 、Multiply 和 Data 等。



UnrealLightmass.exe!Lightmass::GetLUVW(const float * RGB, float & L, float & U, float & V, float & W) Line 67	C++
UnrealLightmass.exe!Lightmass::QuantizeLightSamples(TArray<Lightmass::FLightSample,TSizedDefaultAllocator<32>> & InLightSamples, TArray<Lightmass::FQuantizedLightSampleData,TSizedDefaultAllocator<32>> & OutLightSamples, float[4] * OutMultiply, float[4] * OutAdd, int DebugSampleIndex, bool bUseMappedFlag) Line 146	C++
UnrealLightmass.exe!Lightmass::FLightMapData2D::Quantize(int DebugSampleIndex) Line 349	C++
UnrealLightmass.exe!Lightmass::FLightMapData2D::Compress(int DebugSampleIndex) Line 56	C++
UnrealLightmass.exe!Lightmass::FLightmassSolverExporter::ExportResults(Lightmass::FTextureMappingStaticLightingData & LightingData, bool bUseUniqueChannel) Line 399	C++
//更多略...
  • 在 FLightmassProcessor::ProcessMapping 的时候根据 FTextureMappingImportHelper* TImportData 的 FQuantizedLightmapData* QuantizedData 等创建和更新 TUniquePtr Allocation (的 TArray RawData 等数据)
[UE4Editor-Engine.dll] FLightMap2D::AllocateLightMap(UObject *,FQuantizedLightmapData *&,const TMap<ULightComponent *,FShadowMapData2D *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<ULightComponent *,FShadowMapData2D *,0> > &,const FBoxSphereBounds &,ELightMapPaddingType,ELightMapFlags) LightMap.cpp:2073
[UE4Editor-Landscape.dll] FLandscapeStaticLightingTextureMapping::Apply(FQuantizedLightmapData *,const TMap<ULightComponent *,FShadowMapData2D *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<ULightComponent *,FShadowMapData2D *,0> > &,ULevel *) LandscapeLight.cpp:68
[UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessMapping(const FGuid &) Lightmass.cpp:4342
[Inlined] [UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessAvailableMappings() Lightmass.cpp:4391
[UE4Editor-UnrealEd.dll] FLightmassProcessor::CompleteRun() Lightmass.cpp:3472
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2237
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...
  • 在 FStaticLightingSystem::FinishLightmassProcess 的时候 FLightMap2D::EncodeTextures 时创建和初始化 FLightMapPendingTexture 。之后执行 FLightMapPendingTexture 的 FAsyncEncode 异步编码操作。


[UE4Editor-Engine.dll] FLightMapPendingTexture::CreateUObjects() LightMap.cpp:1230
[UE4Editor-Engine.dll] FLightMap2D::EncodeTextures(UWorld *,ULevel *,bool,bool) LightMap.cpp:2544
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::EncodeTextures(bool) StaticLightingSystem.cpp:1244
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2253
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...
  • 在 FAsyncEncode 中,根据 FLightMapAllocation (的 TArray RawData 、Scale 、Add 等数据)对 FLightMapPendingTexture 中的 ULightMapTexture2D* Textures[NUM_STORED_LIGHTMAP_COEF] 进行编码,设置其 DestColor 的 RGBA 。

[UE4Editor-Engine.dll] FLightMapPendingTexture::EncodeCoefficientTexture(int,UTexture *,unsigned int,const FColor &,bool) LightMap.cpp:1648
[UE4Editor-Engine.dll] FLightMapPendingTexture::StartEncoding(ULevel *,ITextureCompressorModule *) LightMap.cpp:1827
[UE4Editor-Engine.dll] FAsyncEncode<FLightMapPendingTexture>::DoThreadedWork() SceneManagement.h:903
[UE4Editor-Core.dll] FQueuedThread::Run() ThreadingBase.cpp:994
[UE4Editor-Core.dll] FRunnableThreadWin::Run() WindowsRunnableThread.cpp:84
[UE4Editor-Core.dll] FRunnableThreadWin::GuardedRun() WindowsRunnableThread.cpp:27
[kernel32.dll] <unknown> 0x00007ffeb8d77604
[ntdll.dll] <unknown> 0x00007ffeba9226a1
  • 在 FLightMapPendingTexture::PostEncode 的时候通过 UTexture::CachePlatformData 把 FLightMapPendingTexture 的 ULightMapTexture2D* Textures[NUM_STORED_LIGHTMAP_COEF] 序列化到本地(生成DDC啥的),即生成了对应的 Lightmap 贴图资源。

[UE4Editor-Engine.dll] FTexturePlatformData::Cache(UTexture &,const FTextureBuildSettings *,unsigned int,ITextureCompressorModule *) TextureDerivedData.cpp:854
[UE4Editor-Engine.dll] UTexture::CachePlatformData(bool,bool,bool,ITextureCompressorModule *) TextureDerivedData.cpp:1690
[Inlined] [UE4Editor-Engine.dll] FLightMapPendingTexture::PostEncode(UTexture2D *) LightMap.cpp:596
[UE4Editor-Engine.dll] FLightMapPendingTexture::PostEncode() LightMap.cpp:679
[UE4Editor-Engine.dll] FLightMap2D::EncodeTextures(UWorld *,ULevel *,bool,bool) LightMap.cpp:2575
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::EncodeTextures(bool) StaticLightingSystem.cpp:1244
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2253
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...
  • 之后调用 FLightMapPendingTexture::FinishCacheTexture 做一些收尾,如提示等。

[Inlined] [UE4Editor-Engine.dll] FLightMapPendingTexture::FinishCacheTexture(UTexture2D *) LightMap.cpp:589
[UE4Editor-Engine.dll] FLightMapPendingTexture::FinishCachingTextures() LightMap.cpp:753
[UE4Editor-Engine.dll] FLightMap2D::EncodeTextures(UWorld *,ULevel *,bool,bool) LightMap.cpp:2590
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::EncodeTextures(bool) StaticLightingSystem.cpp:1244
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2253
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...
  • 在 FLightmassProcessor::ImportTextureMapping 从 Swarm 中读 Lightmass::FLightMapData2DData LMLightmapData2DData,据此初始化 FTextureMappingImportHelper& TMImport 的 FQuantizedLightmapData* QuantizedData (如其 Add 、Multiply 和 Data 等)

  • FLightmassProcessor::ProcessMapping 中 FStaticMeshStaticLightingTextureMapping::Apply 时,拿到对应的 ULevel ,通过 GetOrCreateMapBuildData 获取对应的 UMapBuildDataRegistry ,通过 AllocateMeshBuildData 分配 FMeshMapBuildData ,通过 FLightMap2D::AllocateLightMap 根据 FQuantizedLightmapData 类型的 QuantizedData 创建和分配 FMeshMapBuildData 。

[UE4Editor-Landscape.dll] FLandscapeStaticLightingTextureMapping::Apply(FQuantizedLightmapData *,const TMap<ULightComponent *,FShadowMapData2D *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<ULightComponent *,FShadowMapData2D *,0> > &,ULevel *) LandscapeLight.cpp:68
[UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessMapping(const FGuid &) Lightmass.cpp:4342
[Inlined] [UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessAvailableMappings() Lightmass.cpp:4391
[UE4Editor-UnrealEd.dll] FLightmassProcessor::CompleteRun() Lightmass.cpp:3472
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2237
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...


[UE4Editor-Engine.dll] FStaticMeshStaticLightingTextureMapping::Apply(FQuantizedLightmapData *,const TMap<ULightComponent *,FShadowMapData2D *,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<ULightComponent *,FShadowMapData2D *,0> > &,ULevel *) StaticMeshLight.cpp:260
[UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessMapping(const FGuid &) Lightmass.cpp:4342
[Inlined] [UE4Editor-UnrealEd.dll] FLightmassProcessor::ProcessAvailableMappings() Lightmass.cpp:4391
[UE4Editor-UnrealEd.dll] FLightmassProcessor::CompleteRun() Lightmass.cpp:3472
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2237
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
//更多略...



  • 在 FLightMap2D::AllocateLightMap 中基于 FQuantizedLightmapData*& SourceQuantizedData 创建 TUniquePtr Allocation,添加到 FLightMapAllocationGroup AllocationGroup 的 TArray<TUniquePtr, TInlineAllocator<1>> Allocations ,继而赋值给 static TArray PendingLightMaps (尚未编码为纹理的Lightmap列表) 。


  • 基于 PendingLightMaps 设置和更新 PendingTextures
  • PendingLightMaps 跟 PendingTextures 并非一一对应关系,而是多对一的关系。如 PendingLightMaps 可能有23张,对应的 PendingTextures 只有6张。多张源自 PendingLightMaps 的贴图会在 FLightMap2D::EncodeTextures 的时候通过一定的规则整合到一张图中。

  • FLightMapPendingTexture::AddElement ,其中调用 FTextureLayout::AddElement 。
  • 在纹理中查找足够大的空闲区域以包含给定大小的表面。 如果找到足够大的区域,则将其标记为正在使用,输出参数 OutBaseX 和 OutBaseY 设置为空闲区域左上角的坐标,并且函数返回 true。 否则,该函数返回 false,并且 OutBaseX 和 OutBaseY 保持未初始化状态。 如果分配成功,Allocation.OffsetX和Allocation.OffsetY将被设置为分配区域的左上角。参数 Allocation: 尝试匹配的Lightmap分配组,参数 ForceIntoThisTexture :在考虑是否应将映射打包到该纹理时是否忽略距离和其他因素
[UE4Editor-Engine.dll] FLightMap2D::EncodeTextures(UWorld *,ULevel *,bool,bool) LightMap.cpp:2521
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::EncodeTextures(bool) StaticLightingSystem.cpp:1244
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2253
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
[UE4Editor-UnrealEd.dll] UEditorEngine::UpdateBuildLighting() StaticLightingSystem.cpp:2519
//更多略...
  • 基于 PendingTextures 中的元素创建 Lightmap 文件。每次 创建2张,一张HQ,一张LQ


Lightmap 从磁盘里的 FMeshMapBuildData 中到 Shader 参数

  • 在地图加载完毕,FLinkerLoad Preload 的时候触发 UMapBuildDataRegistry 的反序列化 Serialize ,初始化 MeshMapBuildData 中的 LightMap 等数据。

[UE4Editor-Engine.dll] operator<<(FArchive &,FMeshMapBuildData &) MapBuildData.cpp:39
[Inlined] [UE4Editor-Engine.dll] UE4Tuple_Private::operator<<(FArchive &,UE4Tuple_Private::TTupleBase<TIntegerSequence<unsigned int,0,1>,FGuid,FMeshMapBuildData> &) Tuple.h:318
[Inlined] [UE4Editor-Engine.dll] operator<<(FArchive &,TSetElement<TTuple<FGuid,FMeshMapBuildData> > &) Set.h:244
[UE4Editor-Engine.dll] operator<<(FArchive &,TSparseArray<TSetElement<TTuple<FGuid,FMeshMapBuildData> >,TSparseArrayAllocator<TSizedDefaultAllocator<32>,FDefaultBitArrayAllocator> > &) SparseArray.h:606
[UE4Editor-Engine.dll] operator<<(FArchive &,TSet<TTuple<FGuid,FMeshMapBuildData>,TDefaultMapHashableKeyFuncs<FGuid,FMeshMapBuildData,0>,FDefaultSetAllocator> &) Set.h:994
[Inlined] [UE4Editor-Engine.dll] operator<<(FArchive &,TMapBase<FGuid,FMeshMapBuildData,FDefaultSetAllocator,TDefaultMapHashableKeyFuncs<FGuid,FMeshMapBuildData,0> > &) Map.h:712
[UE4Editor-Engine.dll] UMapBuildDataRegistry::Serialize(FArchive &) MapBuildData.cpp:367
[UE4Editor-CoreUObject.dll] FLinkerLoad::Preload(UObject *) LinkerLoad.cpp:4180
[UE4Editor-CoreUObject.dll] EndLoad(FUObjectSerializeContext *) UObjectGlobals.cpp:1604
[UE4Editor-CoreUObject.dll] <lambda_fc6dac93f63b3d981a3bc8e7f817eeef>::operator()() UObjectGlobals.cpp:1270
[UE4Editor-CoreUObject.dll] LoadPackageInternal(UPackage *,const wchar_t *,unsigned int,FLinkerLoad *,FArchive *,const FLinkerInstancingContext *) UObjectGlobals.cpp:1375
[UE4Editor-CoreUObject.dll] LoadPackage(UPackage *,const wchar_t *,unsigned int,FArchive *,const FLinkerInstancingContext *) UObjectGlobals.cpp:1473
[UE4Editor-UnrealEd.dll] UEditorEngine::Map_Load(const wchar_t *,FOutputDevice &) EditorServer.cpp:2667
[UE4Editor-UnrealEd.dll] UEditorEngine::HandleMapCommand(const wchar_t *,FOutputDevice &,UWorld *) EditorServer.cpp:6154
[UE4Editor-UnrealEd.dll] UEditorEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) EditorServer.cpp:5711
[UE4Editor-UnrealEd.dll] UUnrealEdEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) UnrealEdSrv.cpp:697
[UE4Editor-UnrealEd.dll] FEditorFileUtils::LoadMap(const FString &,bool,const bool) FileHelpers.cpp:2554
//更多略...
  • 在加载中的 UMapBuildDataRegistry PostLoad 的时候触发 SetupLightmapResourceClusters ,其通过 GetClusterInput 拿到 FLightmapClusterResourceInput ,以此为 UMapBuildDataRegistry 的 LightmapResourceClusters 进行初始化,完事后赋值为 ResourceCluster 保存到 FMeshMapBuildData 。

[UE4Editor-Engine.dll] UMapBuildDataRegistry::SetupLightmapResourceClusters() MapBuildData.cpp:872
[UE4Editor-CoreUObject.dll] UObject::ConditionalPostLoad() Obj.cpp:1092
[UE4Editor-CoreUObject.dll] UObject::PostLoadSubobjects(FObjectInstancingGraph *) Obj.cpp:1125
[Inlined] [UE4Editor-CoreUObject.dll] UObject::ConditionalPostLoadSubobjects(FObjectInstancingGraph *) Obj.cpp:1190
[UE4Editor-CoreUObject.dll] UObject::ConditionalPostLoad() Obj.cpp:1075
[UE4Editor-CoreUObject.dll] EndLoad(FUObjectSerializeContext *) UObjectGlobals.cpp:1662
[UE4Editor-CoreUObject.dll] <lambda_fc6dac93f63b3d981a3bc8e7f817eeef>::operator()() UObjectGlobals.cpp:1270
[UE4Editor-CoreUObject.dll] LoadPackageInternal(UPackage *,const wchar_t *,unsigned int,FLinkerLoad *,FArchive *,const FLinkerInstancingContext *) UObjectGlobals.cpp:1375
[UE4Editor-CoreUObject.dll] LoadPackage(UPackage *,const wchar_t *,unsigned int,FArchive *,const FLinkerInstancingContext *) UObjectGlobals.cpp:1473
[UE4Editor-UnrealEd.dll] UEditorEngine::Map_Load(const wchar_t *,FOutputDevice &) EditorServer.cpp:2667
[UE4Editor-UnrealEd.dll] UEditorEngine::HandleMapCommand(const wchar_t *,FOutputDevice &,UWorld *) EditorServer.cpp:6154
[UE4Editor-UnrealEd.dll] UEditorEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) EditorServer.cpp:5711
[UE4Editor-UnrealEd.dll] UUnrealEdEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) UnrealEdSrv.cpp:697
[UE4Editor-UnrealEd.dll] FEditorFileUtils::LoadMap(const FString &,bool,const bool) FileHelpers.cpp:2554
[UE4Editor-UnrealEd.dll] FEditorFileUtils::LoadDefaultMapAtStartup() FileHelpers.cpp:4045
//更多略
  • 在地图加载完毕,InitWorld 的时候,调用 UMapBuildDataRegistry 的 InitializeClusterRenderingResources 来初始化 Cluster 渲染资源。通过 GetLightmapClusterResourceParameters 从 FLightmapClusterResourceInput 中拿到 LightMapTextures 等数据为 FLightmapResourceClusterShaderParameters 的 LightMapTexture 和 LightMapSampler 等赋值。

[UE4Editor-Engine.dll] GetLightmapClusterResourceParameters(Type,const FLightmapClusterResourceInput &,IAllocatedVirtualTexture *,FLightmapResourceClusterShaderParameters &) SceneManagement.cpp:944
[UE4Editor-Engine.dll] FLightmapResourceCluster::UpdateUniformBuffer_RenderThread() LightMap.cpp:3451
[UE4Editor-Engine.dll] UMapBuildDataRegistry::InitializeClusterRenderingResources(Type) MapBuildData.cpp:916
[UE4Editor-Engine.dll] ULevel::InitializeRenderingResources() Level.cpp:1995
[UE4Editor-Engine.dll] UWorld::InitWorld(InitializationValues) World.cpp:1683
[UE4Editor-UnrealEd.dll] UEditorEngine::Map_Load(const wchar_t *,FOutputDevice &) EditorServer.cpp:2741
[UE4Editor-UnrealEd.dll] UEditorEngine::HandleMapCommand(const wchar_t *,FOutputDevice &,UWorld *) EditorServer.cpp:6154
[UE4Editor-UnrealEd.dll] UEditorEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) EditorServer.cpp:5711
[UE4Editor-UnrealEd.dll] UUnrealEdEngine::Exec(UWorld *,const wchar_t *,FOutputDevice &) UnrealEdSrv.cpp:697
[UE4Editor-UnrealEd.dll] FEditorFileUtils::LoadMap(const FString &,bool,const bool) FileHelpers.cpp:2554
//更多略...
  • 在 FMeshPassProcessor BuildMeshDrawCommands 的时候触发 FUniformLightMapPolicy 等的 GetVertexShaderBindings 和 GetPixelShaderBindings 等。

[UE4Editor-Renderer.dll] SetupLCIUniformBuffers(const FPrimitiveSceneProxy *,const FLightCacheInterface *,FRHIUniformBuffer *&,FRHIUniformBuffer *&,FRHIUniformBuffer *&) LightMapRendering.cpp:60
[UE4Editor-Renderer.dll] FUniformLightMapPolicy::GetPixelShaderBindings(const FPrimitiveSceneProxy *,const FLightCacheInterface *const &,const FUniformLightMapPolicyShaderParametersType *,FMeshDrawSingleShaderBindings &) LightMapRendering.cpp:139
[UE4Editor-Renderer.dll] TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>::GetShaderBindings(const FScene *,Type,const FPrimitiveSceneProxy *,const FMaterialRenderProxy &,const FMaterial &,const FMeshPassProcessorRenderState &,const TBasePassShaderElementData<FUniformLightMapPolicy> &,FMeshDrawSingleShaderBindings &) BasePassRendering.inl:93
[UE4Editor-Renderer.dll] FMeshPassProcessor::BuildMeshDrawCommands<TMeshProcessorShaders<TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,FBaseHS,FBaseDS,TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>,FMeshMaterialShader,FMeshMaterialShader,FMeshMaterialShader>,TBasePassShaderElementData<FUniformLightMapPolicy> >(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,const FMaterialRenderProxy &__restrict,const FMaterial &__restrict,const FMeshPassProcessorRenderState &__restrict,TMeshProcessorShaders<TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,FBaseHS,FBaseDS,TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>,FMeshMaterialShader,FMeshMaterialShader,FMeshMaterialShader>,ERasterizerFillMode,ERasterizerCullMode,FMeshDrawCommandSortKey,EMeshPassFeatures,const TBasePassShaderElementData<FUniformLightMapPolicy> &) MeshPassProcessor.inl:101
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::ProcessDeferredShadingPath(const FMeshBatch &,unsigned long long,const FMaterial &,const FMaterialRenderProxy &,const FPrimitiveSceneProxy *,int) EditorPrimitivesRendering.cpp:111
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::TryAddMeshBatch(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,int,const FMaterialRenderProxy &,const FMaterial &) EditorPrimitivesRendering.cpp:56
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::AddMeshBatch(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,int) EditorPrimitivesRendering.cpp:29
//更多略...
  • 在 GetPixelShaderBindings 等的时候通过 SetupLCIUniformBuffers 调用 GDefaultLightmapResourceClusterUniformBuffer 的 GetUniformBufferRHI() 拿到全局的 FDefaultLightmapResourceClusterUniformBuffer 为 LightmapResourceClusterBuffer 赋值。

[UE4Editor-Renderer.dll] SetupLCIUniformBuffers(const FPrimitiveSceneProxy *,const FLightCacheInterface *,FRHIUniformBuffer *&,FRHIUniformBuffer *&,FRHIUniformBuffer *&) LightMapRendering.cpp:60
[UE4Editor-Renderer.dll] FUniformLightMapPolicy::GetVertexShaderBindings(const FPrimitiveSceneProxy *,const FLightCacheInterface *const &,const FUniformLightMapPolicyShaderParametersType *,FMeshDrawSingleShaderBindings &) LightMapRendering.cpp:122
[UE4Editor-Renderer.dll] TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>::GetShaderBindings(const FScene *,Type,const FPrimitiveSceneProxy *,const FMaterialRenderProxy &,const FMaterial &,const FMeshPassProcessorRenderState &,const TBasePassShaderElementData<FUniformLightMapPolicy> &,FMeshDrawSingleShaderBindings &) BasePassRendering.inl:45
[UE4Editor-Renderer.dll] FMeshPassProcessor::BuildMeshDrawCommands<TMeshProcessorShaders<TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,FBaseHS,FBaseDS,TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>,FMeshMaterialShader,FMeshMaterialShader,FMeshMaterialShader>,TBasePassShaderElementData<FUniformLightMapPolicy> >(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,const FMaterialRenderProxy &__restrict,const FMaterial &__restrict,const FMeshPassProcessorRenderState &__restrict,TMeshProcessorShaders<TBasePassVertexShaderPolicyParamType<FUniformLightMapPolicy>,FBaseHS,FBaseDS,TBasePassPixelShaderPolicyParamType<FUniformLightMapPolicy>,FMeshMaterialShader,FMeshMaterialShader,FMeshMaterialShader>,ERasterizerFillMode,ERasterizerCullMode,FMeshDrawCommandSortKey,EMeshPassFeatures,const TBasePassShaderElementData<FUniformLightMapPolicy> &) MeshPassProcessor.inl:87
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::ProcessDeferredShadingPath(const FMeshBatch &,unsigned long long,const FMaterial &,const FMaterialRenderProxy &,const FPrimitiveSceneProxy *,int) EditorPrimitivesRendering.cpp:111
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::TryAddMeshBatch(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,int,const FMaterialRenderProxy &,const FMaterial &) EditorPrimitivesRendering.cpp:56
[UE4Editor-Renderer.dll] FEditorPrimitivesBasePassMeshProcessor::AddMeshBatch(const FMeshBatch &__restrict,unsigned long long,const FPrimitiveSceneProxy *__restrict,int) EditorPrimitivesRendering.cpp:29
//更多略...
  • 回到 GetPixelShaderBindings 等中,把 SetupLCIUniformBuffers 获取到的 LightmapResourceClusterBuffer 作为 LightmapResourceCluster PixelShaderParameters 绑定到 ShaderBindings 中。

  • FUniformLightMapPolicyShaderParametersType 中设置和绑定了名为 LightmapResourceCluster 的 FShaderUniformBufferParameter ,可在 Shader 中获取。

静态阴影的渲染流程(Lightmap 从 Shader 参数到屏幕上的阴影效果)

  • 在 Shader 中采样拿到对应的 Lightmap ,并作解析 (/Engine/Private/LightmapCommon.ush :GetLightMapColorLQ 、 GetLightMapColorHQ)

  • 基于 Lightmap 的光照和阴影的计算,最终叠加在 basePass 的 diffuse color 上 (/Engine/Private/BasePassPixelShader.usf:GetPrecomputedIndirectLightingAndSkyLight,/Engine/Private/BasePassPixelShader.usf:FPixelShaderInOut_MainPS,/Engine/Private/BasePassPixelShader.usf:LightAccumulator_Add)



  • 截帧下的 Pass、贴图和 Shader 。






问题答疑

  • 背景提到的问题的原因和解决办法
  • 在自定义的地形系统获取静态光照相关的顶点数据( FLandscapeStaticLightingMesh::GetStaticLightingVertex)的时候,计算顶点 (OutVertex) 的 世界坐标 (WorldPosition) 时,没有为新地形做适配。解决办法为基于新地形的逻辑去计算顶点的世界坐标( OutVertex.WorldPosition )即可。
  • Lightmap 中 LQ 和 HQ 的异同,像素的编码规则、数据来源和在 Shader 中的解码使用
  • Lightmap 包含 HighQuality 和 LowQuality 两种,根据是否移动平台决定使用哪种,光照烘焙的时候两种都会烘焙。
  • 光照信息分为上下两部分,上半部分存储的是线性空间的光照颜色(HQ 的基于 Gamma )和亮度(HQ 不含)的预乘信息,下半部分存储是入射光照的最大贡献方向。
  • HQ Lightmap 的 Alpha 分上下两部分,上半部分存整数,下半部分小数。

  • LQ Lightmap 不包含 Alpha 通道

  • 详细的编码规则见:LightmapData.cpp 的 QuantizeLightSamples


HQ Lightmap 的编码规则

LQ Lightmap 的编码规则

  • SourceSample 的 Coefficients[0] 和 Coefficients[2] 分别表示 HQ 和 LQ 的 Lightmap ,来源见:FGatheredLightMapSample::ConvertToLightSample 或详细流程。

  • 其中 ConvertToLightSampleHelper

  • SHVector 表示世界空间入射光,计算方式和来源见 FStaticLightingSystem::CalculateDirectAreaLightingTextureMapping 或详细流程。



  • 两种 Lightmap 在 Shader 中的解码

  • Lightmap 的名字
  • 在 FLightMap2D::EncodeTextures 中,贴图合并后生成最终的贴图前通过 FLightMapPendingTexture::GetLightmapName 获取名字。

[UE4Editor-Engine.dll] FLightMapPendingTexture::GetLightmapName(int,int) LightMap.cpp:1934
[UE4Editor-Engine.dll] FLightMapPendingTexture::CreateUObjects() LightMap.cpp:1229
[UE4Editor-Engine.dll] FLightMap2D::EncodeTextures(UWorld *,ULevel *,bool,bool) LightMap.cpp:2544
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::EncodeTextures(bool) StaticLightingSystem.cpp:1244
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::FinishLightmassProcess() StaticLightingSystem.cpp:2253
[UE4Editor-UnrealEd.dll] FStaticLightingManager::ProcessLightingData() StaticLightingSystem.cpp:170
[UE4Editor-UnrealEd.dll] FStaticLightingSystem::UpdateLightingBuild() StaticLightingSystem.cpp:2383
[UE4Editor-UnrealEd.dll] FStaticLightingManager::UpdateBuildLighting() StaticLightingSystem.cpp:347
[UE4Editor-UnrealEd.dll] UEditorEngine::UpdateBuildLighting() StaticLightingSystem.cpp:2519
//更多略...
  • Lightmap 的尺寸设置和限制
  • 在 FLightMap2D::EncodeTextures 中根据 AWorldSettings 中设置的 PackedLightAndShadowMapTextureSize 及贴图的实际大小决定贴图的尺寸。


FLightMap2D::EncodeTextures

  • Lightmap 的宽高比
  • 如果没有现有的合适纹理,创建一个新纹理。 如果我们没有 VT,需要 2:1 的宽高比来处理将Lightmap打包到顶部/底部纹理区域。如果包含 VT,最好使用方形Lightmap(更好的页表使用)。


FLightMap2D::EncodeTextures


FTextureLayout::AddElement

  • Lightmap 的数目与合并
  • 各静态资源的光照烘焙数据存储在 FQuantizedLightmapData*& SourceQuantizedData 中,据此添加到 PendingLightMaps 内,在 FLightMap2D::EncodeTextures 中对 PendingLightMaps 的贴图进行重新排序、编码(在 FLightMapPendingTexture::AddElement 、FTextureLayout::AddElement 中合并),生成与 Worldsettings 等处看到的数目、数据和名字等一致的 Lightmaps 。
  • Lightmap 的 UV
  • 默认情况下,参与烘焙的静态 Mesh 顶点的第一个 UV(UE4 中的 UV 通道 0)为纹理 UV,据此在其 UV1 保存Lightmap UV ,结合坐标 CoordinateScale和CoordinateBias,“标识”Mesh在贴图中的采样区域。此特定 UV 用于存储烘焙光照和阴影信息。

  • 纹理 UV 不一定为每个面使用 UV 空间(如当它们的不同面具有相同的纹理时),Lightmap则所有面都有自己的 UV 空间表示。
  • Lightmap 与 Shader
  • 主要用于 LightmapCommon.ush 的 GetLightMapColorLQ 和 GetLightMapColorHQ 中,见:Lightmap 中 LQ 和 HQ 的异同,像素的编码规则、数据来源和在 Shader 中的解码使用,详见原理流程。
  • Lightmap 与阴影
  • 静态光源下的静态物体主要基于 Lightmap 来渲染显示阴影效果。
  • 见“概念简述”的“Lightmap”部分。
  • Lightmap 与 Mesh
  • Lightmap 的像素点的颜色基于各网格顶点与光照之间的关系权重等计算得到,详见“Lightmap 中 LQ 和 HQ 的异同,像素的编码规则、数据来源和在 Shader 中的解码使用”或原理流程。
  • Lightmap 与 Shadowmap
  • Light Map、Shadow Map 和 Precomputed Light Volume 是预计算光照的3个主要组成部分。
  • Shadow Map 并不是为 Static Light 准备的,而是为 Stational Light 准备的。在进行光照计算时,每一个动态的物体,都会根据自己的 bound box 与 stational Light 的朝向关系,将 Shadow Map 合成到光照运算结果中去。
  • 本文主要关注于 Static Light ,不对 Shadow Map 做过多讨论。
  • 不添加 Lightmass importance volume 的影响
  • 不添加 Lightmass importance volume ,在烘焙光照时会提示“No importance volume found and the scene is so large that the automatically synthesized volume will not yield good results. Please add a tightly bounding Lightmass importance volume to optimize your scene's quality and Lighting build times.”
  • 主要影响场景的性能、质量和照明构建时间。但是会自动添加或缩放得到一个大概可用的体积用于烘焙。

  • 为什么地形渲染显示的时候没问题但烘焙的时候顶点有问题?
  • 地形在渲染的时候可在顶点着色器里对输入的Mesh的顶点数据做转换,变成其它样子后用于渲染。但烘焙的时候不一定会走相关的渲染逻辑。
  • 烘焙见:FLandscapeStaticLightingMesh::GetStaticLightingVertex

相关链接

**声明:**本文来自公众号:GameDevLearning,转载请附上原文链接(https://mp.weixin.qq.com/s/LHKDWDgW4UKDsiT50AznYg)及本声明。

0 Answers