虚幻引擎的实时渲染概述(上)

发布时间 2023-07-12 04:04:37作者: Tcohneyn

虚幻引擎的实时渲染概述(上)

1.介绍

实时渲染(Real-Time Rendering,RTR)是指在计算机上快速生成图像的一个过程。它是计算机图形学中交互性最高的领域。

图像出现在屏幕上,观众做出反应,这种反馈会影响接下来生成的内容。这种反应和渲染的循环以足够快的速度发生,以至于观众看不到单帧图像,而是沉浸在动态过程中。

实时渲染在不渲染任何物体的时候能达到其最高性能

RTR流程的本质是管理性能损耗和画面质量的平衡

img

实时渲染流程

  • 确定目标帧率
  • 专注于方案和工作流达到的目标帧率
  • 确保获得尽可能多的回报

注意事项

  • 你无法让RTR变得完美
  • 你可以做的是找到三者之间完美的平衡

img

实现真实感非常复杂

  • 所有环节要保证尽可能的高效
  • 需要严格的流程标准和限制
  • 将一部分工作分配到预先计算环节
  • 需要多种方案混合工作

CPU vs GPU

  • CPU和GPU负责处理渲染的不同部分多数时候是同步的
  • 有可能成为对方的瓶颈
  • 需要知道工作负荷如何在两者间分配,才能采取对应措施

2.渲染前准备

img

上图CPU(游戏线程);DRAW(渲染线程);GPU线程,三个线程并行工作CPU先处理处理完后依次向下进行处理,而CPU处理新的帧。

第0帧-0毫秒-游戏进程(CPU)

在开始渲染前,我们需要知道参与渲染的物体在什么位置

游戏线程执行所有逻辑计算和物体的变换

  • 动画
  • 模型和物体的位置
  • 物理
  • AI
  • 生成或销毁,隐藏或显示

以及其它任何与物体位置变化相关的逻辑

第1帧-33毫秒-渲染进程(大部分在CPU)

至此,我们已经知道了各个物体处在什么位置

我们还需要知道哪些物体应该被渲染在画面中——>只渲染可见的内容

这部分的工作大部分也是在CPU上计算的,但也有一部分是在GPU上计算

遮挡过程——建立一个可见物体的列表(逐物体,而不是三角形)

分为以下4个步骤处理

  1. 距离剔除——根据物体与相机的距离决定是否剔除(默认为开启)
  2. 视锥剔除——根据物体是否在视锥内决定是否剔除
  3. 预计算可见性——将可见性结果提前计算好并储存下来
  4. 遮挡剔除——性能消耗最大,在最后才执行

遮挡过程性能提示

  • 设置距离剔除
  • 大于1万个物体会对性能有较大影响
  • 大部分情况下CPU是瓶颈,有时候也可能是GPU
  • 遮挡在大型开放世界通常作用比较小
  • 所有的物体都会被遮挡
  • 大的物体很难被遮挡,因此在GPU渲染上可能需要消耗更多的时间

3.几何体渲染

第2帧-66毫秒-GPU

Prepass/Early Z Pass

GPU现在已经知道了需要渲染的物体的列表及其位置信息,但是如果直接渲染,会有一些像素重复绘制,造成非常大的浪费

因此我们需要找出哪些模型应该先被渲染

  • Early Z

GPU驱动会在执行像素着色器前会检查该点深度,提前跳过不符合条件的像素

Drawcall

Drawcall是指渲染时在特定pass中采用的单一处理过程

通常可以理解为绘制拥有相同属性的一组多边形

Drawcall的含义就是CPU调用图像编程接口,以命令GPU进行渲染的操作。

img

不考虑auto instance,左边Drawcall为5 *2=10,右边Drawcall为6 *2=12

切换材质影响性能开销

在GPU渲染时,引擎会根据材质对物体进行排序,相同材质的会在一个批次里绘制

  • Drawcall对性能有非常大的影响
  • 每次GPU绘制完成后,需要从渲染线程拿新的指令,这里会有比较大性能的开销
  • Drawcall数量对性能影响比三角形数量要大许多

通过start RHI命令查看运行统计数据

  • 2000-3000:合理
  • 大于等于5000:略高
  • 大于等于10000:也许会有性能问题
  • 移动端在几百左右比较合适

Drawcall相关的性能提示

  • Drawcall数量相比多边形数量对性能的影响更大

  • 引擎有一些Drawcall的基础开销

  • 为了降低Drawcall,我们可以将模型进行合并,但也有副作用

    • 遮挡检测性能更差
    • 计算碰撞性能更差
    • 占用更多内存
  • 一个较为常用的技术称为Modular Meshes

  • 也可以使用Instancing(只针对相同的模型,包括有不同的transform信息才会进行合并,如果是不同的模型但是是同一个材质还是要花费多个Drawcall)降低Drawcall

  • Level Of Detail(LOD)和HLOD,LOD可以降低模型的面数,HLOD可以动态的将几个物体的mesh合并成一个mesh,HLOD需要在离线的时候预先计算好

    Shader

  • Shader是一小段在GPU上执行计算的代码

  • 有许多不同类型的Shader:Vertex,Pixel,Compute,Other

  • Shader运行非常高效

    • 流水线执行

Vertex Shader

Vertex Shader是渲染过程的第一步,主要处理做三件事情

  1. 将坐标从局部空间->世界空间->投影空间进行转换
  2. 处理顶点着色
  3. 应用世界坐标偏移(WPO)
  • Shader系统经过了高度优化-因此它是整个渲染过程的核心
  • CPU无法处理如此海量的数据
  • Vertex Shader并不直接修改模型,只是视觉上的效果
  • CPU无法察觉到VS对模型顶点数据的修改
    • 因此物理和碰撞不会受任何影响

顶点着色器性能提示

  • 动画越复杂性能越慢
  • 顶点越多性能越慢
  • 高精度的模型最好使用简单的Vertex shaders
  • 对远距离的物体禁用顶点动画

Nanite

Nanite是虚幻引擎5新推出的虚拟几何体系统,采用新的内部网格体格式和渲染技术来渲染像素级别的细节以及海量物体对象,可以仅处理能观察到的细节,采用高度压缩的数据格式,并且支持高细节流送和自动LOD

img

一般来说,能启用时应该尽量启用Nanite,虚幻引擎5中NaniteMesh渲染经过了高度优化,通常可以更快渲染,占用的内存和磁盘空间也更少具体而言,网格体如果满足以下条件,将很适合使用Nanite:

  • 包含很多三角形,或屏幕上三角形非常小
  • 场景中有很多实例
  • 作为其他Nanite几何体的主要遮挡物

Nanite目前还不支持:

  • 骨骼动画(Skeletal animation)
  • 变形目标 (Morph Targets)
  • 自定义深度或模板
  • 半透明材质
  • 针对完整Nanite细节的光线追踪
    • 支持光线追踪功能,但光线会与代理网格体(Nantie网格体的简化表现)交互,而不是具体的Nanite网格体

光栅化和G-Buffer

提供了变换数据、投影顶点以及必要的着色数据后,下一步是找到所有在这些三角形内部需要渲染的像素点,这个过程我们称为光栅化

光栅化按照Drawcall的顺序逐次调用

img

在远处观察一个有10万面的模型,即使最终只占了画面中的一个像素,GPU仍然会处理这个10万面的数据

由于硬件设计的原因,在计算一个像素的时候,同时还需要计算其周边2×2的像素

img

光栅化和Overshading性能提示

  • 多边形越密集,渲染的开销越大
  • 从更远处观察物体,多边形的密度会增加
  • 因此可以使用LOD的方法或远距离剔除减少这部分的开销
  • PixelShader越复杂,OverShading的开销也越大
  • 非常细长的三角形也会造成比较严重的OverShading

G-buffer

我们将物体表面信息储存到一系列的图像中,这些图像称为G-Buffer

G-Buffer可以用于合成各种信息,最终生成渲染输出

虚幻引擎会生成6中G-Buffer

可以使用Custom Depth作为Mask输出

也可以使用MovieRenderQueue可以输出其它G-Bufffer,或Object ID用于合成

G-buffer 性能提示

  • G-Buffer会占用很多内存和带宽,因此对G-Buffer的数量是有限制的,如果需要扩展G-Buffer需要考虑到这一点

纹理

  • 纹理在导入时会被压缩,可以大大减少GPU显存占用
  • 不同的平台有不同的压缩方式,Windows上通常使用BC算法
  • 法线贴图使用了特殊的压缩方式,只保留了RG通道
  • Shader中对采样纹理的数量有限制
  • 纹理的分辨率主要影响的是内存和带宽,而不是着色

为了减少锯齿以及加快渲染的速度,我们需要使用Mipmap

1+1/4+1/16+1/32+...=4/3≈1.33,Mipmap会使图像占用大小增加为原来的1.33倍

img

img

Midmap注意事项

  • 纹理的尺寸需要为2的幂次方,例如:2×2;4×4
  • 长方形贴图长和宽都是是2的幂次方也是可以的
  • 边长不是2的幂次方的贴图无法生成Mipmap和进行纹理流送
  • UI贴图可以不需要Mipmap

像素着色器和材质

  • 像素着色器 (Pixelshader) 与顶点着色器(Vertex shader) 类似
  • 在GPU上执行的程序,可以同时执行大量的简单计算,用于修改像素的颜色
  • 对于渲染管线极为重要
  • 像素着色器是材质系统的底层实现
  • 同时也实现了光照,雾,后期等任何与效果相关的处理

像素着色器是以Shader Language书写的,在Windows平台上我们使用HLSL,不同平台使用不同的Shader Language,引擎会自动进行转换

  • 材质管线很大一部分都基于PBR也就是基于物理的渲染(Physic Based Rendering)
  • PBR使用Specular/Metallic/Roughness来描述一种材质的属性
  • PBR是一种统一着色模型,可以通过修改参数变现很多不同的材质
  • UE中几乎所有的模型都是使用PBR模型进行渲染的
      1. 可以达到最快的效率
      2. 可以预测性
      3. 基于G-Buffer的合成工作流限制

材质的性能提示

  • 每个材质的纹理采样器有最大数量限制 (16个),DX11可以使用共享采样器(最多可以使用128张纹理)
  • 纹理尺寸过大会导致短暂的卡顿,但不会降低帧率
  • 像素着色器会对性能有非常大的影响,可以优化材质编辑器中的指令数(100~200)
  • 屏幕输出的分辨率越大,复杂材质对性能的影响也越大

学习地址

地址