add render_to_texture example (#1927)
Co-authored-by: Carter Anderson <mcanders1@gmail.com>
This commit is contained in:
parent
3d4b1b9ff2
commit
fa6d4dbd53
@ -153,6 +153,10 @@ path = "examples/3d/parenting.rs"
|
|||||||
name = "pbr"
|
name = "pbr"
|
||||||
path = "examples/3d/pbr.rs"
|
path = "examples/3d/pbr.rs"
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "render_to_texture"
|
||||||
|
path = "examples/3d/render_to_texture.rs"
|
||||||
|
|
||||||
[[example]]
|
[[example]]
|
||||||
name = "spawner"
|
name = "spawner"
|
||||||
path = "examples/3d/spawner.rs"
|
path = "examples/3d/spawner.rs"
|
||||||
|
@ -3,6 +3,7 @@ mod pass_node;
|
|||||||
mod render_resources_node;
|
mod render_resources_node;
|
||||||
mod shared_buffers_node;
|
mod shared_buffers_node;
|
||||||
mod texture_copy_node;
|
mod texture_copy_node;
|
||||||
|
mod texture_node;
|
||||||
mod window_swapchain_node;
|
mod window_swapchain_node;
|
||||||
mod window_texture_node;
|
mod window_texture_node;
|
||||||
|
|
||||||
@ -11,5 +12,6 @@ pub use pass_node::*;
|
|||||||
pub use render_resources_node::*;
|
pub use render_resources_node::*;
|
||||||
pub use shared_buffers_node::*;
|
pub use shared_buffers_node::*;
|
||||||
pub use texture_copy_node::*;
|
pub use texture_copy_node::*;
|
||||||
|
pub use texture_node::*;
|
||||||
pub use window_swapchain_node::*;
|
pub use window_swapchain_node::*;
|
||||||
pub use window_texture_node::*;
|
pub use window_texture_node::*;
|
||||||
|
69
crates/bevy_render/src/render_graph/nodes/texture_node.rs
Normal file
69
crates/bevy_render/src/render_graph/nodes/texture_node.rs
Normal file
@ -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
examples/3d/render_to_texture.rs
Normal file
232
examples/3d/render_to_texture.rs
Normal file
@ -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();
|
||||||
|
}
|
@ -91,6 +91,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)
|
`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
|
`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
|
`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
|
`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
|
`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
|
`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
|
||||||
|
Loading…
Reference in New Issue
Block a user