UE渲染源码剖析

首先,老规矩:

未经允许禁止转载(防止某些人乱转,转着转着就到蛮牛之类的地方去了)

B站:Heskey0


UE基础

架构

  • ULevel是UE的关卡,是场景中物体的集合,存储着一系列Actor

  • UWorld是ULevel的容器。每个UWorld实例必须包含一个主关卡(Persistent Level),还可能包含若干个流式关卡(Streaming Level,可选,非必需,可按需动态加载和卸载)

  • 常见的WorldType有游戏(Game)、编辑器(Editor)、编辑器播放(PIE)以及预览模式(EditorPreview、GamePreview)等。编辑器内的场景其实也是个World,类型为Editor。

内存

GC

  • UObject提供了GC

  • 标记清除法

    • 将可达对象标记为TRUE
    • 收集待清理对象(UE增加了一步)
    • 清理不可达对象
  • 原子性GC过程

    • 获取GC锁,防止GC被其它线程操作
    • 执行GC
    • 释放GC锁

多线程渲染

UGameEngine::Tick()

  • UWorld::Tick() : [LevelTick.cpp]
    *

单线程渲染:CPU和GPU可能相互等待

UE的线程(FRunnableThread)

  • 游戏线程(Game Thread)(也称作主线程)(UGameEngine::Tick)
  • 渲染线程(FRenderingThread)
    • 专门用于生成渲染指令 (FRenderCommand) 和渲染逻辑的独立线程
  • RHI线程(RHI Thread)
    • 转换渲染指令到指定图形API,创建、上传渲染资源到GPU
    • RHI全称是Render Hardware Interface(渲染硬件接口), 封装了众多图形API(DirectX、OpenGL、Vulkan、Metal)之间的差异

线程同步

  • 渲染命令栅栏(FRendeLauchEngineLooprCommandFence)

    • 强制同步RHI和GPU交换链
  • 同步(FFrameEndSync)

    • 在引擎循环的帧末尾添加游戏线程和渲染线程的同步事件

渲染机制

剖析虚幻渲染体系(03)- 渲染机制 - 0向往0 - 博客园 (cnblogs.com)

Draw Call 的产生

UE5

  • RHI层的变动主要在于将各种顶点、索引Buffer统一成了FRHIBuffer。

  • Renderer层增强了光线追踪,特别是屏幕空间的光线追踪,加强了距离场的各种应用,同时删除了LPV。

  • Engine层主要围绕着RHI、Renderer层的变动做了相应修改和调整

Nanite

渲染特性:

  • 不支持:
    • Forward rendering
    • MSAA

Component:

  • 支持:

    • static mesh
  • 不支持:

    • skeletal animation
    • morph target / blend shape
    • spline mesh

Preview

1. VSM (Virtual Shadow Map):一种新的阴影投射方法

UE5中VirtualShadowMap的简易实现原理(一) - 知乎 (zhihu.com)

UE5 虚拟阴影贴图 (VirtualShadowMaps)的优势和局限性 - 知乎 (zhihu.com)

  • 开启后,会替换传统阴影技术(CSM, SDF,......),GPU上实现

  • 开启后,SMRT (Shadow Map Ray Tracing) 可以利用VSM生成阴影

Algorithm Overview:

  • 假设有一张分辨率极高的 Virtual ShadowMap(16K)

  • 通过坐标转换,找到深度图中像素对应到 Virtual ShadowMap中的格子 (Virtual Tile),然后标记为Used,每个标记的格子挨个(紧密)存放到一张UAV (Tile Padding)

  • 利用UAV走正常的shadowmap流程,没有标记的 Virtual Tile 用不到

Mipmap适配:

  • 生成不同分辨率的Virtual ShadowMap(Mipmap)
  • 根据深度算出当前屏幕对应的Mipmap Level级别

裁剪图(Clipmap):

  • 单张虚拟阴影贴图无法提供足够的分辨率来覆盖大型区域

  • 定向光源使用裁剪图(Clipmap)结构来扩展摄像机周围的范围

  • 每个裁剪图级别都有其单独的16K VSM

2. SMRT (Shadow Map Ray Tracing):利用VSM生成软阴影

PCF过于模糊

Algorithm Overview:

  • 沿光源发射一些光线
  • 用VSM作测试
3. TSR (Temporal Super Resolution):替换传统TAA

Core

Cluster :

  • 一组相邻三角形的集合 ( 在早前已被育碧和寒霜引擎使用 )

  • Cluster可以和相邻的Cluster动态合批

Lumen

Surface Cache

  • Lumen为场景表面附近生成Surface Cache
  • 用于快速查找射线命中点的光照

屏幕追踪

  • Lumen先对屏幕进行追踪
  • 追踪失败,则使用更可靠的方法

光线追踪

  • 软件光线追踪(ray marching)

    • 依赖于距离场,较大网格会有不良表现
    • Detail Tracing(默认):前两米使用网格距离场,其它距离使用全局距离场
    • Global Tracing:利用全局距离场快速追踪,损失画质效果
  • 硬件光线追踪

补充(非Lumen):DFAO

  • DFAO中网格距离场没有合并到全局距离场
  • 所有的网格距离场atlas到一张体贴图上(300M显存)
  • step1 : 光源出发Trace,剔除掉不会相交的距离场
  • step2 : 从场景出发沿光源做Trace

Shader

第一梯队的shader模块是最底层最基础的模块,这些模块不会引用其它模块,但会被其它很多模块引用。这些模块主要有:

  • BasePassCommon.ush

  • BRDF.ush

  • CapsuleLight.ush

  • Common.ush

    • 图形API或Feature Level相关的宏、类型、局部变量、静态变量、基础工具接口

      • FeatureLevel: Feature levels in Direct3D - Wikipedia

      • // FEATURE_LEVEL的宏定义
        #define FEATURE_LEVEL_ES2_REMOVED    1
        #define FEATURE_LEVEL_ES3_1          2
        #define FEATURE_LEVEL_SM3            3
        #define FEATURE_LEVEL_SM4            4
        #define FEATURE_LEVEL_SM5            5
        #define FEATURE_LEVEL_MAX            6
        
  • CommonViewUniformBuffer.ush

  • Definitions.usf

    • 定义了一些常见的宏,防止其它模块引用时出现语法错误
  • FP16Math.ush

  • Platform.ush

    • 图形API(DirectX、OpenGL、Vulkan、Metal)和FEATURE_LEVEL相关的宏、变量及工具类接口
  • ShaderVersion.ush

  • LightGridCommon.ush

  • LocalVertexFactoryCommon.ush

    • Get/Set :
    • Color
    • LightMapCoordinates
    • Tangent
    • 图元id(PrimitiveId)
  • ShadingCommon.ush

    • 定义了材质所有着色模型ID,并提供了少量相关的工具类接口

      • // 材质着色模型, 每种类型都有对应的光照算法和流程.
        #define SHADINGMODELID_UNLIT                0
        #define SHADINGMODELID_DEFAULT_LIT            1
        #define SHADINGMODELID_SUBSURFACE            2
        #define SHADINGMODELID_PREINTEGRATED_SKIN    3
        #define SHADINGMODELID_CLEAR_COAT            4
        #define SHADINGMODELID_SUBSURFACE_PROFILE    5
        #define SHADINGMODELID_TWOSIDED_FOLIAGE        6
        #define SHADINGMODELID_HAIR                    7
        #define SHADINGMODELID_CLOTH                8
        #define SHADINGMODELID_EYE                    9
        #define SHADINGMODELID_SINGLELAYERWATER        10
        #define SHADINGMODELID_THIN_TRANSLUCENT        11
        #define SHADINGMODELID_NUM                    12
        #define SHADINGMODELID_MASK                    0xF        // ShadingModelID只占用了GBuffer的4bit.
        
  • ShadowDepthCommon.ush

  • ShadowProjectionCommon.ush

  • SHCommon.ush

  • (......)

第二梯队的重要或基础模块会引用第一梯队的基础模块,但也会被其它梯队或模块引用:

  • BasePassVertexCommon.ush

    • 从VS传到PS/DS的结构体
  • CapsuleLightIntegrate.ush

  • DeferredLightingCommon.ush

    • 光源数据 struct FDeferredLightData
    • 阴影射线检测ShadowRayCast
    • 光照计算接口
  • DeferredShadingCommon.ush

    • 颜色空间转换

      • RGBToYCoCg
      • YCoCgToRGB
    • 颜色,法线,GBuffer数据的编码解码

    • GBuffer数据集合:struct FGBufferData

    • 屏幕空间数据:

      //包含了GBuffer和AO.
      struct FScreenSpaceData
      {
          // GBuffer (material attributes from forward rendering pass)
          FGBufferData GBuffer;
          float AmbientOcclusion;
      };
      
  • LocalVertexFactory.ush

    • // 从绑定的顶点Buffer中获取的顶点输入数据.
      struct FVertexFactoryInput{}
      
  • MaterialTemplate.ush

  • RectLight.ush

  • RectLightIntegrate.ush

  • ShadingModels.ush

    • 着色模型以及光照计算相关的类型和辅助接口
    • Light结构体
      • struct FAreaLight区域光
      • struct FDirectLighting直接光
      • struct FShadowTerms阴影数据
    • 光照接口
      • 能量归一化EnergyNormalization
      • DualSpecularGGX
      • RefractBlend
    • ShadingModel光照
      • SimpleShading
      • DefaultLitBxDF
      • HairBxDF
      • ClearCoatBxDF
      • SubsurfaceProfileBxDF
      • ClothBxDF
      • SubsurfaceBxDF
      • (......)
      • //会根据ShadingModelID调用上面不同的接口
      • IntegrateBxDF
      • EvaluateBxDF
  • ShadingModelsMaterial.ush

    • 提供了根据材质和指定参数设置GBuffe的接口
  • VertexFactoryCommon.ush

    • 顶点变换接口
      • TransformLocalToWorld
      • TransformLocalToTranslatedWorld
      • RotateLocalToWorld
      • RotateWorldToLocal
      • UnitToOct
  • (......)

最后是第三梯队的模块,重要模块的实现,会引用第一、第二梯队的模块:

  • BasePassPixelShader.usf
  • BasePassVertexShader.usf
  • CapsuleShadowShaders.usf
  • DeferredLightPixelShaders.usf
  • DeferredLightVertexShaders.usf
  • ShadowProjectionPixelShader.usf
  • ShadowProjectionVertexShader.usf
  • (......)

FShader

继承

graph BT FGlobalShader-->FShader; FMaterialShader-->FShader;

基础概念

Shader :

  • FGlobalShader
    • 子类在内存中只有唯一实例
  • FMaterialShader
    • 由FMaterialShaderType指定的材质引用的Shader
    • 材质蓝图实例化后的shader子集

ShaderType :

  • FShaderType
    • FGlobelShaderType
      • 最简单的Shader
      • 每个ShaderType只有一个实例
    • FMaterialShaderType
      • 与材质链接
    • FMeshMaterialShaderType
      • 与材质链接
      • 使用Vertex Factory

ShaderParameter

  • 由CPU的C++层传入GPU Shader并存储于GPU寄存器或显存的数据,提供Bind()进行数据绑定。着色器参数可以绑定任何GPU类型的资源或数据 ,但不同的类只能绑定特定的着色器类型

  • FShaderParameter

    • float1/2/3/4,数组,UAV
  • FShaderResourceParameter

    • 纹理,采样器
  • FRWShaderParameter

    • 绑定了UAV或SRV资源

Vertex Factory

  • 支持网格类型( 静态网格、蒙皮骨骼、程序化网格以及地形等等)

  • 渲染线程包含了Vertex Factory对象,横跨CPU和GPU两端

  • FVertexFactory封装了可以链接到顶点着色器的顶点数据资源

编译机制

usf文件编译成对应目标平台的shader代码【Shader: 一段文本,GPU硬件上执行】

Shader Map

  • 存储编译后的Shader代码(Shader Permuation)

  • FGlobalShaderMap

  • FMaterialShaderMap

  • FMeshMaterialShaderMap

  • 可以把它理解成一个三维矩阵,长度为每个材质类型,宽度为每个渲染阶段Pass,高度为每个顶点工厂类型,矩阵的每一个方格都对应了一组着色器组合(顶点着色器,像素着色器),材质也不一定参与全部阶段,所以这个三维矩阵中是存在有很多空缺的

Uber Shader设计:Shader Permutation

编译与跨平台

1. 编译
2. Shader跨平台

UE造了个轮子HLSLCC (HLSL Cross Compiler)

HLSLCC (以GLSL为例):

  • Preprocessing,预处理阶段。通过类似C风格的预处理器运行,在编译之前,UE使用MCPP进行预处理,因此跳过了这一步。
  • Parsing,语法分析阶段。通过Mesa的_mesa_hlsl_parse接口,HLSL将被分析成抽象语法树,Lexer(语法分析)和Parser分别由flex和bison生成。
  • Compilation,编译阶段。利用 _mesa_ast_to_hir,将AST(抽象语法树)编译为Mesa IR。在此阶段,编译器执行隐式转换、函数重载解析、生成内部函数的指令等功能,也将生成 GLSL 主入口点,会将输入及输出变量的全局声明添加到IR,同时计算HLSL入口点的输入,调用HLSL入口点,并将输出写入全局输出变量。
  • Optimization,优化阶段。主要通过do_optimization_pass对IR执行多遍优化,包括直接插入函数、消除无用代码、传播常量、消除公共的子表达式等等。
  • Uniform packing,全局变量打包。将全局统一变量打包成数组并保留映射信息,以便引擎可将参数与一致变量数组的相关部分绑定。
  • Final optimization,最终优化阶段。打包统一变量之后,将对IR运行第二遍优化,以简化打包统一变量时生成的代码。
  • Generate GLSL,生成GLSL。最后步骤,将已经优化的IR转换为GLSL源代码。除了生成所有构造及统一变量缓冲区的定义以及源代码本身以外,还会在文件开头的注释中写入一个映射表。

在UE4.25,Shader的跨平台示意图如下:

img

注:

Direct3D和OpenGL虽然在标准化设备坐标一致,但在UV空间的坐标是不一致的:

img

UE为了不让shader的开发人员察觉到这一差异,采用了翻转的图片,强制使得UV坐标用统一的范式:

img

这样做的后果就是OpenGL的纹理实际上是垂直翻转的(从RenderDoc截取的UE在OpenGL平台下的应用也可佐证),不过渲染后期可以再次翻转就行了。但是,UE采用颠倒(Upside down)的渲染方式,并且将颠倒的参数集成到投影矩阵:

img

因此,看起来标准化设备坐标和D3D下的纹理都是垂直翻转的。

调试

虚幻5渲染编程专栏概述及目录 - 知乎 (zhihu.com)

step1: 开启指令,方便RenderDoc调试

Engine\Config\ConsoleVariables.ini

命令行 解析
r.ShaderDevelopmentMode=1 获得关于着色器编译的详细日志和错误重试的机会。
r.DumpShaderDebugInfo=1 将编译的所有着色器的文件保存到磁盘ProjectName/Saved/ShaderDebugInfo的目录。包含源文件、预处理后的版本、一个批处理文件(用于使用编译器等效的命令行选项来编译预处理版本)。
r.DumpShaderDebugShortNames=1 保存的Shader路径将被精简。
r.Shaders.Optimize=0 禁用着色器优化,使得shader的调试信息被保留。
r.Shaders.KeepDebugInfo=1 保留调试信息,配合RenderDoc等截帧工具时特别有用。
r.Shaders.SkipCompression=1 忽略shader压缩,可以节省调试shader的时间。

修改了某些Shader文件, 控制台输入RecompileShaders即可重新编译

Material

FMaterialRenderProxy负责接收游戏线程的数据,然后传递给渲染器去处理和渲染

FMaterial有3个功能:

  • 表示材质到材质的编译过程,并提供可扩展性钩子(CompileProperty等) 。
  • 将材质数据传递到渲染器,并使用函数访问材质属性。
  • 存储缓存的shader map,和其他来自编译的瞬态输出,这对异步着色器编译是必要的。

渲染

UE4渲染模块分析 - 简书 (jianshu.com)

类型 解析
UPrimitiveComponent 图元组件,是所有可渲染或拥有物理模拟的物体父类。是CPU层裁剪的最小粒度单位。
FPrimitiveSceneProxy 图元场景代理,是UPrimitiveComponent在渲染器的代表,镜像了UPrimitiveComponent在渲染线程的状态。
FPrimitiveSceneInfo 渲染器内部状态(描述了FRendererModule的实现),相当于融合了UPrimitiveComponent and FPrimitiveSceneProxy。只存在渲染器模块,所以引擎模块无法感知到它的存在。
FScene 是UWorld在渲染模块的代表。只有加入到FScene的物体才会被渲染器感知到。渲染线程拥有FScene的所有状态(游戏线程不可直接修改)。
FSceneView 描述了FScene内的单个视图(view),同个FScene允许有多个view,换言之,一个场景可以被多个view绘制,或者多个view同时被绘制。每一帧都会创建新的view实例。
FViewInfo view在渲染器的内部代表,只存在渲染器模块,引擎模块不可见。
FSceneRenderer 每帧都会被创建,封装帧间临时数据。下派生FDeferredShadingSceneRenderer(延迟着色场景渲染器)和FMobileSceneRenderer(移动端场景渲染器),分别代表PC和移动端的默认渲染器。
FMeshBatchElement 单个网格模型的数据,包含网格渲染中所需的部分数据,如顶点、索引、UniformBuffer及各种标识等。
FMeshBatch 存着一组FMeshBatchElement的数据,这组FMeshBatchElement的数据拥有相同的材质和顶点缓冲。
FMeshDrawCommand 完整地描述了一个Pass Draw Call的所有状态和数据,如shader绑定、顶点数据、索引数据、PSO缓存等。
FMeshPassProcessor 网格渲染Pass处理器,负责将场景中感兴趣的网格对象执行处理,将其由FMeshBatch对象转成一个或多个FMeshDrawCommand。

渲染模块由如下几个部分组成:

  • 场景的描述
  • 场景遍历和拣选
  • 渲染的执行

1. 场景的描述

UE4场景管理相关的数据结构如下:

  • FScene 场景类
  • FPrimitiveSceneProxy 场景里的几何体类
  • FPrimitiveSceneInfo 场景里的结点(拥有几何体和状态信息)

每个几何体具有材质属性,相关的数据结构如下:

  • FMaterial 材质接口类,提供材质属性的查询(eg. blend mode)和shader查找。
  • FMaterialResoruce UMaterial实现的具体的FMaterial
  • FMaterialRenderProxy 渲染线程用的Material对象,提供FMaterial的访问和材质参数的访问(eg. scaler, vector, texture parameter等参数)。

2. 场景的遍历和拣选

可见性判断

2.1 从FPrimitiveSceneProxy到FMeshBatch

基本概念:

  • UPrimitiveComponent是图元组件,是所有可渲染或拥有物理模拟的物体父类。是CPU层裁剪的最小粒度单位。

  • FPrimitiveSceneProxy是游戏线程UPrimitiveComponent在渲染线程的镜像数据

  • FMeshBatch包含了绘制Pass所需的所有信息,解耦了网格Pass和FPrimitiveSceneProxy(FMeshBatch作为中间层),所以FPrimitiveSceneProxy并不知道会被哪些Pass绘制

  • FMeshBatch存着一组FMeshBatchElement,这组FMeshBatchElement的数据拥有相同的材质和顶点缓冲

FMeshBatch记录了一组拥有相同材质和顶点工厂的FMeshBatchElement数据. 一个FMeshBatch拥有一组FMeshBatchElement、一个顶点工厂和一个材质实例,同一个FMeshBatch的所有FMeshBatchElement共享着相同的材质和顶点缓冲 (大部分情况一个FMeshBatch只有一个FMeshBatchElement)

FMeshElementCollector和FSceneRenderer是一一对应关系,每个FSceneRenderer拥有一个收集器。

2.2 从FMeshBatch到FMeshDrawCommand

【MeshPassProcessor.h】

  • 收集完动态的MeshElement,紧接着会调用SetupMeshPass来创建FMeshPassProcessorFMeshPassProcessor充当了将FMeshBatch转换成FMeshDrawCommands的角色。

  • FMeshBatch转换成FMeshDrawCommand后,每个Pass都对应了一个FMeshPassProcessor,每个FMeshPassProcessor保存了该Pass需要绘制的所有FMeshDrawCommand,以便渲染器在合适的时间触发并渲染。

FMeshPassProcessor的主要作用是:

  • Pass过滤。将该Pass无关的MeshBatch给过滤掉,比如深度Pass过滤掉透明物体。( 不同的MeshPass处理FMeshBatch会有所不同 )
  • 选择绘制命令所需的Shader及渲染状态(深度、模板、混合状态、光栅化状态等)。
  • 收集绘制命令涉及的Shader资源绑定。
    • Pass的Uniform Buffer,如ViewUniformBuffer、DepthPassUniformBuffer。
    • 顶点工厂绑定(顶点数据和索引)。
    • 材质绑定。
    • Pass的与绘制指令相关的绑定。
  • 收集Draw Call相关的参数。

img

最终,FMeshDrawCommand接入RHI层

2.3 静态与动态绘制路径

img

UE存在3种网格绘制路径(橙色为每帧动态生成,蓝色为只生成一次后缓存)

  • 第1种是动态绘制路径,从FPrimitiveSceneProxy到RHICommandList每帧都会动态创建,效率最低,但可控性最强
  • 第2种是需要View的静态路径,可以缓存FMeshBatch数据,效率中,可控性中
  • 第3种是不需要view的静态绘制路径,可以缓存FMeshBatch和FMeshDrawCommand,效率最高,但可控性差,需满足的条件多。
(1) 动态

通过GetDynamicMeshElements接口来收集FMeshBatch

3. 渲染的执行

1)多Pass绘制

Pass_0: PrePass/Depth Only Pass

Pass_1: BassPass

Pass_2: Issue Occlusion Queries / BeginOcclusionTests

  • 深度测试

Pass_3: ShadowMap

  • 每个光源渲染相应的Shadowmap

Pass_4: Lighting

  • 预处理组合型光照(SSAO)

  • 光照计算

Pass_5: Draw atmosphere

  • 对非透明表面绘制大气

Pass_6: Draw Fog

  • 对非透明表面逐像素计算Fog

Pass_7: Draw translucency

Pass_8: Post Processing

延迟渲染

简介

  • 1988年提出
  • 基于屏幕空间
1. Overview:

两个Pass

  • Geometry Pass (UE称为Base Pass):生成 GBuffer
    • 所有的不透明物体(Opaque)和Masked物体 使用Unlit材质进行渲染
    • 将几何信息写入RT中
      • Depth
      • Normal
      • 材质参数(Diffuse Color, Emissive Color, Roughness, , Shading Model, AO, ......)
  • Lighting Pass:使用 GBuffer

Coding in Unreal Engine:

void RenderBasePass()
{
    SetupGBuffer(); // 设置几何数据缓冲区。
    
    // 遍历场景的非半透明物体
    for each(Object in OpaqueAndMaskedObjectsInScene)
    {
        SetUnlitMaterial(Object);    // 设置无光照的材质
        DrawObjectToGBuffer(Object); // 渲染Object的几何信息到GBuffer,通常在GPU光栅化中完成。
    }
}

void RenderLightingPass()
{
    BindGBuffer(); // 绑定几何数据缓冲区。
    SetupRenderTarget(); // 设置渲染纹理。
    
    // 遍历RT所有的像素
    for each(pixel in RenderTargetPixels)
    {
        // 获取GBuffer数据。
        pixelData = GetPixelDataFromGBuffer(pixel.uv);
        // 清空累计颜色
        color = 0;    
        // 遍历所有灯光,将每个灯光的光照计算结果累加到颜色中。
        for each(light in Lights)
        {
            color += CalculateLightColor(light, pixelData);
        }
        // 写入颜色到RT。
        WriteColorToRenderTarget(color);
    }
}
2. 优劣:

  • 复杂度 \(O(N_{light}\times W_{RT}\times H_{RT})\) ,跟场景的物体数量解耦,只跟Render Targe尺寸相关
  • 避免了很多本被遮挡的物体光照计算

  • 多一个通道来绘制几何信息,需要多渲染纹理(MRT)的支持,更多的CPU和GPU显存占用,更高的带宽要求
  • 有限的材质呈现类型
  • 难以使用MSAA等硬件抗锯齿,存在画面较糊的情况等等
  • 应对简单场景时,可能反而得不到渲染性能方面的提升

Unreal Engine Implementation

graph BT FMobileSceneRenderer-->FSceneRenderer FDeferredShadingSceneRenderer-->FSceneRenderer
  • FMobileSceneRenderer是用于移动平台的场景渲染器,默认采用了前向渲染的流程。

  • FDeferredShadingSceneRenderer虽然名字叫做延迟着色场景渲染器,但其实集成了包含前向渲染和延迟渲染的两种渲染路径,是PC和主机平台的默认场景渲染器。

1. FSceneRenderer创建

FSceneRenderer

  • 处理和渲染场景,生成RHI层的渲染指令

  • 由游戏线程的 FRendererModule::BeginRenderingViewFamily负责创建和初始化,然后传递给渲染线程。

  • 渲染线程会调用FSceneRenderer::Render(),渲染完返回后,会删除FSceneRenderer的实例。也就是说,FSceneRenderer会被每帧创建和销毁。

class UGameEngine : public UEngine

UGameEngine::Tick()

  • called UGameEngine::RedrawViewports()
    • called UGameViewportClient::Draw()
      • called FRendererModule::BeginRenderingViewFamily()
FRendererModule::BeginRenderingViewFamily(..., FSceneViewFamily* ViewFamily)
{
FScene* const Scene = ViewFamily->Scene->GetRenderScene();
FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, ...);
}

创建FSceneRenderer之后发送指令给渲染线程

  • 调用FSceneRenderer->Render(RHICmdList);
  • 等待SceneRenderer的任务完成然后清理并删除实例
2. FDeferredShadingSceneRenderer

In deferred shader, the SSAO uses the GBuffer and must be executed after base pass. Otherwise, async compute runs the shader in RenderHzb()

3. PrePass

浅谈HiZ-buffer - DeepDream

PrePass又被称为提前深度Pass、Depth Only Pass、Early-Z Pass,用来渲染不透明物体的深度。

此Pass只会写入深度而不会写入颜色,写入深度时有disabled、occlusion only、complete depths三种模式,视不同的平台和Feature Level决定。

通常用来建立Hierarchical-Z,以便能够开启硬件的Early-Z技术,还可被用于遮挡剔除,提升Base Pass的渲染效率

绘制深度时,由于不需要写入颜色,那么渲染物体时使用的材质肯定不应该是物体本身的材质,而是某种简单的材质 UMaterial::GetDefaultMaterial(MD_Surface)( 在engine.ini配置文件中指定 )

ResolvedPath = L"/Engine/EngineMaterials/WorldGridMaterial.WorldGridMaterial"

非常值得一提的是:WorldGridMaterial使用的Shading Model是Default Lit,材质中也存在冗余的节点。如果想要做极致的优化,建议在配置文件中更改此材质,删除冗余的材质节点,改成Unlit模式更佳,以最大化地缩减shader指令,提升渲染效率。

4. BasePass
  • 用来渲染不透明物体的几何信息,包含法线、深度、颜色、AO、粗糙度、金属度等等,这些几何信息会写入若干张GBuffer中
  • Base Pass正常情况下使用的是FMeshBatch收集到的材质,也就是网格自身的材质,唯一不同的是shader中不会启用光照计算,类似于Unlit模式。
  • 遍历所有view, 每个view渲染一次Base Pass.

OverView : O(n^3)

foreach(dscene in scenes)
{
    foreach(view in views)
    {
        foreach(mesh in meshes)
        {
            DrawMesh(...) // 每次调用渲染就执行一次BasePassVertexShader和BasePassPixelShader的代码.
        }
    }
}

RenderBasePass

  • RenderBasePassInternal
    • SetupBasePassState: 初始化BassPass所用的渲染状态(渲染设置)

执行Shader

BasePass绘制时使用的VS和PS分别是TBasePassVSTBasePassPS (BasePassRendering.h)

template <typename LightMapPolicyType>
void GetBasePassShaders(const FMaterial& Material, ...)
{
    (......)

    VertexShader = Material.GetShader<TBasePassVS<LightMapPolicyType, false> >(VertexFactoryType);
    PixelShader = Material.GetShader<TBasePassPS<LightMapPolicyType, false> >(VertexFactoryType);
    
    (......)
}
  • TBasePassVSTBasePassPS 提供了获取Shader绑定、更改编译环境和只编译指定的排列组合shader等接口,此外还拥有反射球、光照图类型等属性

执行Shader文件:BasePassVertexShader.usf ,BasePassPixelShader.usf

BasePassPixelShader的主要步骤:

  • 初始化ResolvedView、GBuffer等数据。
  • GetMaterialPixelParameters:获取材质的参数,从材质编辑器编辑的材质节点和插值数据而来。详细数据由FMaterialPixelParameters定义
// Engine\Shaders\Private\MaterialTemplate.ush
struct FMaterialPixelParameters{}
  • CalcMaterialParametersEx:根据FMaterialPixelParameters计算额外的材质参数。
  • 根据FMaterialPixelParameters的数据和从顶点插值而来的数据计算GBuffer数据。
  • 计算次表面散射、贴花、体积光照图等数据。
  • SetGBufferForShadingModel:设置前面步骤收集到的参数到GBuffer。
  • 计算或处理速度缓冲、高光颜色、漫反射颜色、环境法线、AO,叠加次表面散射颜色到漫反射中。
  • 处理预计算的非直接光照和天空光、非直接AO,计算最终的漫反射项。
  • 处理前向渲染光照或平行光,处理透明物体光照。
  • 计算雾效果,包含高度雾、指数雾、体积雾、逐像素雾、云体雾,以及天空大气效果。
  • 处理自发光。
  • EncodeGBuffer:将GBuffer基础数据编码到MRT中
  • 将GBuffer的非基础数据写入到后面几个RT,如速度缓冲、逐物体阴影等。
  • 处理预曝光。

即:

5. LightingPass

负责间接阴影、AO、透明体积光照、光源计算、LPV、天空光、SSS等

FDeferredShadingSceneRenderer::RenderLights

graph TB FinalColor-->LightAccumnulator; FinalColor-->View.PreExposure; LightAccumnulator-->Color; Color-->Fogging.a; Color-->+; +-->Fogging.rgb; +-->DiffuseColor; +-->Emissive; DiffuseColor-->AOMultiBounce; AOMultiBounce-->GBuffer.BaseColor; AOMultiBounce-->DiffOcclusion; DiffuseColor-->\(+); \(+)-->*; *-->DiffuseIndirectLighting; *-->GBuffer.DiffuseColor; \(+)-->**; **-->SubsurfaceIndirectLighting; **-->SubsurfaceColor;

RHI

RDG

RDG的理念不在GPU上立即执行Pass,而是先收集所有需要渲染的Pass,然后按照依赖的顺序对图表进行编译和执行,期间会执行各类裁剪和优化。(Pass的管理者)

UE5

  • 增加实例化裁剪模块:InstanceCulling、InstanceCullingManager等,包含FInstanceCullingRdgParams、EInstanceCullingMode、FInstanceCullingContext、FInstanceCullingManager等类型,主要用于Nanite技术。
  • 虚拟纹理增加或完善了数据读写和FVirtualTextureFeedbackBuffer、RenderPages、RenderPagesStandAlone等接口。
  • 全局距离场数据(FGlobalDistanceFieldParameterData)增加了Mipmap和VT数据和接口。
  • HairStrand增加EHairBindingType、EHairInterpolationType、FHairStrandsInstance等类型。
  • 增加或完善FVertexFactoryShaderPermutationParameters的类型。
  • MeshPassProcessor:
    • EMeshPass增加VSMShadowDepth、LumenCardCapture、EditorLevelInstance等专用通道。
    • 增加EFVisibleMeshDrawCommandFlags、FCompareFMeshDrawCommands、FMeshPassProcessorRenderState等类型。
    • 增加FSimpleMeshDrawCommandPass类型,用于处理不需要并行处理绘制命令的地方,减少开销。
    • FVisibleMeshDrawCommand增加EFVisibleMeshDrawCommandFlags以及用于GPUCull的FMeshDrawCommandSortKey、InRunArray等。
  • FDynamicPassMeshDrawListContext的FinalizeCommand阶段增加了NewVisibleMeshDrawCommand.Setup阶段。
  • 摒弃SetInstancedViewUniformBuffer、SetPassUniformBuffer、GetInstancedViewUniformBuffer等UniformBuffer接口。
  • 新增Nanite模块:
    • 新增NaniteRender:FNaniteCommandInfo、ENaniteMeshPass、FNaniteDrawListContext、FCullingContext、FRasterContext、FRasterResults、FNaniteShader、FNaniteMaterialVS、FNaniteMeshProcessor、FNaniteMaterialTables、ERasterTechnique、ERasterScheduling、EOutputBufferMode、FPackedView等等类型及处理接口。
    • FPrimitiveFlagsCompact增加bIsNaniteMesh标记。
    • FPrimitiveSceneInfo增加NaniteCommandInfos、NaniteMaterialIds、LumenPrimitiveIndex以及CachedRayTracingMeshCommandsHashPerLOD、bRegisteredWithVelocityData、InstanceDataOffset、NumInstanceDataEntries实例化和光追相关的等数据和处理接口。
  • 新增Lumen模块:
    • 涉及的模块非常多,总结起来有DiffuseIndirect、Scene、HardwareRayTracing、Mesh、Probe、Radiance、Radiosity、TranslucencyVolume、Voxel以及数据结构、工具箱等。
  • 替换传统Shader绑定接口到RDG,如SHADER_PARAMETER_TEXTURE改成SHADER_PARAMETER_RDG_TEXTURE。
  • 增加或完善RuntimeVirtualTextureProducer、FSceneTextureExtracts、EMobileSceneTextureSetupMode、Strata地层。
  • 增强后处理效果,如增加了Temporal Super Resolution(TSR)。
  • 增强了光照追踪模块,如RTGI、RTAO、RTR、RTShadow、RTSkyLight等。
  • DeferredShadingRenderer:
    • 增加Lumen、IndirectLightRender、SSRT、TranslucencyLightingVolume等相关的模块、类型和步骤。
    • 增加FLumenCardRenderer、TPipelineState、EDiffuseIndirectMethod、EAmbientOcclusionMethod、EReflectionsMethod、FPerViewPipelineState、FFamilyPipelineState等类型。
  • 增加或增强了BasePass、DepthPass、SDF、GPUScene、GlobalDistanceField、BVH、GenerateConservativeDepthBuffer、LightRendering、InrectLightRendering、MeshDrawCommands、Shader、ScreenSpace、Shadow、MobileRender等等渲染模块。

从UE4.26到5.0EA,重要和基础的渲染模块都做了大大小小的修改。

Appendix

Compute Shader

Compute Shader : Optimize your game using compute - 知乎

为了执行通用计算,NV推出了CUDA,Khronos推出了OpenCL,Microsoft推出了DirectCompute,也就是后来的Compute Shader,然后,各种图形API也相继推出了CS

​ DX虽然从10开始支持Compute Shader/Direct Compute,但是限制比较大。DX11的Compute Shader拥有更强大的功能(当然肯定还有DX12)[6]。所以我们一般在Unity中使用CS,还是要求shader target4.5(也就是shader model 5)

posted @ 2022-04-23 16:16  Heskey0  阅读(1737)  评论(0编辑  收藏  举报

载入天数...载入时分秒...