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 render_to_texture example #1927

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,10 @@ path = "examples/3d/parenting.rs"
name = "pbr"
path = "examples/3d/pbr.rs"

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

[[example]]
name = "spawner"
path = "examples/3d/spawner.rs"
Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_render/src/render_graph/nodes/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ mod pass_node;
mod render_resources_node;
mod shared_buffers_node;
mod texture_copy_node;
mod texture_node;
mod window_swapchain_node;
mod window_texture_node;

Expand All @@ -11,5 +12,6 @@ pub use pass_node::*;
pub use render_resources_node::*;
pub use shared_buffers_node::*;
pub use texture_copy_node::*;
pub use texture_node::*;
pub use window_swapchain_node::*;
pub use window_texture_node::*;
69 changes: 69 additions & 0 deletions crates/bevy_render/src/render_graph/nodes/texture_node.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
use bevy_asset::HandleUntyped;
use bevy_ecs::world::World;
use std::borrow::Cow;

use crate::{
render_graph::{Node, ResourceSlotInfo, ResourceSlots},
renderer::{RenderContext, RenderResourceId, RenderResourceType},
texture::{SamplerDescriptor, TextureDescriptor, SAMPLER_ASSET_INDEX, TEXTURE_ASSET_INDEX},
};
pub struct TextureNode {
pub texture_descriptor: TextureDescriptor,
pub sampler_descriptor: Option<SamplerDescriptor>,
pub handle: Option<HandleUntyped>,
}

impl TextureNode {
pub const TEXTURE: &'static str = "texture";

pub fn new(
texture_descriptor: TextureDescriptor,
sampler_descriptor: Option<SamplerDescriptor>,
handle: Option<HandleUntyped>,
) -> Self {
Self {
texture_descriptor,
sampler_descriptor,
handle,
}
}
}

impl Node for TextureNode {
fn output(&self) -> &[ResourceSlotInfo] {
static OUTPUT: &[ResourceSlotInfo] = &[ResourceSlotInfo {
name: Cow::Borrowed(TextureNode::TEXTURE),
resource_type: RenderResourceType::Texture,
}];
OUTPUT
}

fn update(
&mut self,
_world: &World,
render_context: &mut dyn RenderContext,
_input: &ResourceSlots,
output: &mut ResourceSlots,
) {
if output.get(0).is_none() {
let render_resource_context = render_context.resources_mut();
let texture_id = render_resource_context.create_texture(self.texture_descriptor);
if let Some(handle) = &self.handle {
render_resource_context.set_asset_resource_untyped(
handle.clone(),
RenderResourceId::Texture(texture_id),
TEXTURE_ASSET_INDEX,
);
if let Some(sampler_descriptor) = self.sampler_descriptor {
let sampler_id = render_resource_context.create_sampler(&sampler_descriptor);
render_resource_context.set_asset_resource_untyped(
handle.clone(),
RenderResourceId::Sampler(sampler_id),
SAMPLER_ASSET_INDEX,
);
}
}
output.set(0, RenderResourceId::Texture(texture_id));
}
}
}
232 changes: 232 additions & 0 deletions examples/3d/render_to_texture.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
use bevy::{
prelude::*,
reflect::TypeUuid,
render::{
camera::{ActiveCameras, Camera, CameraProjection},
pass::{
LoadOp, Operations, PassDescriptor, RenderPassColorAttachmentDescriptor,
RenderPassDepthStencilAttachmentDescriptor, TextureAttachment,
},
render_graph::{
base::{node::MAIN_PASS, MainPass},
CameraNode, PassNode, RenderGraph, TextureNode,
},
texture::{
Extent3d, SamplerDescriptor, TextureDescriptor, TextureDimension, TextureFormat,
TextureUsage,
},
},
window::WindowId,
};

pub struct FirstPass;

pub const RENDER_TEXTURE_HANDLE: HandleUntyped =
HandleUntyped::weak_from_u64(Texture::TYPE_UUID, 13378939762009864029);

pub const TEXTURE_NODE: &str = "texure_node";
pub const DEPTH_TEXTURE_NODE: &str = "depth_texure_node";
pub const FIRST_PASS: &str = "first_pass";
pub const FIRST_PASS_CAMERA: &str = "first_pass_camera";

fn add_render_to_texture_graph(graph: &mut RenderGraph, size: Extent3d) {
let mut pass_node = PassNode::<&FirstPass>::new(PassDescriptor {
color_attachments: vec![RenderPassColorAttachmentDescriptor {
attachment: TextureAttachment::Input("color_attachment".to_string()),
resolve_target: None,
ops: Operations {
load: LoadOp::Clear(Color::rgb(0.1, 0.2, 0.3)),
store: true,
},
}],
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
attachment: TextureAttachment::Input("depth".to_string()),
depth_ops: Some(Operations {
load: LoadOp::Clear(1.0),
store: true,
}),
stencil_ops: None,
}),
sample_count: 1,
});

pass_node.add_camera(FIRST_PASS_CAMERA);

graph.add_node(FIRST_PASS, pass_node);
graph.add_system_node(FIRST_PASS_CAMERA, CameraNode::new(FIRST_PASS_CAMERA));
graph.add_node_edge(FIRST_PASS_CAMERA, FIRST_PASS).unwrap();

graph.add_node(
TEXTURE_NODE,
TextureNode::new(
TextureDescriptor {
size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: Default::default(),
usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::SAMPLED,
},
Some(SamplerDescriptor::default()),
Some(RENDER_TEXTURE_HANDLE),
),
);

graph.add_node(
DEPTH_TEXTURE_NODE,
TextureNode::new(
TextureDescriptor {
size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float,
usage: TextureUsage::OUTPUT_ATTACHMENT | TextureUsage::SAMPLED,
},
None,
None,
),
);

graph.add_node_edge(TEXTURE_NODE, FIRST_PASS).unwrap();
graph
.add_slot_edge(
TEXTURE_NODE,
TextureNode::TEXTURE,
FIRST_PASS,
"color_attachment",
)
.unwrap();
graph
.add_slot_edge(
DEPTH_TEXTURE_NODE,
TextureNode::TEXTURE,
FIRST_PASS,
"depth",
)
.unwrap();
graph.add_node_edge(FIRST_PASS, MAIN_PASS).unwrap();
graph.add_node_edge("transform", FIRST_PASS).unwrap();
}

struct FirstPassCube;
struct MainPassCube;

/// rotates the inner cube (first pass)
fn rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<FirstPassCube>>) {
for mut transform in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(1.5 * time.delta_seconds());
transform.rotation *= Quat::from_rotation_z(1.3 * time.delta_seconds());
}
}

/// rotates the outer cube (main pass)
fn cube_rotator_system(time: Res<Time>, mut query: Query<&mut Transform, With<MainPassCube>>) {
for mut transform in query.iter_mut() {
transform.rotation *= Quat::from_rotation_x(1.0 * time.delta_seconds());
transform.rotation *= Quat::from_rotation_y(0.7 * time.delta_seconds());
}
}

fn setup(
mut commands: Commands,
mut meshes: ResMut<Assets<Mesh>>,
mut materials: ResMut<Assets<StandardMaterial>>,
mut render_graph: ResMut<RenderGraph>,
mut active_cameras: ResMut<ActiveCameras>,
) {
let size = Extent3d::new(512, 512, 1);
add_render_to_texture_graph(&mut render_graph, size);

let cube_handle = meshes.add(Mesh::from(shape::Cube { size: 4.0 }));
let cube_material_handle = materials.add(StandardMaterial {
base_color: Color::rgb(0.8, 0.7, 0.6),
reflectance: 0.02,
roughness: 1.0,
unlit: false,
..Default::default()
});

commands
.spawn_bundle(PbrBundle {
mesh: cube_handle,
material: cube_material_handle,
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 1.0)),
..Default::default()
})
.insert(FirstPassCube)
.insert(FirstPass)
.remove::<MainPass>();

// light
// note: currently lights are shared between passes!
commands.spawn_bundle(PointLightBundle {
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 10.0)),
..Default::default()
});

// camera
let mut first_pass_camera = PerspectiveCameraBundle {
camera: Camera {
name: Some(FIRST_PASS_CAMERA.to_string()),
window: WindowId::new(), // otherwise it will use main window size / aspect for calculation of projection matrix
..Default::default()
},
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0))
.looking_at(Vec3::default(), Vec3::Y),
..Default::default()
};
active_cameras.add(FIRST_PASS_CAMERA);

let camera_projection = &mut first_pass_camera.perspective_projection;
camera_projection.update(size.width as f32, size.height as f32);
first_pass_camera.camera.projection_matrix = camera_projection.get_projection_matrix();
first_pass_camera.camera.depth_calculation = camera_projection.depth_calculation();

commands.spawn_bundle(first_pass_camera);

let texture_handle = RENDER_TEXTURE_HANDLE.typed();

let cube_size = 4.0;
let cube_handle = meshes.add(Mesh::from(shape::Box::new(cube_size, cube_size, cube_size)));

let material_handle = materials.add(StandardMaterial {
base_color_texture: Some(texture_handle),
reflectance: 0.02,
unlit: false,
..Default::default()
});

// add entities to the world
commands
.spawn_bundle(PbrBundle {
mesh: cube_handle,
material: material_handle,
transform: Transform {
translation: Vec3::new(0.0, 0.0, 1.5),
rotation: Quat::from_rotation_x(-std::f32::consts::PI / 5.0),
..Default::default()
},
visible: Visible {
is_transparent: true,
..Default::default()
},
..Default::default()
})
.insert(MainPassCube);

commands.spawn_bundle(PerspectiveCameraBundle {
transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0))
.looking_at(Vec3::default(), Vec3::Y),
..Default::default()
});
}

fn main() {
let mut app = App::build();
app.add_plugins(DefaultPlugins)
.add_startup_system(setup.system())
.add_system(cube_rotator_system.system())
.add_system(rotator_system.system())
.run();
}
1 change: 1 addition & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ Example | File | Description
`orthographic` | [`3d/orthographic.rs`](./3d/orthographic.rs) | Shows how to create a 3D orthographic view (for isometric-look games or CAD applications)
`parenting` | [`3d/parenting.rs`](./3d/parenting.rs) | Demonstrates parent->child relationships and relative transformations
`pbr` | [`3d/pbr.rs`](./3d/[pbr].rs) | Demonstrates use of Physically Based Rendering (PBR) properties
`render_to_texture` | [`3d/render_to_texture.rs`](./3d/render_to_texture.rs) | Shows how to render to texture
`spawner` | [`3d/spawner.rs`](./3d/spawner.rs) | Renders a large number of cubes with changing position and material
`texture` | [`3d/texture.rs`](./3d/texture.rs) | Shows configuration of texture materials
`update_gltf_scene` | [`3d/update_gltf_scene.rs`](./3d/update_gltf_scene.rs) | Update a scene from a gltf file, either by spawning the scene as a child of another entity, or by accessing the entities of the scene
Expand Down