diff --git a/assets/shaders/custom_material_chromatic_aberration.wgsl b/assets/shaders/custom_material_chromatic_aberration.wgsl index e8ccdcfb62513..2c93409d635a4 100644 --- a/assets/shaders/custom_material_chromatic_aberration.wgsl +++ b/assets/shaders/custom_material_chromatic_aberration.wgsl @@ -6,6 +6,13 @@ var texture: texture_2d; @group(1) @binding(1) var our_sampler: sampler; +@group(1) @binding(2) +var offset_r: vec2; +@group(1) @binding(3) +var offset_g: vec2; +@group(1) @binding(4) +var offset_b: vec2; + @fragment fn fragment( @builtin(position) position: vec4, @@ -13,15 +20,14 @@ fn fragment( ) -> @location(0) vec4 { // Get screen position with coordinates from 0 to 1 let uv = position.xy / vec2(view.width, view.height); - let offset_strength = 0.02; // Sample each color channel with an arbitrary shift var output_color = vec4( - textureSample(texture, our_sampler, uv + vec2(offset_strength, -offset_strength)).r, - textureSample(texture, our_sampler, uv + vec2(-offset_strength, 0.0)).g, - textureSample(texture, our_sampler, uv + vec2(0.0, offset_strength)).b, + textureSample(texture, our_sampler, uv + offset_r).r, + textureSample(texture, our_sampler, uv + offset_g).g, + textureSample(texture, our_sampler, uv + offset_b).b, 1.0 - ); + ); return output_color; } diff --git a/assets/shaders/screen_vertex.wgsl b/assets/shaders/screen_vertex.wgsl new file mode 100644 index 0000000000000..deec73fb7f82a --- /dev/null +++ b/assets/shaders/screen_vertex.wgsl @@ -0,0 +1,23 @@ +#import bevy_sprite::mesh2d_view_bindings +#import bevy_sprite::mesh2d_bindings + +// NOTE: Bindings must come before functions that use them! +#import bevy_sprite::mesh2d_functions + +struct Vertex { + @location(0) position: vec3, + @location(2) uv: vec2, +}; + +struct VertexOutput { + @builtin(position) clip_position: vec4, + #import bevy_sprite::mesh2d_vertex_output +} + +@vertex +fn vertex(vertex: Vertex) -> VertexOutput { + var out: VertexOutput; + out.uv = vertex.uv; + out.clip_position = vec4((out.uv - vec2(0.5, 0.5)) * 2.0, 0.0, 1.0); + return out; +} \ No newline at end of file diff --git a/examples/3d/render_to_texture.rs b/examples/3d/render_to_texture.rs index 801f44f3caead..632942e442c6b 100644 --- a/examples/3d/render_to_texture.rs +++ b/examples/3d/render_to_texture.rs @@ -10,6 +10,7 @@ use bevy::{ render_resource::{ Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, }, + texture::BevyDefault, view::RenderLayers, }, }; @@ -49,7 +50,7 @@ fn setup( label: None, size, dimension: TextureDimension::D2, - format: TextureFormat::Bgra8UnormSrgb, + format: TextureFormat::bevy_default(), mip_level_count: 1, sample_count: 1, usage: TextureUsages::TEXTURE_BINDING diff --git a/examples/shader/post_processing.rs b/examples/shader/post_processing.rs index efe6d6fd7d814..67b62b20de5cb 100644 --- a/examples/shader/post_processing.rs +++ b/examples/shader/post_processing.rs @@ -3,26 +3,13 @@ //! This example is useful to implement your own post-processing effect such as //! edge detection, blur, pixelization, vignette... and countless others. -use bevy::{ - core_pipeline::clear_color::ClearColorConfig, - prelude::*, - reflect::TypeUuid, - render::{ - camera::{Camera, RenderTarget}, - render_resource::{ - AsBindGroup, Extent3d, ShaderRef, TextureDescriptor, TextureDimension, TextureFormat, - TextureUsages, - }, - texture::BevyDefault, - view::RenderLayers, - }, - sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle}, -}; +use bevy::{core_pipeline::clear_color::ClearColorConfig, prelude::*}; +use post_processing::PostProcessingCamera; fn main() { let mut app = App::new(); app.add_plugins(DefaultPlugins) - .add_plugin(Material2dPlugin::::default()) + .add_plugin(post_processing::PostProcessingPlugin) .add_startup_system(setup) .add_system(main_camera_cube_rotator_system); @@ -35,43 +22,12 @@ struct MainCube; fn setup( mut commands: Commands, - mut windows: ResMut, mut meshes: ResMut>, - mut post_processing_materials: ResMut>, mut materials: ResMut>, - mut images: ResMut>, asset_server: Res, ) { asset_server.watch_for_changes().unwrap(); - let window = windows.get_primary_mut().unwrap(); - let size = Extent3d { - width: window.physical_width(), - height: window.physical_height(), - ..default() - }; - - // This is the texture that will be rendered to. - let mut image = Image { - texture_descriptor: TextureDescriptor { - label: None, - size, - dimension: TextureDimension::D2, - format: TextureFormat::bevy_default(), - mip_level_count: 1, - sample_count: 1, - usage: TextureUsages::TEXTURE_BINDING - | TextureUsages::COPY_DST - | TextureUsages::RENDER_ATTACHMENT, - }, - ..default() - }; - - // fill image.data with zeroes - image.resize(size); - - let image_handle = images.add(image); - 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), @@ -98,57 +54,17 @@ fn setup( }); // Main camera, first to render - commands.spawn_bundle(Camera3dBundle { - camera_3d: Camera3d { - clear_color: ClearColorConfig::Custom(Color::WHITE), - ..default() - }, - camera: Camera { - target: RenderTarget::Image(image_handle.clone()), - ..default() - }, - transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) - .looking_at(Vec3::default(), Vec3::Y), - ..default() - }); - - // This specifies the layer used for the post processing camera, which will be attached to the post processing camera and 2d quad. - let post_processing_pass_layer = RenderLayers::layer((RenderLayers::TOTAL_LAYERS - 1) as u8); - - let quad_handle = meshes.add(Mesh::from(shape::Quad::new(Vec2::new( - size.width as f32, - size.height as f32, - )))); - - // This material has the texture that has been rendered. - let material_handle = post_processing_materials.add(PostProcessingMaterial { - source_image: image_handle, - }); - - // Post processing 2d quad, with material using the render texture done by the main camera, with a custom shader. commands - .spawn_bundle(MaterialMesh2dBundle { - mesh: quad_handle.into(), - material: material_handle, - transform: Transform { - translation: Vec3::new(0.0, 0.0, 1.5), + .spawn_bundle(Camera3dBundle { + camera_3d: Camera3d { + clear_color: ClearColorConfig::Custom(Color::WHITE), ..default() }, + transform: Transform::from_translation(Vec3::new(0.0, 0.0, 15.0)) + .looking_at(Vec3::default(), Vec3::Y), ..default() }) - .insert(post_processing_pass_layer); - - // The post-processing pass camera. - commands - .spawn_bundle(Camera2dBundle { - camera: Camera { - // renders after the first main camera which has default value: 0. - priority: 1, - ..default() - }, - ..Camera2dBundle::default() - }) - .insert(post_processing_pass_layer); + .insert(PostProcessingCamera); } /// Rotates the cube rendered by the main camera @@ -162,20 +78,254 @@ fn main_camera_cube_rotator_system( } } -// Region below declares of the custom material handling post processing effect +mod post_processing { + use bevy::{ + prelude::*, + reflect::TypeUuid, + render::{ + camera::RenderTarget, + mesh::Indices, + render_resource::{ + AsBindGroup, Extent3d, PrimitiveTopology, ShaderRef, TextureDescriptor, + TextureDimension, TextureFormat, TextureUsages, + }, + texture::BevyDefault, + view::RenderLayers, + }, + sprite::{Material2d, Material2dPlugin, MaterialMesh2dBundle}, + window::{WindowId, WindowResized}, + }; + + pub struct PostProcessingPlugin; -/// Our custom post processing material -#[derive(AsBindGroup, TypeUuid, Clone)] -#[uuid = "bc2f08eb-a0fb-43f1-a908-54871ea597d5"] -struct PostProcessingMaterial { - /// In this example, this image will be the result of the main camera. - #[texture(0)] - #[sampler(1)] - source_image: Handle, -} + impl Plugin for PostProcessingPlugin { + fn build(&self, app: &mut App) { + app.add_plugin(Material2dPlugin::::default()) + .add_system(setup_new_post_processing_cameras) + .add_system(update_image_to_window_size) + .add_system(update_material); + } + } + + /// To support window resizing, this fits an image to a windows size. + #[derive(Component)] + struct FitToWindowSize { + image: Handle, + material: Handle, + window_id: WindowId, + } + #[derive(Component)] + pub struct PostProcessingCamera; + + /// Update image size to fit window + fn update_image_to_window_size( + windows: Res, + mut image_events: EventWriter>, + mut images: ResMut>, + mut post_processing_materials: ResMut>, + mut resize_events: EventReader, + fit_to_window_size: Query<&FitToWindowSize>, + ) { + for resize_event in resize_events.iter() { + for fit_to_window in fit_to_window_size.iter() { + if resize_event.id == fit_to_window.window_id { + let size = { + let window = windows.get(fit_to_window.window_id).expect("PostProcessingCamera is rendering to a window, but this window could not be found"); + Extent3d { + width: window.physical_width(), + height: window.physical_height(), + ..Default::default() + } + }; + let image = images.get_mut(&fit_to_window.image).expect( + "FitToWindowSize is referring to an Image, but this Image could not be found", + ); + info!("resize to {:?}", size); + image.resize(size); + // Hack because of https://github.com/bevyengine/bevy/issues/5595 + image_events.send(AssetEvent::Modified { + handle: fit_to_window.image.clone(), + }); + post_processing_materials.get_mut(&fit_to_window.material); + } + } + } + } + + fn update_material( + time: Res