From ae7c3c0f7fc39b7808e5dabecec67c17bd439689 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?charlotte=20=F0=9F=8C=B8?= Date: Sat, 27 Dec 2025 14:06:22 -0800 Subject: [PATCH 1/2] Correctly implement background fullscreen quad. --- crates/processing_render/src/render/mod.rs | 133 +++++++++++++++------ examples/animated_mesh.rs | 4 + examples/box.rs | 5 + examples/custom_attribute.rs | 4 + 4 files changed, 111 insertions(+), 35 deletions(-) diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index abe34eb..b509d6f 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -5,16 +5,17 @@ pub mod primitive; pub mod transform; use bevy::{ - camera::visibility::RenderLayers, ecs::system::SystemParam, math::Affine3A, prelude::*, + camera::visibility::RenderLayers, + ecs::system::SystemParam, + math::{Affine3A, Mat4, Vec4}, + prelude::*, }; use command::{CommandBuffer, DrawCommand}; use material::MaterialKey; use primitive::{TessellationMode, empty_mesh}; use transform::TransformStack; -use crate::{ - Flush, geometry::Geometry, graphics::SurfaceSize, image::Image, render::primitive::rect, -}; +use crate::{Flush, geometry::Geometry, image::Image, render::primitive::rect}; #[derive(Component)] #[relationship(relationship_target = TransientMeshes)] @@ -94,16 +95,26 @@ pub fn flush_draw_commands( &mut CommandBuffer, &mut RenderState, &RenderLayers, - &SurfaceSize, + &Projection, + &Transform, ), With, >, p_images: Query<&Image>, p_geometries: Query<&Geometry>, ) { - for (graphics_entity, mut cmd_buffer, mut state, render_layers, SurfaceSize(width, height)) in - graphics.iter_mut() + for ( + graphics_entity, + mut cmd_buffer, + mut state, + render_layers, + projection, + camera_transform, + ) in graphics.iter_mut() { + let clip_from_view = projection.get_clip_from_view(); + let view_from_world = camera_transform.to_matrix().inverse(); + let world_from_clip = (clip_from_view * view_from_world).inverse(); let draw_commands = std::mem::take(&mut cmd_buffer.commands); let mut batch = BatchState::new(graphics_entity, render_layers.clone()); @@ -143,18 +154,26 @@ pub fn flush_draw_commands( }); } DrawCommand::BackgroundColor(color) => { - add_fill(&mut res, &mut batch, &state, |mesh, _| { - rect( - mesh, - 0.0, - 0.0, - *width as f32, - *height as f32, - [0.0; 4], - color, - TessellationMode::Fill, - ) - }); + flush_batch(&mut res, &mut batch); + + let mesh = create_ndc_background_quad(world_from_clip, color, false); + let mesh_handle = res.meshes.add(mesh); + + let material_key = MaterialKey { + transparent: color.alpha() < 1.0, + background_image: None, + }; + let material_handle = res.materials.add(material_key.to_material()); + + res.commands.spawn(( + Mesh3d(mesh_handle), + MeshMaterial3d(material_handle), + BelongsToGraphics(batch.graphics_entity), + Transform::IDENTITY, + batch.render_layers.clone(), + )); + + batch.draw_index += 1; } DrawCommand::BackgroundImage(entity) => { let Some(p_image) = p_images.get(entity).ok() else { @@ -164,28 +183,24 @@ pub fn flush_draw_commands( flush_batch(&mut res, &mut batch); + let mesh = create_ndc_background_quad(world_from_clip, Color::WHITE, true); + let mesh_handle = res.meshes.add(mesh); + let material_key = MaterialKey { transparent: false, background_image: Some(p_image.handle.clone()), }; + let material_handle = res.materials.add(material_key.to_material()); - batch.material_key = Some(material_key); - batch.current_mesh = Some(empty_mesh()); - - if let Some(ref mut mesh) = batch.current_mesh { - rect( - mesh, - 0.0, - 0.0, - *width as f32, - *height as f32, - [0.0; 4], - Color::WHITE, - TessellationMode::Fill, - ) - } + res.commands.spawn(( + Mesh3d(mesh_handle), + MeshMaterial3d(material_handle), + BelongsToGraphics(batch.graphics_entity), + Transform::IDENTITY, + batch.render_layers.clone(), + )); - flush_batch(&mut res, &mut batch); + batch.draw_index += 1; } DrawCommand::PushMatrix => state.transform.push(), DrawCommand::PopMatrix => state.transform.pop(), @@ -348,3 +363,51 @@ fn flush_batch(res: &mut RenderResources, batch: &mut BatchState) { } batch.material_key = None; } + +/// Creates a fullscreen quad by transforming NDC fullscreen by inverse of the clip-from-world matrix +/// so that when the vertex shader applies clip_from_world, the vertices end up correctly back in +/// NDC space. +fn create_ndc_background_quad(world_from_clip: Mat4, color: Color, with_uvs: bool) -> Mesh { + use bevy::asset::RenderAssetUsages; + use bevy::mesh::{Indices, PrimitiveTopology}; + + let ndc_z = 0.001; // near far plane (bevy uses reverse-z) + let ndc_corners = [ + Vec4::new(-1.0, -1.0, ndc_z, 1.0), // bl + Vec4::new(1.0, -1.0, ndc_z, 1.0), // br + Vec4::new(1.0, 1.0, ndc_z, 1.0), // tr + Vec4::new(-1.0, 1.0, ndc_z, 1.0), // tl + ]; + + let world_positions: Vec<[f32; 3]> = ndc_corners + .iter() + .map(|ndc| { + let world = world_from_clip * *ndc; + [world.x / world.w, world.y / world.w, world.z / world.w] + }) + .collect(); + + let uvs: Vec<[f32; 2]> = vec![ + [0.0, 1.0], // bl + [1.0, 1.0], // br + [1.0, 0.0], // tr + [0.0, 0.0], // tl + ]; + + let color_array: [f32; 4] = color.to_linear().to_f32_array(); + let colors: Vec<[f32; 4]> = vec![color_array; 4]; + + // two tris + let indices: Vec = vec![0, 1, 2, 0, 2, 3]; + + let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()); + + mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, world_positions); + mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors); + if with_uvs { + mesh.insert_attribute(Mesh::ATTRIBUTE_UV_0, uvs); + } + mesh.insert_indices(Indices::U32(indices)); + + mesh +} diff --git a/examples/animated_mesh.rs b/examples/animated_mesh.rs index 5d75d1c..4245229 100644 --- a/examples/animated_mesh.rs +++ b/examples/animated_mesh.rs @@ -86,6 +86,10 @@ fn sketch() -> error::Result<()> { } graphics_begin_draw(graphics)?; + graphics_record_command( + graphics, + DrawCommand::BackgroundColor(bevy::color::Color::srgb(0.05, 0.05, 0.1)), + )?; graphics_record_command(graphics, DrawCommand::Geometry(mesh))?; graphics_end_draw(graphics)?; diff --git a/examples/box.rs b/examples/box.rs index acc542e..c1a79fb 100644 --- a/examples/box.rs +++ b/examples/box.rs @@ -38,6 +38,11 @@ fn sketch() -> error::Result<()> { while glfw_ctx.poll_events() { graphics_begin_draw(graphics)?; + graphics_record_command( + graphics, + DrawCommand::BackgroundColor(bevy::color::Color::srgb(0.1, 0.1, 0.15)), + )?; + graphics_record_command(graphics, DrawCommand::PushMatrix)?; graphics_record_command(graphics, DrawCommand::Rotate { angle })?; graphics_record_command(graphics, DrawCommand::Geometry(box_geo))?; diff --git a/examples/custom_attribute.rs b/examples/custom_attribute.rs index a900267..ccdf412 100644 --- a/examples/custom_attribute.rs +++ b/examples/custom_attribute.rs @@ -63,6 +63,10 @@ fn sketch() -> error::Result<()> { while glfw_ctx.poll_events() { graphics_begin_draw(graphics)?; + graphics_record_command( + graphics, + DrawCommand::BackgroundColor(bevy::color::Color::srgb(0.1, 0.1, 0.12)), + )?; graphics_record_command(graphics, DrawCommand::Geometry(mesh))?; graphics_end_draw(graphics)?; } From ba93e380818b9239d141e97c03c108e26f445b16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?charlotte=20=F0=9F=8C=B8?= Date: Sat, 27 Dec 2025 14:53:46 -0800 Subject: [PATCH 2/2] Fmt. --- crates/processing_render/src/render/mod.rs | 15 ++++++--------- 1 file changed, 6 insertions(+), 9 deletions(-) diff --git a/crates/processing_render/src/render/mod.rs b/crates/processing_render/src/render/mod.rs index b509d6f..7871f14 100644 --- a/crates/processing_render/src/render/mod.rs +++ b/crates/processing_render/src/render/mod.rs @@ -103,14 +103,8 @@ pub fn flush_draw_commands( p_images: Query<&Image>, p_geometries: Query<&Geometry>, ) { - for ( - graphics_entity, - mut cmd_buffer, - mut state, - render_layers, - projection, - camera_transform, - ) in graphics.iter_mut() + for (graphics_entity, mut cmd_buffer, mut state, render_layers, projection, camera_transform) in + graphics.iter_mut() { let clip_from_view = projection.get_clip_from_view(); let view_from_world = camera_transform.to_matrix().inverse(); @@ -400,7 +394,10 @@ fn create_ndc_background_quad(world_from_clip: Mat4, color: Color, with_uvs: boo // two tris let indices: Vec = vec![0, 1, 2, 0, 2, 3]; - let mut mesh = Mesh::new(PrimitiveTopology::TriangleList, RenderAssetUsages::default()); + let mut mesh = Mesh::new( + PrimitiveTopology::TriangleList, + RenderAssetUsages::default(), + ); mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, world_positions); mesh.insert_attribute(Mesh::ATTRIBUTE_COLOR, colors);