👁️🗨️ Bevy 引擎渲染架构设计概览
2025-06-05
前言
渲染 App 与世界
fn build(&self, app: &mut App) {
...
let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Err(_) => return,
};
}
渲染集合
pub enum RenderSet {
ExtractCommands,
PrepareAssets,
ManageViews,
Queue,
QueueMeshes,
PhaseSort,
Prepare,
PrepareResources,
PrepareResourcesFlush,
PrepareBindGroups,
Render, //!!
Cleanup,
PostCleanup,
}
render_app.add_systems(Render, custom_system.in_set(RenderSet::Queue));
管理渲染流程的数据结构
pub trait Node:
Downcast
+ Send
+ Sync
+ 'static {
// Required method
fn run<'w>(
&self,
graph: &mut RenderGraphContext<'_>,
render_context: &mut RenderContext<'w>,
world: &'w World,
) -> Result<(), NodeRunError>;
// Provided methods
fn input(&self) -> Vec<SlotInfo> { ... }
fn output(&self) -> Vec<SlotInfo> { ... }
fn update(&mut self, _world: &mut World) { ... }
}
render_app
// 添加一个子图
.add_render_sub_graph(Core3d)
// 添加 Node
.add_render_graph_node::<ViewNodeRunner<PrepassNode>>(Core3d, Node3d::Prepass)
.add_render_graph_node::<ViewNodeRunner<DeferredGBufferPrepassNode>>(
Core3d,
Node3d::DeferredPrepass,
)
...
...
// 定义边(Node 的顺序)
.add_render_graph_edges(
Core3d,
(
Node3d::Prepass,
Node3d::DeferredPrepass,
Node3d::CopyDeferredLightingId,
Node3d::EndPrepasses,
Node3d::StartMainPass,
Node3d::MainOpaquePass,
Node3d::MainTransmissivePass,
Node3d::MainTransparentPass,
Node3d::EndMainPass,
Node3d::Tonemapping,
Node3d::EndMainPassPostProcessing,
Node3d::Upscaling,
),
);
// * 实现 RenderLable 需要实现 Debug, Clone, Eq
#[derive(Debug, Clone, Hash, PartialEq, Eq, RenderLabel)]
pub struct StartFooPass;
View
ViewNode
pub trait ViewNode {
type ViewQuery: ReadOnlyQueryData;
// Required method
fn run<'w>(
&self,
graph: &mut RenderGraphContext<'_>,
render_context: &mut RenderContext<'w>,
view_query: <Self::ViewQuery as WorldQuery>::Item<'w>,
world: &'w World,
) -> Result<(), NodeRunError>;
// Provided method
fn update(&mut self, _world: &mut World) { ... }
}
注: ViewNode 并不是 Node 的实现, ViewNodeRunner 才是。
-
render_context 包含了 RenderDevice, CommandEncoder 等渲染上下文内容 -
graph 包含了我们当前渲染图的各类上下文信息 -
view_query 是我们要的 Query 的 View 的结果
ViewNode 的实现案例
impl ViewNode for MainOpaquePass3dNode {
type ViewQuery = (
Entity,
&'static ExtractedCamera,
&'static ViewTarget,
&'static ViewDepthTexture,
Option<&'static SkyboxPipelineId>,
Option<&'static SkyboxBindGroup>,
&'static ViewUniformOffset,
);
...
}
渲染阶段
-
它不是一个 Node -
它是一个管理被渲染的物体的上下文的数据结构 -
它可以管理这些信息 -
一个渲染阶段的渲染过程中,所有渲染命令使用同一个 RenderPass ,无法中途切换 -
一个实体可以在 BinnedRenderPhase 中出现多次,例如同一个实体分别在不同批次被绘制
...
fn run(...) { // impl ViewNode for MainOpaquePass3dNode
...
let opaque_phase = ... // 从世界获得 Opaque3d 的 BinnedRenderPhase 实例(用 Resource 管理)
// 渲染这个渲染阶段
opaque_phase.render(&mut render_pass, world, view_entity);
// 返回值是一个 Result,实际使用中需要处理异常,这里简写
}
...
// Item: 需要实现 BinnedPhaseItem
pub struct Opaque3d {
...
}
impl BinnedPhaseItem for Opaque3d {
type BinKey = Opaque3dBinKey;
...
}
// Key: 需要实现 Clone + Send + Sync + Eq + Ord + Hash
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Opaque3dBinKey { // Key
pub pipeline: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
...
}
pub fn add(
&mut self,
key: <BPI as BinnedPhaseItem>::BinKey,
entity: (Entity, MainEntity),
phase_type: BinnedRenderPhaseType,
)
pub struct MainEntity(Entity);
Draw trait、DrawFunctions
pub trait Draw<P>:
Send
+ Sync
+ 'static
where
P: PhaseItem,
{
// Required method
fn draw<'w>(
&mut self,
world: &'w World,
pass: &mut TrackedRenderPass<'w>,
view: Entity,
item: &P,
) -> Result<(), DrawError>;
// Provided method
fn prepare(&mut self, world: &World) { ... }
}
RenderCommand trait
// 声明一个新的 Command
struct DrawCustomPhaseItem;
// 为其实现 RenderCommand trait
impl<P> RenderCommand<P> for DrawCustomPhaseItem
where
P: PhaseItem,
{
type Param = SRes<CustomPhaseItemBuffers>;
type ViewQuery = ();
type ItemQuery = ();
fn render<'w>(...) -> RenderCommandResult {
// 渲染行为
...
}
}
// 一系列 RenderCommand 的元组被实现了 RenderCommand,
// 我们借由这种方式来将多个 Command 结合在一起
type DrawCustomPhaseItemCommands = (SetItemPipeline, DrawCustomPhaseItem);
...
fn main() {
...
app.get_sub_app_mut(RenderApp)
.unwrap()
...
// 为 App 的 Opaque3d 渲染阶段添加 DrawCustomPhaseItemCommands
.add_render_command::<Opaque3d, DrawCustomPhaseItemCommands>()
}
如何渲染一个渲染阶段
pub fn render<'w>(
&self,
render_pass: &mut TrackedRenderPass<'w>,
world: &'w World,
view: Entity,
) -> Result<(), DrawError>
其它的渲染阶段结构体
RenderPhase | 介绍 |
BinnedRenderPhase | 采用 Bin 的数据结构存储 PhaseItem |
SortedRenderPhase | 采用 Vec 存储 PhaseItem,可排序 |
使用 bevy_render
-
如果要在某个 Bevy 中已有的渲染阶段中插入一个自定义渲染行为,我们需要 -
如果我们的渲染行为不和任何现有的渲染阶段共享,比如后处理效果,我们需要
参考阅读
Extract
-
ExtractSchedule: https://docs.rs/bevy/latest/bevy/prelude/struct.ExtractSchedule.html -
ExtractComponent: https://docs.rs/bevy/latest/bevy/render/extract_component/index.html -
extract 相机到 ExtractedCamera 的实现: https://github.com/bevyengine/bevy/blob/main/crates/bevy_render/src/camera/camera.rs