中EN
概念页

UE5 渲染架构

UE5 Rendering Architecture

UE5 的渲染器是独立的 Renderer 模块,在渲染线程 (Rendering Thread) 上以「场景代理」镜像游戏线程的组件状态,每帧按固定顺序执行深度预通道、G-buffer 基础通道、光照、雾、半透明和后处理。现代渲染代码用渲染依赖图 (Render Dependency Graph, RDG) 声明通道与资源,由 RDG 自动排屏障、复用瞬态内存、并行录制;最终命令经 RHI 线程翻译成 D3D12/Vulkan 等图形 API 调用。默认走延迟着色 (Deferred Shading),也可切换前向着色 (Forward Shading)。

三条线程与"落后一帧"

UE 把一帧拆给三条主线程:游戏线程 (Game Thread) 跑玩法与 Tick;渲染线程 (Render Thread) 作为前端,把与平台无关的图形命令录进命令列表 (Command List);RHI 线程 (RHI Thread) 作为后端,把命令翻译到具体图形 API。"前端并行生成的内容,在后端也并行翻译"。(Parallel Rendering Overview)

默认是"落后一帧"的流水线:游戏线程处理第 N+1 帧时,渲染线程处理第 N 帧;有了 RHI 线程后,渲染线程还能在 RHI 线程翻译第 N 帧时先做第 N+1 帧的可见性计算。同步由 FFrameEndSync 和 RHIThreadFence 仲裁,并且无论怎么并行,提交给 GPU 的命令顺序与单线程渲染器一致。(Parallel Rendering Overview)

下图表示稳态下三条线程和 GPU 同时处理相邻的几帧(具体错位取决于各自耗时):

gantt
    dateFormat X
    axisFormat %s
    section 游戏线程
    帧 N+1 :0, 3
    帧 N+2 :3, 6
    section 渲染线程
    帧 N   :0, 3
    帧 N+1 :3, 6
    section RHI 线程
    帧 N-1/N :0, 3
    帧 N/N+1 :3, 6
    section GPU
    帧 N-1 :0, 3
    帧 N   :3, 6

这正是 多线程渲染与任务系统 中"按阶段流水线化、用延迟换吞吐"的思路在 UE 中的落地;代价是输入到显示的延迟增加,且跨线程访问必须严格遵守所有权。

游戏线程对象与渲染线程镜像

引擎把横跨两条线程的类按所有权一分为二:(Graphics Programming Overview)

游戏线程(Engine 模块)渲染线程
UWorldFScene
UPrimitiveComponentFPrimitiveSceneProxy / FPrimitiveSceneInfo
ULightComponentFLightSceneProxy / FLightSceneInfo
ULocalPlayerFSceneViewState(跨帧的视图私有数据)
—FSceneView / FViewInfo(每帧新建)

组件注册 (Register) 后才对渲染器可见;游戏线程改了组件属性必须调用 MarkRenderStateDirty() 才会同步到渲染线程,渲染线程不得直接读 UPrimitiveComponent。图元组件 (Primitive Component) 是可见性与相关性判断的基本单位,视锥剔除、遮挡剔除都按图元进行,其包围盒还决定阴影投射和光源影响范围——所以组件拆多大是一个设计决策。(Graphics Programming Overview) 场景内用八叉树 (Octree) 加速空间查询。

实际项目中最常见的坑:在 Tick(见 委托与 Tick 机制)里每帧改组件渲染属性会反复触发渲染状态重建;在渲染线程读 UObject 会产生数据竞争。

一帧的渲染顺序(延迟路径)

入口是 FDeferredShadingSceneRenderer::Render。官方给出的高层顺序如下(文档自称是简化视图):(Graphics Programming Overview)

flowchart LR
    A["InitViews<br/>可见性剔除/阴影准备"] --> B["PrePass<br/>仅深度, 建 HiZ"]
    B --> C["Base Pass<br/>写 G-buffer"]
    C --> D["遮挡查询<br/>供下一帧用"]
    D --> E["Lighting<br/>阴影图+延迟/分块延迟光照"]
    E --> F[Fog]
    F --> G[Translucency]
    G --> H[Post Processing]

与通用理论的对应:

UE 步骤对应理论页说明
PrePass 填充 Hierarchical Z硬件深度剔除与纹理单元先写深度,让后续昂贵的基础通道像素着色被 early-z/HiZ 剔掉
Base Pass 写 G-buffer延迟着色几何与光照解耦的经典延迟着色
光照"标准延迟 + 分块延迟"分块着色多光源时按屏幕分块裁剪光源
遮挡查询结果用于下一帧 InitViews遮挡剔除用一帧延迟换取不阻塞 GPU 的硬件遮挡查询
每图元剔除剔除技术图元粒度决定剔除效率

注意:这份 Graphics Programming Overview 虽挂在 5.8 文档下,但其中关于绘制策略 (Drawing Policy)、静态绘制列表的文字明显沿袭 UE4,可能与当前代码(Mesh Draw Command、Nanite 独立通道、Lumen)不完全一致,阅读源码时以 FDeferredShadingSceneRenderer::Render 为准。(Graphics Programming Overview)

静态与动态绘制路径

动态路径每帧遍历可见动态图元、调用代理回调生成网格元素:插入成本低、遍历成本高、无状态排序。静态路径在组件挂接时就把绘制状态缓存好并排序,官方称静态网格在渲染线程上约快 3 倍,但只能缓存与视图无关的状态。(Graphics Programming Overview) 这就是 绘制调用与状态切换优化 中"按状态排序、缓存绘制命令"的思想。RDG 文档也提到 Mesh Draw Command 会被缓存并在多个通道和视图间共享,因此通道/视图级参数改用静态统一缓冲 (Static Uniform Buffer) 按通道频率绑定,而不是按 PSO 频率重绑。(Render Dependency Graph)

渲染依赖图 (RDG)

RDG 是"立即模式 API,把渲染命令记录进图数据结构再编译执行"。官方要求"所有高层渲染代码都应使用 RDG"。(Render Dependency Graph)

它把渲染管线拆成两条时间线:设置 (Setup) 时创建资源描述、添加通道、做分支;执行 (Execute) 时 RDG 编译图后在多个线程调用各通道的 lambda 录制 RHI 命令。(Render Dependency Graph)

flowchart TB
    S1["AllocParameters 声明读写资源"] --> S2["AddPass 名称+参数+标志+lambda"]
    S2 --> S3[GraphBuilder.Execute]
    S3 --> C1["编译: 剔除无用通道/资源"]
    C1 --> C2["计算资源生命周期 瞬态内存别名"]
    C2 --> C3["插入分离屏障 split barrier 与异步计算栅栏"]
    C3 --> X["并行执行 lambda 录制命令列表"]

RDG 自动完成的事情:异步计算 (Async Compute) 栅栏、瞬态资源的最优生命周期与内存别名 (Aliasing)、子资源状态转换、并行命令录制、剔除未使用的通道和资源、API 用法校验,以及在 RDG Insights 中可视化。(Render Dependency Graph) 关键规则与坑:

  • CreateTexture/CreateBuffer 只分配描述符,真正的 RHI 资源在执行期才分配;GetRHI() 只能在声明了该资源的通道 lambda 里调用,否则校验层断言。(Render Dependency Graph)
  • lambda 应无副作用,且不要按引用捕获栈上对象——执行时它们已经不存在;用 AllocObject/AllocPOD 走 RDG 的线性分配器。(Render Dependency Graph)
  • 瞬态分配器让生命周期不相交的资源共用内存,显著降低显存峰值,但资源的"旧内容"是垃圾,不能依赖;排查时可关 r.RDG.TransientAllocator。(Render Dependency Graph)
  • 使用 FRHICommandListImmediate 会让通道失去并行资格。(Render Dependency Graph)
  • 调试:r.RDG.ImmediateMode/-rdgimmediate 让通道在 AddPass 时立刻执行(关掉所有图优化),配合 r.RHICmdBypass 得到单一调用栈。(Render Dependency Graph)

RDG 本质是业界"帧图 (Frame Graph)"思路:先声明后执行,让引擎掌握整帧资源依赖。它把 计算着色器 的异步计算、多线程渲染与任务系统 的并行命令录制都变成自动化的。异步计算的调度是:RDG 找到异步通道在图形队列上的最后一个生产者并插入栅栏,到第一个图形队列消费者处再汇合。(Render Dependency Graph)

延迟 vs 前向

维度延迟渲染器(默认)前向渲染器
定位功能最全、最通用更快的基线,适合 VR
抗锯齿TSR/TAA/FXAA额外支持 MSAA
光源处理G-buffer + 延迟/分块延迟光照光源和反射捕获按视锥空间网格剔除,每像素遍历影响它的光源
屏幕空间效果支持 SSR、SSAO、接触阴影不支持
材质可采样 G-buffer 场景纹理不能访问 G-buffer;全部特性打包进一个 shader,采样器更少
UE5 新特性Lumen、Nanite 可用Lumen 不兼容前向;Nanite 不支持前向

来源:(Forward Shading Renderer)、Lumen Technical Details、Nanite Virtualized Geometry Overview。前向渲染器"按视锥空间网格剔除光源"正对应 分簇着色。官方案例 Robo Recall 中前向比延迟快约 22%(GTX 970),主要来自按材质关闭的特性;而 MSAA 替代 TAA 约增加 25% GPU 帧时间。(Forward Shading Renderer)

选型:PC/主机高画质项目用默认延迟 + Nanite/Lumen/VSM/TSR 一套;VR 或需要 MSAA 的项目考虑前向,但要接受失去 Lumen、Nanite 和屏幕空间效果。

RHI

RHI (Render Hardware Interface) 是平台图形 API 之上的薄层,抽象层级"尽可能低",让大多数功能写一次即可在支持对应特性级别的平台运行。(Graphics Programming Overview) 渲染器作为独立模块(见 模块、插件与构建系统),引擎通过 IRendererModule/FSceneInterface 接口调用它。(Graphics Programming Overview)

取舍小结

  • 多线程流水线提高吞吐但增加延迟与同步复杂度;
  • RDG 带来自动屏障/别名/并行,代价是两条时间线让调试变难(崩溃栈离设置代码"隔了两层");(Render Dependency Graph)
  • 延迟渲染功能全,前向渲染基线快、MSAA 友好,但与 UE5 主力特性不兼容。
  1. Graphics Programming Overview UE 5.8 文档
  2. Render Dependency Graph UE 5.8 文档
  3. Parallel Rendering Overview UE 5.8 文档
  4. Forward Shading Renderer UE 5.8 文档

相关页面

图形学延迟着色Deferred Shading延迟着色先用一遍几何 pass 把所有可见表面的材质参数写入多张渲染目标(G-buffer),再以后处理方式对这些存储值逐光源计算光照,从而把"几何可见性"与"光照"彻底解耦,并把材质与光源分离。它避免了着色的过度绘制、简化了着色器管理、天然适合贴花,但 G-buffer 占用显存和带宽,且难以直接支持透明和 MSAA。本页也覆盖前向渲染下限制光源影响范围的基础手段与"光照预通道 (light prepass)"类混合方案。图形学分块着色Tiled Shading分块着色把屏幕划分为瓦片(如 32×32 像素),先为每个瓦片建立可能影响它的光源列表,着色时每个像素在一次着色器调用中只遍历所在瓦片的列表。它可用于延迟着色(tiled deferred),也可用于前向着色(tiled forward,又称 Forward+)。配合 z-prepass 得到的瓦片深度范围可进一步剔除光源,但深度不连续会削弱效果,于是出现 HalfZ、2.5D 剔除等改进。图形学分簇着色Clustered Shading分簇着色把整个视锥(而不只是屏幕平面)划分为三维单元——簇 (cluster),按簇建立光源列表。与依赖场景几何深度的分块着色不同,簇的划分与几何无关,可只由视图和光源计算,因此对相机位置的性能波动更小、对深度不连续更稳健,并为不透明、透明、体积物体提供统一的光照方案。它同样可配合前向或延迟着色,并衍生出 z-binning、光源树等更省内存的变体。本页还汇总了各类多光源方案的对比。图形学渲染管线Rendering Pipeline渲染管线是实时图形的核心:给定虚拟相机、三维物体、光源等,把场景转换成一张二维图像。RTR4 把它粗分为四个功能阶段:应用、几何处理、光栅化、像素处理,各阶段并行执行,整体速度由最慢的阶段决定。游戏开发多线程渲染与任务系统Multithreaded Rendering & Task SystemsCPU 时钟频率约在 2003 年停在 3.4 GHz 附近后,性能来自多核。应用阶段的优化一是让代码与内存访问更缓存友好,二是利用多核:多处理器流水线(时间并行,提高吞吐但增加延迟)、并行处理(空间并行,延迟更低)以及两者结合的任务系统;图形 API 用命令缓冲让多个线程并行录制绘制命令,新一代 API(DirectX 12、Vulkan、Metal)把更多责任交给应用以降低驱动开销。综合帧时间预算与渲染性能Frame Budget & Rendering Performance本页把 wiki 中分散在 RTR4 第 18~23 章的性能知识串成一条线:一帧的时间预算由最慢的管线阶段决定,先用毫秒帧时间度量并定位瓶颈,再按瓶颈所在位置——CPU/驱动、几何、像素、带宽/内存——选用对应手段;最后从 GPU 硬件机制(占用率、HiZ/early-z、压缩、分块架构)理解这些手段为何有效。各结论的细节与出处见所链接的概念页。图形学遮挡剔除Occlusion Culling遮挡剔除尽早剔除被其他物体挡住的物体,避免它们在 z-buffer 中被光栅化、深度比较甚至着色。本页覆盖遮挡剔除的分类与通用框架、GPU 遮挡查询、层次 Z 缓冲 (HZB) 及其在现代引擎中的简化变体(含两遍法、软件光栅化遮挡),以及在 CPU/GPU 上按物体—簇—三角形多粒度组合剔除并用间接绘制提交的剔除系统。图形学硬件深度剔除与纹理单元Hardware Depth Culling & Texture UnitsGPU 的深度管线先在瓦片级用 HiZ 单元做 zmax/zmin 粗剔除,再逐采样做 early-z 深度测试以免执行被遮挡片元的像素着色器,必要时退回到着色后的 late depth test;深度缓冲同样有瓦片压缩与快速清除。纹理单元以固定功能硬件完成寻址、过滤、钳位与解压(比软件快可达 40 倍),按 quad 有限差分求导数以选择 LOD,并依靠瓦片化/Morton 交织存储和纹理缓存提高局部性。图形学绘制调用与状态切换优化Draw Calls & State Change Optimization调用图形 API 的开销主要在 CPU 端的驱动里:状态切换昂贵,每次绘制调用有固定开销,大量小批次会让 GPU"饿死"。对策是:合理放置缓冲;按状态代价排序分组;共享纹理和着色器;合并网格和实例化以减少调用;并用一个整数排序键同时兼顾状态分组与前后排序。图形学计算着色器Compute Shader计算着色器是 DirectX 11 引入的一种 GPU 计算方式:它不固定在图形管线的某个位置,但由图形 API 调用,和其他着色器共用统一着色器核心。它暴露了线程组和组内共享内存,最大的优势是能直接处理 GPU 上生成的数据,不用来回传给 CPU。游戏开发 · UE5Nanite 虚拟化几何体Nanite Virtualized GeometryNanite 是 UE5 的虚拟化几何体 (Virtualized Geometry) 系统:导入时把网格切成分层的三角形簇 (Cluster),渲染时按相机视角逐簇选择细节层次并无缝拼接,按需从磁盘流送,并在绕过传统绘制调用 (Draw Call) 的独立通道中渲染,使帧预算不再受多边形数、绘制调用和网格内存约束。游戏开发 · UE5Lumen 全局光照与反射Lumen Global Illumination & ReflectionsLumen 是 UE5 默认的全动态全局光照 (Global Illumination, GI) 与反射系统:先做屏幕空间追踪,未命中再对"Lumen 场景"做软件光追(有符号距离场)或硬件光追,命中点的光照从按网格离线放置的卡片 (Card) 构成的表面缓存 (Surface Cache) 中查询,表面缓存的直接与间接光照分摊到多帧更新,从而得到无限次漫反射弹射和全粗糙度范围的反射。游戏开发 · UE5虚拟阴影贴图Virtual Shadow Maps虚拟阴影贴图 (Virtual Shadow Maps, VSM) 是 UE5 默认的阴影方法:概念上就是分辨率极高(虚拟 16k×16k)的阴影贴图,切成 128×128 的页 (Page),只为屏幕上需要着色的像素分配和渲染页,并跨帧缓存;方向光用 clipmap 覆盖大范围,软阴影由阴影贴图光线追踪 (Shadow Map Ray Tracing, SMRT) 生成。它为 Nanite 设计,与 Nanite 一起使用时最高效。游戏开发 · UE5TSR 与抗锯齿方案Temporal Super Resolution & Anti-AliasingUE5 提供四种抗锯齿:时间超分辨率 (Temporal Super Resolution, TSR,默认)、时间抗锯齿上采样 (TAAU)、快速近似抗锯齿 (FXAA) 和仅前向渲染可用的多重采样抗锯齿 (MSAA)。TSR 是与平台无关的时间性上采样器,用低内部分辨率渲染,借助历史帧累积细节输出接近原生的画面,把节省的 GPU 时间留给 Nanite 与 Lumen。游戏开发 · UE5UE5 性能分析UE5 ProfilingUE5 性能分析的基本流程是:先用 stat unit 判断瓶颈在游戏线程、渲染线程还是 GPU,再用对应的 stat 组、profilegpu/GPU 统计或 Unreal Insights 的 Timing/Memory 等视图向下钻取;渲染特性(RDG、VSM、TSR)各自提供作用域和可视化。测量时要避开会扭曲数据的设置(调试构建、异步计算、可视化模式)。游戏开发 · UE5委托与 Tick 机制Delegates & TickUE 有两种驱动游戏逻辑的方式:事件驱动——委托 (Delegate) 在某事发生时回调绑定的函数;轮询驱动——Tick 每帧(或按最小间隔)调用 Actor/组件的更新函数,并按 Tick 组 (Tick Group) 与物理模拟排序。官方建议能用定时器、委托就不用 Tick,必须 Tick 时用 Tick 组和 Tick 依赖保证顺序,避免“差一帧”问题。游戏开发 · UE5模块、插件与构建系统Modules, Plugins & Build SystemUE 的代码由模块 (Module) 组成:每个模块是一个独立编译单元,用 [模块名].Build.cs 声明依赖。插件 (Plugin) 是可按项目启用/禁用的代码与内容集合,用 .uplugin 描述,可包含多个模块。虚幻构建工具 (UnrealBuildTool, UBT) 根据 .Target.cs 和 .Build.cs(而不是 IDE 的 .sln)编译,默认把模块编成 DLL 由一个可执行文件加载,也可整体链接成单体 (Monolithic) 可执行文件。游戏开发 · UE5材质系统Material SystemUE 材质 (Material) 是用节点图描述「表面如何与光交互」的着色器生成器。它有三个正交维度:着色模型 (Shading Model) 决定光照模型(Default Lit、次表面、清漆、毛发等),混合模式 (Blend Mode) 决定与背景的合成公式(Opaque/Masked/Translucent…),参数 + 材质实例 (Material Instance) 让美术在不重编译着色器的前提下做变体。PBR 输入(Base Color/Roughness/Metallic/Specular)直接对应 RTR4 的金属度-粗糙度工作流;最大的工程坑是静态参数导致的着色器排列爆炸。游戏开发 · UE5任务系统Tasks SystemUE::Tasks 是 UE5 的任务级并行库,是对 TaskGraph 的改进,与 TaskGraph 共用同一套调度器和工作线程。它支持启动任务、等待或取结果、声明前置依赖、嵌套任务、管道 (Pipe) 和任务事件。官方建议用依赖关系把工作连成图,而不是阻塞等待;5.5 起用「超额订阅」取代了容易死锁的忙等待。游戏开发 · UE5像素流送Pixel Streaming像素流送 (Pixel Streaming) 让 UE 应用运行在桌面 PC 或云服务器上,把渲染好的画面和音频编码成媒体流,通过 WebRTC 点对点发送到浏览器和移动设备;用户可以在网页里用键盘、鼠标、触摸、手柄操作应用。系统由引擎内的 Pixel Streaming 插件和一个信令与 Web 服务器 (Signalling and Web Server) 组成,跨网络连接时需要 STUN/TURN。游戏开发双缓冲Double Buffer双缓冲 (Double Buffer) 让一系列按顺序执行的修改,在外界看来像是瞬间、同时完成的:类内部保留“当前缓冲”和“下一缓冲”两份状态,读取总是读当前缓冲,写入总是写下一缓冲,改完后用一次原子交换让新状态整体生效。图形里的前后缓冲是最经典的例子;同样的思路也能让一帧内依次更新的游戏对象互相看到的都是上一帧的一致状态。游戏开发脏标记Dirty Flag脏标记 (Dirty Flag) 用一个布尔标记记录“派生数据是否已经过时”:原始数据变化时置位;需要派生数据时,如果标记已置位就重新计算并清除标记,否则直接用缓存的结果。场景图里由局部变换求世界变换是经典例子。它只在原始数据变化比派生数据被使用更频繁、而且难以增量更新时才划算,最大的风险是漏置标记导致使用过时数据。游戏开发运行时引擎分层架构Runtime Engine ArchitectureGregory 把游戏视为“软实时、交互式、基于代理的计算机模拟”,把游戏引擎定义为数据驱动、可复用来做多款游戏的软件。运行时引擎按层组织:从目标硬件、驱动、操作系统、第三方 SDK、平台无关层、核心系统、资源管理器,到渲染、物理、动画、音频、网络等子系统,再到游戏性基础层和游戏专属子系统;上层依赖下层,避免循环依赖。图形学图形 API 设备、管线状态与资源Graphics API Devices, Pipeline State & Resources所有图形 API(OpenGL、DirectX、Vulkan、Metal)都围绕同一组概念:代表 GPU 的设备、描述各阶段配置的管线状态、承载数据的资源及其视图。GEA 把 API 分成高层(OpenGL、D3D11 及更早)和底层(D3D12、Vulkan、Metal、PlayStation 的 Gnm),并介绍 D3D11 的特性级别、设备上下文与延迟上下文、D3D12 的命令列表、PSO 和根签名。本页按 GEA 的整理讲这些跨 API 的共同概念,不是任何一个 API 的教程。图形学图形栈与渲染引擎架构Graphics Stack & Rendering Engine Architecture渲染引擎要解决的"渲染问题"是:给定由场景元素、虚拟光源和虚拟相机组成的虚拟世界,生成相机看到的二维彩色图像。GEA 把它拆成可见性问题和光照问题,按解法把引擎分为光线追踪、深度缓冲光栅化、辐射度和混合四类;光栅化引擎通常采用立即模式的两层结构:下层是图形管线,上层是负责剔除、提交几何和设置着色器参数的应用层。本页讲这套整体架构,管线内部见 渲染管线。综合引擎运行时架构总览Engine Runtime Architecture Overview本页把 wiki 中分散在 GEA 第 1、6、8、17 章及 GPP、UE5 相关页面里的内容串成一条线,回答"一个游戏引擎在运行时是怎样组织起来的":静态结构上是自下而上的分层,上层依赖下层;启动关闭按依赖顺序显式进行;每一帧由主循环驱动各子系统批量更新,游戏对象的逻辑被安排在子系统阶段之间,并按依赖分桶或设先后关系;多核上一帧被拆成成百上千个作业;玩法基础系统(对象模型、事件、脚本、世界加载流送、高层流程)位于引擎与游戏的分界线附近。各结论的细节与出处见所链接的概念页。图形学剔除技术Culling Techniques剔除 (culling) 是在渲染前尽快去掉对最终图像没有贡献的部分。本页讲几何类剔除:背面剔除(含成簇背面剔除与法线锥)、视锥剔除(层次化、平面掩码)、室内场景的入口剔除,以及牺牲质量换速度的细节剔除和小三角形剔除。更复杂的遮挡剔除见 遮挡剔除。综合游戏编程模式选型:解耦与性能Choosing Game Programming Patterns本页把《Game Programming Patterns》里的 19 个模式放到同一张取舍图上:大多数模式要么为了解耦(降低改代码时需要装进脑子里的知识量),要么为了性能(利用对数据和硬件的具体假设),而这两个目标经常互相拉扯。下面先讲全书的定位和 Nystrom 的架构观,再给出按问题选模式的路线和与 UE 的对照。

被这些页面引用2 篇

输入关键词开始搜索

滚轮或双指缩放 · 拖动平移 · 双击放大 / 还原