(整)Unreal渲染模块 框总览
@author: 黑袍小道
随缘查看
说明
由于搬山的渲染这部分担心自己理解错误,故而搬移官方下,后面整个完成再反过来更新
(这当且仅当做Unreal的帮助文档)。
图形编程
模块
渲染器代码存在于其自身的模块中。此模块将编译为非单块版本的一个 dll 文件。这可以使迭代更快,因为在渲染代码变更时无需重新链接整个应用程序。渲染器模块取决于引擎,因为其拥有许多向引擎的回调。然而当引擎需要调用渲染器中的某些代码时,这会通过某个接口来完成,通常为 IRendererModule 或 FSceneInterface。
场景代表
在 UE4 中,渲染器所见的场景由基本组件和 FScene 中存储的多种其他结构的列表定义。将维护一个基元的八叉树,用于加速空间查询。
主要场景类
UE4 中拥有和游戏线程并行运行的渲染线程。
主要的类为:
类 |
描述 |
UWorld |
包含多个可交互的 Actor 和组件的世界场景。关卡可以流送进入和退出世界场景,且程序中可以同时有多个世界场景处于激活状态。 |
ULevel |
一同加载/卸载并保存在同一地图文件中的 Actor 和组件合集。 |
USceneComponent |
需要添加到 FScene 中的任意对象的基础类,如光照、网格体、雾等。 |
UPrimitiveComponent |
可渲染或进行物理交互的任意资源的基础类。也可以作为可视性剔除的粒度和渲染属性规范(投射阴影等)。与所有 UObjects 一样,游戏线程拥有所有变量和状态,渲染线程不应直接对其进行访问。 |
ULightComponent |
代表光源。渲染器负责计算和添加其对场景的贡献。 |
FScene |
UWorld 的渲染器版本。对象仅在其被添加到 FScene(注册组件时调用)后才会存在于渲染器中。渲染线程拥有 FScene 中的所有状态,游戏线程无法直接对其进行修改。 |
FPrimitiveSceneProxy |
UPrimitiveComponent 的渲染器版本,为渲染线程映射 UPrimitiveComponent 状态。存在于引擎模块中,用于划分为子类以支持不同类型的基元(骨架、刚体、BSP 等)。实现某些非常重要的函数,如 GetViewRelevance、DrawDynamicElements 等。 |
FPrimitiveSceneInfo |
内部渲染器状态(FRendererModule 实现专有),对应于 UPrimitiveComponent 和 FPrimitiveSceneProxy。存在于渲染器模块中,因此引擎看不到它。 |
FSceneView |
单个视图到一个 FScene 的引擎代表。视图可以通过对 FSceneRenderer::Render 的不同调用的不同视图来渲染(多编辑器视口)或通过对 FSceneRenderer::Render 的同一调用中的多个视图来渲染(分屏游戏)。为每个帧构建新视图。 |
FViewInfo |
视图的内部渲染器代表,存在于渲染器模块中。 |
FSceneViewState |
ViewState 存储有关在多个帧中需要的某个视图的私有渲染器信息。在游戏中,每个 ULocalPlayer 只有一个视图状态。 |
FSceneRenderer |
为每个帧创建的类,用于封装跨帧的临时对象。 |
下面按其所在的模块列出了各种主类。
引擎模块 |
渲染器模块 |
UWorld |
FScene |
UPrimitiveComponent / FPrimitiveSceneProxy |
FPrimitiveSceneInfo |
FSceneView |
FViewInfo |
ULocalPlayer |
FSceneViewState |
ULightComponent / FLightSceneProxy |
FLightSceneInfo |
以及相同类(按哪个线程对其状态拥有所有权进行排列)。
游戏线程 |
渲染线程 |
UWorld |
FScene |
UPrimitiveComponent |
FPrimitiveSceneProxy / FPrimitiveSceneInfo |
|
FSceneView / FViewInfo |
ULocalPlayer |
FSceneViewState |
ULightComponent |
FLightSceneProxy / FLightSceneInfo |
材质类
类 |
描述 |
FMaterial |
连接用于渲染的材质的接口。可用于访问材质属性(如混合模式)。包含被渲染器用于检索个体着色器的着色器地图。 |
FMaterialResource |
UMaterial 的 FMaterial 接口实现。 |
FMaterialRenderProxy |
材质在渲染线程上的代表。可用于访问 FMaterial 接口和各个标量、向量和纹理参数。 |
UMaterialInterface |
[abstract] 用于材质功能的游戏线程接口。用于检索用于渲染的 FMaterialRenderProxy 和用作来源的 UMaterial。 |
UMaterial |
材质资源。授权为节点图形。计算用于着色、设置混合模式等的材质属性。 |
UMaterialInstance |
[abstract] UMaterial 的实例。使用 UMaterial 中的节点图形,但提供不同参数(标量、向量、纹理、静态切换)。每个实例都有一个父项 UMaterialInterface。因此,材质实例的父项可能是 UMaterial 或另一个 UMaterialInstance。这会形成一个链,最终通往 UMaterial。 |
UMaterialInstanceConstant |
只能在编辑器中修改的 UMaterialInstance。可以提供标量、向量、纹理和静态开关参数。 |
UMaterialInstanceDynamic |
可以在运行时修改的 UMaterialInstance。可提供标量、向量和纹理参数。无法提供静态开关参数,且无法成为另一 UMaterialInstance 的父项。 |
基元组件和代理
基元(就是Prim)组件是可视性和相关性确定的基本单位。例如,遮蔽和视锥剔除都是按基元进行的。因此在设计系统时,考虑组件的大小十分重要。每个组件都有一个边界,用于多种操作,如剔除、阴影投射和光照影响确定。
组件只有在注册之后才会对场景(以及渲染器)可见。
更改组件属性的游戏线程代码必须调用组件上的 MarkRenderStateDirty(),将更改传播到渲染线程。
FPrimitiveSceneProxy 和 FPrimitiveSceneInfo
FPrimitiveSceneProxy 是 UPrimitiveComponent 的渲染线程版本,用于根据组件类型划分子类。
它存在于引擎模块中,并在渲染通道中调用函数。
FPrimitiveSceneInfo 是基元组件状态,为渲染器模块私有。
重要的 FPrimitiveSceneProxy 方法
函数 |
描述 |
GetViewRelevance |
在帧的开始从 InitViews 调用,并返回填充的 FPrimitiveViewRelevance。 |
DrawDynamicElements |
调用,以便在某代理相关的任何通道中绘制该代理。仅在代理表示自己拥有动态相关性时调用。 |
DrawStaticElements |
调用以在基元与游戏线程相连时提交代理的 StaticMesh 元素。仅在代理表示自己拥有静态相关性时调用。 |
场景渲染顺序
渲染器按照其希望将数据整合给渲染目标的顺序处理场景。例如,仅 Depth 的通道会比 Base 通道先渲染,先填充 Heirarchical Z (HiZ),从而降低基础通道中的着色消耗。此顺序是按通道函数在 C++ 中调用的顺序静态决定的。
相关性
FPrimitiveViewRelevance 是说明通道与基元相关的信息。基元可能有存在不同相关性的多个元素,因此 FPrimitiveViewRelevance 相当于一个所有元素的相关性的逻辑 OR。这表示基元可以同时存在不透明和透明相关性,或动态和静态相关性;它们并非相互排斥。
FPrimitiveViewRelevance 还会显示基元是否需要使用动态 (bDynamicRelevance) 和/或静态 (bStaticRelevance) 渲染路径。
绘制规则
绘制规则包括通过通道特定的着色器渲染网格体的逻辑。它们使用 FVertexFactory 接口来抽取网格体类型,并使用 FMaterial 接口来抽取材质详情。在最底层,一条绘制规则会负责一组网格体材质着色器以及一个顶点工厂,将顶点工厂的缓冲区与渲染硬件接口 (RHI) 绑定,将网格体材质着色器与 RHI 绑定,设置适当的着色器参数,然后执行 RHI 绘制调用。
绘制规则方法
函数 |
描述 |
Constructor |
从给定的顶点工厂和材质着色器地图,并存储这些引用。 |
CreateBoundShaderState |
为绘制规则创建 RHI 边界着色器状态。 |
Matches/Compare |
提供排列绘制规则与静态绘制列表中的其他项目的方法。Matches 必须比较 DrawShared 依赖的所有因素。 |
DrawShared |
设置在从 Matches 返回 True 的绘制规则之间一致的 RHI 状态。例如,大多数绘制规则会为材质和顶点工厂排序,因此着色器参数只依赖可以设置的材质,并且可以绑定特定于该顶点工厂的顶点缓冲区。应尽可能在此处设置状态,而非在 SetMeshRenderState 设置,因为 DrawShared 在静态渲染路径中调用较少。 |
SetMeshRenderState |
设置特定于此网格体的 RHI 状态,或 DrawShared 中未设置的任何项目。这比 DrawShared 调用的次数多得多,因此此处性能非常重要。 |
DrawMesh |
实际发出 RHI 绘制调用。 |
渲染路径
UE4 拥有动态路径(能够提供更多的控制,但转换较慢)和静态渲染路径(缓存尽可能靠近 RHI 级别的场景转换)。差异基本上是整体上的,因为它们都在最底层使用绘制规则。应确保各个渲染通道(绘制规则)在需要时能够同时处理两个渲染路径。
动态渲染路径
动态渲染路径使用 TDynamicPrimitiveDrawer 并对每个要渲染的基元场景代理调用 DrawDynamicElements。需要使用动态路径来渲染的一组基元通过 FViewInfo::VisibleDynamicPrimitives 来跟踪。每个渲染通道都需要在此阵列上迭代,并调用各个基元上的 DrawDynamicElements。随后,代理的 DrawDynamicElements 需按需要组合出多个 FMeshElements,并将其随 DrawRichMesh 或 TDynamicPrimitiveDrawer::DrawMesh 提交。这样最终会创建一个新的临时绘制规则,调用 CreateBoundShaderState、DrawShared、SetMeshRenderState 以及 DrawMesh。
静态渲染路径
静态渲染路径通过静态绘制列表实现。网格体在连接到场景时会插入到绘制列表中。在插入过程中,将调用代理上的 DrawStaticElements,以收取 FStaticMeshElements。随后将随 CreateBoundShaderState 的结果创建并存储一个绘制规则实例。新的绘制示例将根据其 Compare 和 Matches 函数排序,并插入到绘制列表中的适当位置(参见 TStaticMeshDrawList::AddMesh)。在 InitViews 中,包含静态绘制列表中的可见性数据的 bitarray 会初始化并传递到 TStaticMeshDrawList::DrawVisible(实际绘制绘制列表的地方)。DrawShared 只会对所有相互匹配的绘制规则调用一次,而 SetMeshRenderState 和 DrawMesh 会对每个 FStaticMeshElement(参见 TStaticMeshDrawList::DrawElement)调用。
静态渲染路径会将许多工作移动连接时间,这会大大加快渲染时的场景转换。在渲染线程上针对静态网格体时,静态绘制列表的渲染会快 3 倍,从而允许场景中出现多得多的静态网格体。由于静态绘制列表会在连接时间缓存数据,因此它们仅能缓存与视图无关的状态。很少重新连接但经常需要渲染的基元非常适合静态绘制列表。
静态渲染路径可能会暴露出 bug,因为它每个状态桶只调用 DrawShared 一次。这些 bug 可能会很难发现,因为它们取决于场景中网格体的渲染顺序和连接顺序。特别的视图模式(如仅光照、无光照等)会强制所有基元使用动态路径,因此如果在强制动态渲染路径时 bug 消失,则其很可能是由于某绘制规则的 DrawShared 和/或 Matches 函数的错误实现而出现的。
总体渲染顺序
下面将说明从 FDeferredShadingSceneRenderer::Render 开始渲染一个帧时的控制流程:
操作 |
描述 |
GSceneRenderTargets.Allocate |
按需要重新分配全局场景渲染目标,使其对当前视图足够大。 |
InitViews |
通过多种剔除方法为视图初始化基元可见性,设立此帧可见的动态阴影、按需要交叉阴影视锥与世界场景(对整个场景的阴影或预阴影)。 |
PrePass / Depth only pass |
RenderPrePass / FDepthDrawingPolicy。渲染遮挡物,对景深缓冲区仅输出景深。该通道可以在多种模式下工作:禁用、仅遮蔽,或完全景深,具体取决于活动状态的功能的需要。该通道通常的用途是初始化 Hierarchical Z 以降低 Base 通道的着色消耗(Base 通道的像素着色器消耗非常大)。 |
Base pass |
RenderBasePass / TBasePassDrawingPolicy。渲染不透明和遮盖的材质,向 GBuffer 输出材质属性。光照图贡献和天空光照也会在此计算并加入场景颜色。 |
Issue Occlusion Queries / BeginOcclusionTests |
提出将用于下一帧的 InitViews 的延迟遮蔽查询。这会通过渲染所查询物体周围的相邻的框、有时还会将相邻的框组合在一起以减少绘制调用来完成。 |
Lighting |
阴影图将对各个光照渲染,光照贡献会累加到场景颜色,并使用标准延迟和平铺延迟着色。光照也会在透明光照体积中累加。 |
Fog |
雾和大气在延迟通道中对不透明表面进行逐个像素计算。 |
Translucency |
透明度累加到屏外渲染目标,在其中它应用了逐个顶点的雾化,因而可以整合到场景中。光照透明度在一个通道中计算最终光照以正确融合。 |
Post Processing |
多种后期处理效果均通过 GBuffers 应用。透明度将合成到场景中。 |
以上是相当简单概略的介绍。如需了解详情,请通读相关代码或输出的"profilegpu"日志。
渲染硬件接口 (RHI)
RHI 是平台特定的图形 API 之上的一个薄层。UE4 中的 RHI 抽象层尽可能低,这样大多数功能都能以与平台无关的代码写成,从而能够在支持所需功能层级的任何平台上运行。
功能集将量化到 ERHIFeatureLevel 中,降低复杂程度。如果平台无法支持某个功能层级所需的全部功能,则其必须降低层级,直至能够全部支持。
功能层级 |
描述 |
SM5 |
通常对应于 D3D11 Shader Model 5,但由于 OpenGL 4.3 限制,仅有 16 种纹理可以使用。支持曲面细分、计算着色器和立方体贴图阵列。支持延迟着色路径。 |
SM4 |
对应 D3D11 Shader Model 4,这与 SM5 基本相同,但没有曲面细分、计算着色器和立方体贴图阵列。支持延迟着色路径。不支持眼适应,因为其使用计算着色器。 |
ES2 |
对应大多数 OpenGL ES2 移动设备支持的功能。使用削减向前着色路径。 |
渲染状态分组
渲染状态根据其影响的流程部分而分组。例如,RHISetDepthState 可设置所有与景深缓冲相关的状态。
渲染状态默认值
由于渲染状态数量众多,要在每次绘制之前对它们全部设置一遍是不现实的。为此,UE4 具有隐性设置的一组状态,它们被认为是设置为了默认值(因此在变更后必须恢复为默认值),另外还有一组少得多的状态需要显性设置。没有隐性默认值的状态有:
- RHISetRenderTargets
- RHISetBoundShaderState
- RHISetDepthState
- RHISetBlendState
- RHISetRasterizerState
- 由 RHISetBoundShaderState 设置的着色器的任何依赖性
其他所有状态均视为已设置为其默认值(即相关 TStaticState 的定义,如默认的蜡纸模板状态由 RHISetStencilState(TStaticStencilState<>::GetRHI()) 设置。
(整)Unreal渲染模块 框总览的更多相关文章
- (原)Unreal 渲染模块引言Temp
@author:白袍小道 引言 本文只在对Unreal渲染模块做一些详细的理解,务求能分析出个大概. 其中框架的思想和实现的过程,是非常值得学习和推敲一二的. 涉及资源系统,材 ...
- (原)Unreal渲染模块 管线 - 着色器(1)
@author: 白袍小道 转载悄悄说明下 随缘查看,施主开心就好 说明: 本篇继续Unreal搬山部分的渲染模块的Shader部分, 主要牵扯模块RenderCore, ShaderCore, RH ...
- (原)Unreal渲染模块 管线 - 程序和场景查询
@author: 白袍小道 查看随意,转载随缘 第一部分: 这里主要关心加速算法,和该阶段相关的UE模块的结构和组件的处理. What-HOW-Why-HOW-What(嘿嘿,老规矩) 1.渲 ...
- (原)Unreal 渲染模块 渲染流程
@author:白袍小道 浏览分享随缘,评论不喷亦可. 扯淡部分: 在temp中,乱七八糟的说了下大致的UE过程.下面我们还是稍微别那么任性,一步步来吧. UE渲染模块牵扯到场景遍历. ...
- (原)Unreal渲染模块 源码和实例分析说明
@author:白袍小道 说明 1.由于小道就三境武夫而已,而UE渲染部分不仅管理挺大,而且牵扯技术和内容驳杂,所以才有这篇梳理. 2.尽量会按书籍和资料,源码,小模块的调试和搬山(就是敲键盘)..等 ...
- (原、整)Unreal源码 CoreUbject- Uobject
(原.整) Unreal源码 CoreUbject- Uobject 类别 [随笔分类]Unreal源码搬山 @author:白袍小道 随缘那啥 这里还是属于UE ...
- django-rest-framework-源码解析002-序列化/请求模块/响应模块/异常处理模块/渲染模块/十大接口
简介 当我们使用django-rest-framework框架时, 项目必定是前后端分离的, 那么前后端进行数据交互时, 常见的数据类型就是xml和json(现在主流的是json), 这里就需要我们d ...
- Django---Http协议简述和原理,HTTP请求码,HTTP请求格式和响应格式(重点),Django的安装与使用,Django项目的创建和运行(cmd和pycharm两种模式),Django的基础文件配置,Web框架的本质,服务器程序和应用程序(wsgiref服务端模块,jinja2模板渲染模块)的使用
Django---Http协议简述和原理,HTTP请求码,HTTP请求格式和响应格式(重点),Django的安装与使用,Django项目的创建和运行(cmd和pycharm两种模式),Django的基 ...
- DRF框架(一)——restful接口规范、基于规范下使用原生django接口查询和增加、原生Django CBV请求生命周期源码分析、drf请求生命周期源码分析、请求模块request、渲染模块render
DRF框架 全称:django-rest framework 知识点 1.接口:什么是接口.restful接口规范 2.CBV生命周期源码 - 基于restful规范下的CBV接口 3.请求组件 ...
随机推荐
- BestCoder Round #89 1001 Fxx and string
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5944 分析: 竟然 i,j,k成等比数列,即i*k = j*j,还要满足 j|i or j|k. 不防 ...
- vuejs父子组件的数据传递
在vue中,父组件往子组件传递参数都是通过属性的形式来传递的 <div id='root'> <counter :count = '1'></counter> &l ...
- VedioCapture
国内的技术的浮躁可见一般,在一个用了七八年的项目里面使用的类,居然拼写都是错的,在网上一搜,转载的也大有人在,最低级的错误,你可以不懂编程,但是只要上过高中,Video这个单词总该学过吧,居然转载的时 ...
- Java解析Excel工具类(兼容xls和xlsx)
依赖jar <dependency> <groupId>org.apache.poi</groupId> <artifactId>poi-ooxml&l ...
- 使用maven搭建ssm项目配置+tomact
一.代码自动配置:https://www.cnblogs.com/weibanggang/p/10043201.html 二.手动配置 1.创建好maven项目,在pom.xml配置文件 <!- ...
- img适配问题解决方法
将img放到background中,将background-size设置为100%:只需要适配背景为img的元素的宽和高即可.
- 大白话讲解BP算法(转载)
最近在看深度学习的东西,一开始看的吴恩达的UFLDL教程,有中文版就直接看了,后来发现有些地方总是不是很明确,又去看英文版,然后又找了些资料看,才发现,中文版的译者在翻译的时候会对省略的公式推导过程进 ...
- html和node.js实现websocket
websocket websocket是HTML5开始提供的一种单个TCP连接上进行全双工通讯的协议.它让客户端和服务端之间的数据交换变得更加简单,允许服务端主动向客户端推送数据.浏览器和服务器只需要 ...
- ABAP 调用远程rfc
ABAP 调用rfc DESTINATION附加项后面接的是远程目标名称,该目标在事务SM59中设定,其中包含连接和登录远程系统所需的全部参数信息.如果调用的就是本机的RFC目标,则DESTINATI ...
- 史上最强大的wordpress后台框架redux-framework安装及使用
redux-framework的相关链接 Redux的官方网站:https://reduxframework.com/ Redux文档查询:https://docs.reduxframework.co ...