剔除
- 距离剔除 Distance Culling
默认关闭,可以根据需要在Actor - Detail - Render下设置开启,或是添加Cull Distance Volume,可以根据距离剔除体积内所有Actor - 视锥剔除 Frustum Culling
默认开启,只渲染摄像机视线范围内的物体 - 预计算可视性 Precomputed visibility
默认关闭,可在世界场景设置 - 预计算可视性开启。把场景划分为网状单元格,每个单元格记录一个列表,列表上是可视性信息,当镜头移动至某一单元格时只渲染单元格上记录的可见物体 - 遮挡剔除 Occlusion Culling
准确的检查每个模型的可视性。可在控制台输入freezerendering来观察剔除后的模型。Stat initviews显示性能消耗。10000+个物体需要检查时开始对性能产生影响。
早期Z通道 Prepass / Early Z Pass
因为物体之间互相遮挡,如果把所有的物体所有部分全部渲染,将会有大量根本开不见的像素被渲染,造成性能浪费。按照距离先渲染离屏幕近再渲染离屏幕远的物体也行不通,因为有很多如下图你中有我的情况
所以预先渲染几何体,再对物体进行遮罩处理,这样在最终正式渲染时,只渲染镜头能看到的部分
几何体渲染 Geometry Rendering
GPU开始一个绘制调用接一个绘制调用的渲染。
控制台输入
stat rhi // 渲染头接口 rendering head interface
在下面Counters - DrawPrimitiveCalls可以看到绘制调用数,小于3000次调用是比较合理的,大于5000就略高了,超过10000次就估计有问题了,手游或VR游戏要低得多,最多几百次。
绘制调用数对性能的影响要比多边形数对性能的影响高得多。
降低绘制调用
使用少量较大的模型来替换大量小模型。但是这样做有副作用:会对遮挡,光照贴图,内存,碰撞计算产生负面影响。
顶点着色器
- 将本地坐标中的顶点位置转化为世界坐标中的顶点位置
- 处理顶点着色
- 应用额外偏移
可以通过材质中的世界坐标偏移引脚使物体视觉上产生偏移,但实际上并没有偏移,因为CPU不知道进行了偏移,所以物理碰撞等还是会按照偏移前计算。多用来偏移布料,水波,植被的动画。 - 优化:在远距离禁用世界坐标偏移
光栅化 Rasterization
把顶点数据转化为像素的过程
过度着色 Overshading
当渲染这些像素时,UE使用像素着色器(Pixel Shader)来精准计算着色。由于硬件设计,此计算过程采用2x2像素阵列进行。如果一个多边形十分小,就算实际只需要1个像素显示颜色,也会对4个像素着色。
如上图虽然只有3个像素需要显示颜色,但是会对所处阵列中12个像素都进行着色。
此时如果右边蓝色多边形被渲染时,则需要对红框内8像素重新着色,就造成了过度着色。过度着色对性能的影响不算大,一般可以忽略,对性能要求高的情景可以考虑优化
过度着色优化
- 打开视图模式 - 优化视图模式 - 四边形过度绘制,越蓝越ok
- 多边形密度越大,渲染越耗性能
- 离镜头越远,密度越大,所以可以考虑lodding / culling
- 对前向渲染的影响比延迟渲染更大
RenderDoc
下载RenderDoc软件,按教程设置好后,在视图的右上角会多一个图标,点击后可以跳转至RenderDoc查看当前帧渲染的详情,可以清楚的看到为了渲染这一帧画面的各个步骤,也可以清楚的看到所谓GBuffer到底是什么(如图所示),功能十分强大,不过我的破电脑开了这个之后就卡的不行了
- GBufferA
世界法线贴图,RGB三个通道分别记录XYZ三个轴向的模型的指向 - GBufferB
R通道 金属对象遮罩图层;G通道 高光图层;B通道 粗糙度图层。三个图层一起展示出场景中物体的光泽度。 - GBufferC
不带任何光照效果的图像 - GBufferD E
毛发渲染,次表面散射等 -
SceneDepthZ
图像深度信息,物体离镜头的距离
GBuffers
在UE中视图显示 - 缓冲显示中也可以看到类似的渲染图
管线状态
- 输入汇编 Input Assembler
管线从几何体中获取信息的环节 - 顶点着色器 Vertex Shader
修改顶点位置,顶点颜色等 - 凸包着色器 Hull Shader
用于置换贴图 - 域着色器 Domain Shader
- 几何着色器 Geometry Shader
非必要,可能会用不到 - 光栅化 Rasterizer
- 像素着色器 Pixel Shader
- 输出合并
自定义深度 Custom Depth
可以让物体与场景分离,达到绿幕效果,便于抠图,或是添加各种特效,如轮廓特效。
选中物体后 - Detail - Rendering - Render Custom Depth Pass,在高分辨率截图处可以开启自定义深度遮罩预览效果。
纹理 Textures
虽然纹理的大小不会对性能产生较大的影响,但是会影响内存占用与带宽,玩游戏时遇到的突然完全静止卡顿多是出于带宽不足。在材质被导入时,UE会自动将其压缩,并生成mipmaps
多级渐进纹理 Mipmaps
mipmap的每张图都是前一张图的1/4大小,这就是为什么纹理的宽高要求是2的指数幂
如上图所示,如果没有mipmaps,远处的纹理噪点会非常严重
纹理流送 Texture Streaming
纹理流送是指引擎判断所需纹理与mipmap的过程,只在需要时加载某个纹理。要正确的纹理流送也需要纹理的宽高是2的指数幂。但是也存在不需要生成mipmap的情况,比如游戏的UI纹理,因为一直在屏幕最前方,不会远距离观察,所以可以任意尺寸。
像素着色器 Pixel Shaders
与顶点着色器类似,可以理解为GPU运行的一个个函数来修改像素的颜色。
贯穿于渲染的整个过程。
像素着色器逐像素运行,也可以有选择性的逐像素运行, 比如添加遮罩,对符合规则的像素着色
像素着色器由着色器语言编写,不同平台语言不同
DirectX上用HLSL - High Level Shader Language
UE中双击打开材质 - 窗口 - 着色器代码 - HLSL代码可以看到材质的代码详情
着色器工作流程
- 在Engine\Shaders目录下有巨量的Shader模板
- UE材质编辑器中可以通过node连接使用这些模板(这也就解释了为什么有些node的某些选项是灰色的,因为套用了不同的模板,如有需要,可以自己写新的模板\Shader来使用)
- 材质编辑器对模板重组整理写出新的shader(会生成不止1个新shader,因为多个shader,每个实现简单功能要比1个shader实现复杂功能高效)
PBR 基于物理的渲染 Physical Base Rendering
- 用高光,金属色,粗糙度作为输入来计算环境中几乎所有的着色
- 统一着色,几乎所有的材质都建立在相同的shader上,以获得最大效率
着色模型 Shading Model
着色模型决定了材质的类型,也就是着色所需要的shader,如图,绿色的部分采用了PBR材质,所以用PBR相关Shader进行着色,其他颜色的用其他对应的shader进行着色
性能影响
- 一个材质或着色器可以查询的纹理采样器有数量限制,通常是16个,其中13个可以被使用。在DirectX 11或更新版可以通过共享采样器实现使用最多128个采样器。
- 纹理大小通常造成延迟和卡顿,而不是掉帧。
- 有时会遇到纹理模糊的情况,通常是由于缺少足够的带宽或内存来快速传输完整分辨率的纹理,而是使用了低分辨率的mipmap,可以通过减少带宽使用量或优化纹理池大小来解决。
- 分辨率越高,复杂材质带来的性能影响越大。对于复杂的材质,在屏幕上显示的越多(离得越近),对性能影响越大,因为会有更多的像素参与到复杂的着色计算中。
纹理池 Texture Pool
内存中分配给纹理的空间,分配过后该部分内存被直接占用。控制台输入
r.Streaming.Poolsize
可以改变纹理池大小
反射
反射优先级 SSR > Planar > Reflection Capture
反射捕获
- 捕获一张立方体贴图,然后混合到附近需要反射的模型上
- 预计算
- 性能耗费低
- 不准确
- 纯静态
- 只影响捕获球附近范围内的物体
平面反射 Planar Reflections
- 反射只发生在反射平面上
- 性能耗费高
- 适合如镜子类需要准确反射的平面
- 可以实时反射
屏幕空间反射 SSR Screen Space Reflections
- 默认的反射系统
- 实时且反射屏幕上所有东西
- 准确
- 性能耗费中等
- 只能反射屏幕上可见的物体
性能影响
- 如果项目没有烘培,反射捕获在关卡加载时捕获场景,如果有过多的反射捕获可能会加载更慢,烘焙项目可以解决此问题
- 当很多反射捕获相互叠加时,会增加运算难度
- 反射捕获的清晰度可以通过设置其捕获的立方体贴图来修改,具体来说,项目设置 - 引擎渲染 - 反射 - 反射捕获分辨率
- 天光Skylight会为整个世界场景提供一个低消耗备用反射捕获
- 非必要不用平面反射
- 如果硬件性能受限,关闭SSR
- 如果硬件性能可以,可提高SSR质量
r.SSR.Quality 4 (默认3,改为4可些许提升质量)
光照与阴影
- 主要有两种方式处理光照/阴影,静态与动态,换句话说预渲染/实时。
- 光照与阴影经常是彼此分离的。
静态光照流程方面
- 经编辑器预计算并将大部分结果存储在光照贴图中
- 性能方面非常快速,但会增加内存占用
- 需要花很多时间来预计算光照
- 每当有改动时,光照需要重新渲染
- 模型需要光照贴图UV,准备步骤耗费时间
静态光照质量方面
- 可以处理辐射和全局光照
- 真实的阴影效果,包括软阴影
- 质量取决于光照贴图分辨率和UV布局
- 由于UV布局的关系,可能会有接缝
- 光照贴图分辨率有上限
- 超大型模型会缺乏足够的光照贴图UV空间
光照贴图 Lightmaps
实际上就是一张纹理,一张烘焙的有光照和阴影信息的图片。然后纹理会和底色(BaseColor)相乘,使其看起来像是被照亮了一样。
动态光照流程方面
- 通过使用GBuffer实时渲染
- 可以随意改变,移动,增加删除光源
- 不需要额外的模型准备
- 阴影十分耗费性能
- 有不同的方式来渲染阴影,需要花时间和经验来找到合适的方式或组合
动态光照质量方面
- 由于阴影耗费性能大,所以通常降低渲染质量来补偿
- 对大部分内容都不会产生辐射\全局光照,实现软阴影比较困难
- 动态阴影不太需要考虑模型大小
动态阴影类型
-
常规动态阴影 Regular Dynamic Shadows
应用最对,最常规的动态阴影
常规动态阴影 - 逐个对象阴影 Per Object Shadows
又叫固定光源阴影 Stationary Light Shadows。是动态阴影与静态阴影的混合。 -
联级阴影地图 Cascaded Shadow Maps CSM
只能应用在定向光源,会将不同的动态阴影集相互串联,根据距离切换不同的阴影贴图,在定向光源 - Detail - CSM中可以调整动态阴影联级数量,距离。
CSM -
距离场阴影 Distance Field Shadows
主要处理远距离阴影,不准确,但消耗低。使用距离场信息而不是几何体信息。通过创建体积纹理,存储点之间的距离信息。体积纹理的分辨率决定了阴影的细节程度。此功能默认没有开启,需在项目设置开启,开启后需要花时间生成距离场。之后可以在 显示 - 可视化 - 全局距离场查看。
距离场阴影
体积纹理:一张纹理图,通过切分为一个个小块,并叠加在一起,从而形成立体区域,然后根据白色的位置确定对象的形状 - 插图阴影 Inset Shadows
与逐个对象阴影本质相同,使动态模型(如玩家角色)拥有高分辨率动态阴影。 - 接触阴影 Contact Shadows
能在细小物体下投射效果不错的接触阴影。 - 胶囊阴影 Capsule Shadows
简单便宜,比如用碰撞胶囊来投阴影
光照渲染过程
渲染阴影所需4项
- 相机到几何体的距离
- 相机的位置
- 光源的位置
- 几何体到光源的位置
动态光照性能影响
- 动态光照本身对性能消耗相对较低
- 损耗源于像素的着色运算,像素越多,速度越慢
- 因此镜头离光源越近,受影响的像素就越多,渲染的速度就越慢
- 因此光源的源半径要尽可能小
- 防止光源重叠过多和反复重叠
动态阴影性能影响
- 如不需要,关闭阴影,如可用蓝图对门后的光源进行集体控制
- 多边形面数对阴影性能产生较大影响
- 距离场对棱角分明的几何体效果更好
- 远距离时渐出或关闭阴影
光照混合
- 静态光照用于远距离和微弱的光照
- 用静态光照来渲染摄像机附近的间接光照
- 在一个静态光照之上添加一个动态光照,或直接使用Stationary Light
- 若需要尽可能高的性能,只是用静态光照
- 想随时改变光照效果,只使用动态光照。
距离雾
透明
- 透明效果对延迟渲染比较困难,所以透明效果通常留到渲染最后阶段,或者新辟管线用前向渲染然后与延迟渲染混合。
- 困难的原因是延迟渲染都是通过GBuffer混合实现,但是GBuffer难以提供足够的信息来绘制透明效果。因此透明效果经常看起来比较简单,除非适当的设置或改用前向渲染。
后期处理
泛光 Bloom
颜色分级 Color Grading
- LUT Look Up Table 查找表,16个不同的网格,构成4096种颜色。在一帧中,每个像素在默认表中寻找与之最接近的颜色,通过对比实际使用的LUT来确认颜色的偏移,然后应用到整帧的颜色偏移。