Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add capability to render to a texture #3412

Closed
wants to merge 16 commits into from
Closed
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,10 @@ path = "examples/3d/spherical_area_lights.rs"
name = "texture"
path = "examples/3d/texture.rs"

[[example]]
name = "render_to_texture"
path = "examples/3d/render_to_texture.rs"

[[example]]
name = "update_gltf_scene"
path = "examples/3d/update_gltf_scene.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,5 @@ bevy_asset = { path = "../bevy_asset", version = "0.6.0" }
bevy_core = { path = "../bevy_core", version = "0.6.0" }
bevy_ecs = { path = "../bevy_ecs", version = "0.6.0" }
bevy_render = { path = "../bevy_render", version = "0.6.0" }
bevy_utils = { path = "../bevy_utils", version = "0.6.0" }

35 changes: 26 additions & 9 deletions crates/bevy_core_pipeline/src/clear_pass.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::collections::HashSet;

use crate::ClearColor;
use crate::{ClearColor, RenderTargetClearColors};
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
camera::{ExtractedCamera, RenderTarget},
prelude::Image,
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo},
render_resource::{
LoadOp, Operations, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
Expand Down Expand Up @@ -47,21 +49,26 @@ impl Node for ClearPassNode {
render_context: &mut RenderContext,
world: &World,
) -> Result<(), NodeRunError> {
let mut cleared_windows = HashSet::new();
let mut cleared_targets = HashSet::new();
let clear_color = world.get_resource::<ClearColor>().unwrap();
let render_target_clear_colors = world.get_resource::<RenderTargetClearColors>().unwrap();

// This gets all ViewTargets and ViewDepthTextures and clears its attachments
// TODO: This has the potential to clear the same target multiple times, if there
// are multiple views drawing to the same target. This should be fixed when we make
// clearing happen on "render targets" instead of "views" (see the TODO below for more context).
for (target, depth, camera) in self.query.iter_manual(world) {
let mut color = &clear_color.0;
if let Some(camera) = camera {
cleared_windows.insert(camera.window_id);
cleared_targets.insert(&camera.target);
if let Some(target_color) = render_target_clear_colors.get(&camera.target) {
color = target_color;
}
}
let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"),
color_attachments: &[target.get_color_attachment(Operations {
load: LoadOp::Clear(clear_color.0.into()),
load: LoadOp::Clear((*color).into()),
store: true,
})],
depth_stencil_attachment: depth.map(|depth| RenderPassDepthStencilAttachment {
Expand All @@ -83,18 +90,28 @@ impl Node for ClearPassNode {
// which will cause panics. The real fix here is to clear "render targets" directly
// instead of "views". This should be removed once full RenderTargets are implemented.
let windows = world.get_resource::<ExtractedWindows>().unwrap();
for window in windows.values() {
let images = world.get_resource::<RenderAssets<Image>>().unwrap();
for target in render_target_clear_colors.colors.keys().cloned().chain(
windows
.values()
.map(|window| RenderTarget::Window(window.id)),
) {
// skip windows that have already been cleared
if cleared_windows.contains(&window.id) {
if cleared_targets.contains(&target) {
continue;
}
let pass_descriptor = RenderPassDescriptor {
label: Some("clear_pass"),
color_attachments: &[RenderPassColorAttachment {
view: window.swap_chain_texture.as_ref().unwrap(),
view: target.get_texture_view(windows, images).unwrap(),
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(clear_color.0.into()),
load: LoadOp::Clear(
(*render_target_clear_colors
.get(&target)
.unwrap_or(&clear_color.0))
.into(),
),
store: true,
},
}],
Expand Down
33 changes: 30 additions & 3 deletions crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub mod prelude {
pub use crate::ClearColor;
}

use bevy_utils::HashMap;

pub use clear_pass::*;
pub use clear_pass_driver::*;
pub use main_pass_2d::*;
Expand All @@ -21,7 +23,7 @@ use bevy_app::{App, Plugin};
use bevy_core::FloatOrd;
use bevy_ecs::prelude::*;
use bevy_render::{
camera::{ActiveCameras, CameraPlugin},
camera::{ActiveCameras, CameraPlugin, RenderTarget},
color::Color,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{
Expand All @@ -48,6 +50,20 @@ impl Default for ClearColor {
}
}

#[derive(Clone, Debug, Default)]
pub struct RenderTargetClearColors {
colors: HashMap<RenderTarget, Color>,
}

impl RenderTargetClearColors {
pub fn get(&self, target: &RenderTarget) -> Option<&Color> {
self.colors.get(target)
}
pub fn insert(&mut self, target: RenderTarget, color: Color) {
self.colors.insert(target, color);
}
}

// Plugins that contribute to the RenderGraph should use the following label conventions:
// 1. Graph modules should have a NAME, input module, and node module (where relevant)
// 2. The "top level" graph is the plugin module root. Just add things like `pub mod node` directly under the plugin module
Expand Down Expand Up @@ -96,7 +112,8 @@ pub enum CorePipelineRenderSystems {

impl Plugin for CorePipelinePlugin {
fn build(&self, app: &mut App) {
app.init_resource::<ClearColor>();
app.init_resource::<ClearColor>()
.init_resource::<RenderTargetClearColors>();

let render_app = match app.get_sub_app_mut(RenderApp) {
Ok(render_app) => render_app,
Expand Down Expand Up @@ -330,12 +347,22 @@ impl CachedPipelinePhaseItem for Transparent3d {
}
}

pub fn extract_clear_color(clear_color: Res<ClearColor>, mut render_world: ResMut<RenderWorld>) {
pub fn extract_clear_color(
clear_color: Res<ClearColor>,
clear_colors: Res<RenderTargetClearColors>,
mut render_world: ResMut<RenderWorld>,
) {
// If the clear color has changed
if clear_color.is_changed() {
// Update the clear color resource in the render world
render_world.insert_resource(clear_color.clone());
}

// If the clear color has changed
if clear_colors.is_changed() {
// Update the clear color resource in the render world
render_world.insert_resource(clear_colors.clone());
}
}

pub fn extract_core_pipeline_camera_phases(
Expand Down
90 changes: 46 additions & 44 deletions crates/bevy_pbr/src/light.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
use std::collections::HashSet;

use bevy_asset::Assets;
use bevy_ecs::prelude::*;
use bevy_math::{Mat4, UVec2, UVec3, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_reflect::Reflect;
use bevy_render::{
camera::{Camera, CameraProjection, OrthographicProjection},
color::Color,
prelude::Image,
primitives::{Aabb, CubemapFrusta, Frustum, Sphere},
view::{ComputedVisibility, RenderLayers, Visibility, VisibleEntities},
};
Expand Down Expand Up @@ -354,62 +356,62 @@ const Z_SLICES: u32 = 24;
pub fn add_clusters(
mut commands: Commands,
windows: Res<Windows>,
images: Res<Assets<Image>>,
cameras: Query<(Entity, &Camera), Without<Clusters>>,
) {
for (entity, camera) in cameras.iter() {
let window = match windows.get(camera.window) {
Some(window) => window,
None => continue,
};
let clusters = Clusters::from_screen_size_and_z_slices(
UVec2::new(window.physical_width(), window.physical_height()),
Z_SLICES,
);
commands.entity(entity).insert(clusters);
if let Some(size) = camera.target.get_physical_size(&windows, &images) {
let clusters = Clusters::from_screen_size_and_z_slices(size, Z_SLICES);
commands.entity(entity).insert(clusters);
}
}
}

pub fn update_clusters(windows: Res<Windows>, mut views: Query<(&Camera, &mut Clusters)>) {
pub fn update_clusters(
windows: Res<Windows>,
images: Res<Assets<Image>>,
mut views: Query<(&Camera, &mut Clusters)>,
) {
for (camera, mut clusters) in views.iter_mut() {
let is_orthographic = camera.projection_matrix.w_axis.w == 1.0;
let inverse_projection = camera.projection_matrix.inverse();
let window = windows.get(camera.window).unwrap();
let screen_size_u32 = UVec2::new(window.physical_width(), window.physical_height());
// Don't update clusters if screen size is 0.
if screen_size_u32.x == 0 || screen_size_u32.y == 0 {
continue;
}
*clusters =
Clusters::from_screen_size_and_z_slices(screen_size_u32, clusters.axis_slices.z);
let screen_size = screen_size_u32.as_vec2();
let tile_size_u32 = clusters.tile_size;
let tile_size = tile_size_u32.as_vec2();

// Calculate view space AABBs
// NOTE: It is important that these are iterated in a specific order
// so that we can calculate the cluster index in the fragment shader!
// I (Rob Swain) choose to scan along rows of tiles in x,y, and for each tile then scan
// along z
let mut aabbs = Vec::with_capacity(
(clusters.axis_slices.y * clusters.axis_slices.x * clusters.axis_slices.z) as usize,
);
for y in 0..clusters.axis_slices.y {
for x in 0..clusters.axis_slices.x {
for z in 0..clusters.axis_slices.z {
aabbs.push(compute_aabb_for_cluster(
clusters.near,
camera.far,
tile_size,
screen_size,
inverse_projection,
is_orthographic,
clusters.axis_slices,
UVec3::new(x, y, z),
));
if let Some(screen_size_u32) = camera.target.get_physical_size(&windows, &images) {
// Don't update clusters if screen size is 0.
if screen_size_u32.x == 0 || screen_size_u32.y == 0 {
continue;
}
*clusters =
Clusters::from_screen_size_and_z_slices(screen_size_u32, clusters.axis_slices.z);
let screen_size = screen_size_u32.as_vec2();
let tile_size_u32 = clusters.tile_size;
let tile_size = tile_size_u32.as_vec2();

// Calculate view space AABBs
// NOTE: It is important that these are iterated in a specific order
// so that we can calculate the cluster index in the fragment shader!
// I (Rob Swain) choose to scan along rows of tiles in x,y, and for each tile then scan
// along z
let mut aabbs = Vec::with_capacity(
(clusters.axis_slices.y * clusters.axis_slices.x * clusters.axis_slices.z) as usize,
);
for y in 0..clusters.axis_slices.y {
for x in 0..clusters.axis_slices.x {
for z in 0..clusters.axis_slices.z {
aabbs.push(compute_aabb_for_cluster(
clusters.near,
camera.far,
tile_size,
screen_size,
inverse_projection,
is_orthographic,
clusters.axis_slices,
UVec3::new(x, y, z),
));
}
}
}
clusters.aabbs = aabbs;
}
clusters.aabbs = aabbs;
}
}

Expand Down
Loading