其他分享
首页 > 其他分享> > 【深入解析UE】渲染篇

【深入解析UE】渲染篇

作者:互联网

UE基础

架构

内存

GC

多线程渲染

UGameEngine::Tick()

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

UE的线程(FRunnableThread)

线程同步

渲染机制

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

Draw Call 的产生

UE5

Nanite

渲染特性:

Component:

Preview

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

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

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

Algorithm Overview:

Mipmap适配:

裁剪图(Clipmap):

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

PCF过于模糊

Algorithm Overview:

3. TSR (Temporal Super Resolution):替换传统TAA

Core

Cluster :

Lumen

Surface Cache

屏幕追踪

光线追踪

补充(非Lumen):DFAO

Shader

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

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

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

FShader

继承

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

基础概念

Shader :

ShaderType :

ShaderParameter

Vertex Factory

编译机制

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

Shader Map

Uber Shader设计:Shader Permutation

编译与跨平台

1. 编译
2. Shader跨平台

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

HLSLCC (以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个功能:

渲染

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场景管理相关的数据结构如下:

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

2. 场景的遍历和拣选

可见性判断

2.1 从FPrimitiveSceneProxy到FMeshBatch

基本概念:

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

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

2.2 从FMeshBatch到FMeshDrawCommand

【MeshPassProcessor.h】

FMeshPassProcessor的主要作用是:

img

最终,FMeshDrawCommand接入RHI层

2.3 静态与动态绘制路径

img

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

(1) 动态

通过GetDynamicMeshElements接口来收集FMeshBatch

3. 渲染的执行

1)多Pass绘制

Pass_0: PrePass/Depth Only Pass

Pass_1: BassPass

Pass_2: Issue Occlusion Queries / BeginOcclusionTests

Pass_3: ShadowMap

Pass_4: Lighting

Pass_5: Draw atmosphere

Pass_6: Draw Fog

Pass_7: Draw translucency

Pass_8: Post Processing

延迟渲染

简介

1. Overview:

两个Pass

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. 优劣:

Unreal Engine Implementation

graph BT FMobileSceneRenderer-->FSceneRenderer FDeferredShadingSceneRenderer-->FSceneRenderer
1. FSceneRenderer创建

FSceneRenderer

class UGameEngine : public UEngine

UGameEngine::Tick()

FRendererModule::BeginRenderingViewFamily(..., FSceneViewFamily* ViewFamily)
{
FScene* const Scene = ViewFamily->Scene->GetRenderScene();
FSceneRenderer* SceneRenderer = FSceneRenderer::CreateSceneRenderer(ViewFamily, ...);
}

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

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

OverView : O(n^3)

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

RenderBasePass

执行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);
    
    (......)
}

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

BasePassPixelShader的主要步骤:

// Engine\Shaders\Private\MaterialTemplate.ush
struct FMaterialPixelParameters{}

即:

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

从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)

标签:渲染,Shader,ush,线程,Pass,UE,材质,解析
来源: https://www.cnblogs.com/Heskey0/p/16182731.html