From bb72d32a2965dd93693f1f836e9d4e67ea8cf409 Mon Sep 17 00:00:00 2001 From: Thom Bruce Date: Wed, 4 Oct 2023 22:45:12 +0100 Subject: [PATCH] Revert "Pixels" --- CHANGELOG.md | 4 - assets/shaders/chromatic_abberation.wgsl | 27 -- assets/shaders/pixelate.wgsl | 29 --- src/camera.rs | 17 +- src/main.rs | 1 - src/shader.rs | 317 ----------------------- 6 files changed, 3 insertions(+), 392 deletions(-) delete mode 100644 assets/shaders/chromatic_abberation.wgsl delete mode 100644 assets/shaders/pixelate.wgsl delete mode 100644 src/shader.rs diff --git a/CHANGELOG.md b/CHANGELOG.md index 7183b9c..2c3049b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,6 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added - -- Pixelation shader for retro effect - ## [0.0.10] - 2023-10-04 ### Added diff --git a/assets/shaders/chromatic_abberation.wgsl b/assets/shaders/chromatic_abberation.wgsl deleted file mode 100644 index 2311126..0000000 --- a/assets/shaders/chromatic_abberation.wgsl +++ /dev/null @@ -1,27 +0,0 @@ -#import bevy_pbr::utils -#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput - -@group(0) @binding(0) var screen_texture: texture_2d; -@group(0) @binding(1) var texture_sampler: sampler; -struct ChromaticAbberationSettings { - intensity: f32, -#ifdef SIXTEEN_BYTE_ALIGNMENT - // WebGL2 structs must be 16 byte aligned. - _webgl2_padding: vec3 -#endif -} -@group(0) @binding(2) var settings: ChromaticAbberationSettings; - -@fragment -fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { - // Chromatic aberration strength - let offset_strength = settings.intensity; - - // Sample each color channel with an arbitrary shift - return vec4( - textureSample(screen_texture, texture_sampler, in.uv + vec2(offset_strength, -offset_strength)).r, - textureSample(screen_texture, texture_sampler, in.uv + vec2(-offset_strength, 0.0)).g, - textureSample(screen_texture, texture_sampler, in.uv + vec2(0.0, offset_strength)).b, - 1.0 - ); -} diff --git a/assets/shaders/pixelate.wgsl b/assets/shaders/pixelate.wgsl deleted file mode 100644 index ad2e52f..0000000 --- a/assets/shaders/pixelate.wgsl +++ /dev/null @@ -1,29 +0,0 @@ -#import bevy_pbr::utils -#import bevy_core_pipeline::fullscreen_vertex_shader FullscreenVertexOutput - -@group(0) @binding(0) var screen_texture: texture_2d; -@group(0) @binding(1) var texture_sampler: sampler; - -struct PixelateSettings { - block_size: f32, -#ifdef SIXTEEN_BYTE_ALIGNMENT - // WebGL2 structs must be 16 byte aligned. - _webgl2_padding: vec3 -#endif -} -@group(0) @binding(2) var settings: PixelateSettings; - -@fragment -fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { - let resolution = vec2(textureDimensions(screen_texture)); - - let width_height_over_block_size = resolution / max(1.0, settings.block_size); - - var uv = in.uv + 0.5; - uv *= width_height_over_block_size; - uv = floor(uv); - uv /= width_height_over_block_size; - uv -= 0.5; - - return textureSample(screen_texture, texture_sampler, uv); -} diff --git a/src/camera.rs b/src/camera.rs index 9c11382..831f038 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,29 +1,18 @@ use bevy::prelude::*; -use crate::{ - shader::{PixelateSettings, ShaderPlugin}, - ship::Ship, - state::AppState, -}; +use crate::{ship::Ship, state::AppState}; pub struct CameraPlugin; impl Plugin for CameraPlugin { fn build(&self, app: &mut App) { - app.add_plugins(ShaderPlugin); app.add_systems(Startup, setup); app.add_systems(Update, follow_player.run_if(in_state(AppState::Active))); } } fn setup(mut commands: Commands) { - commands.spawn(( - Camera2dBundle::default(), - PixelateSettings { - block_size: 3.25, - ..default() - }, - Name::new("Main Camera"), - )); + // Spawns game camera + commands.spawn((Camera2dBundle::default(), Name::new("Main Camera"))); } pub fn follow_player( diff --git a/src/main.rs b/src/main.rs index 5b810c6..8a05258 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,7 +15,6 @@ mod orbit; mod pause; mod planet; mod planetary_system; -mod shader; mod ship; mod star; mod state; diff --git a/src/shader.rs b/src/shader.rs deleted file mode 100644 index 971ba88..0000000 --- a/src/shader.rs +++ /dev/null @@ -1,317 +0,0 @@ -/* Ported from: https://github.com/bevyengine/bevy/blob/main/examples/shader/post_processing.rs */ - -use bevy::{ - core_pipeline::{core_2d, fullscreen_vertex_shader::fullscreen_shader_vertex_state}, - ecs::query::QueryItem, - prelude::*, - render::{ - extract_component::{ - ComponentUniforms, ExtractComponent, ExtractComponentPlugin, UniformComponentPlugin, - }, - render_graph::{ - NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner, - }, - render_resource::{ - BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindGroupLayoutDescriptor, - BindGroupLayoutEntry, BindingResource, BindingType, CachedRenderPipelineId, - ColorTargetState, ColorWrites, FragmentState, MultisampleState, Operations, - PipelineCache, PrimitiveState, RenderPassColorAttachment, RenderPassDescriptor, - RenderPipelineDescriptor, Sampler, SamplerBindingType, SamplerDescriptor, ShaderStages, - ShaderType, TextureFormat, TextureSampleType, TextureViewDimension, - }, - renderer::{RenderContext, RenderDevice}, - texture::BevyDefault, - view::ViewTarget, - RenderApp, - }, -}; - -pub struct ShaderPlugin; -impl Plugin for ShaderPlugin { - fn build(&self, app: &mut App) { - app.add_plugins(PostProcessPlugin); - } -} - -/// It is generally encouraged to set up post processing effects as a plugin -struct PostProcessPlugin; - -impl Plugin for PostProcessPlugin { - fn build(&self, app: &mut App) { - app.add_plugins(( - // The settings will be a component that lives in the main world but will - // be extracted to the render world every frame. - // This makes it possible to control the effect from the main world. - // This plugin will take care of extracting it automatically. - // It's important to derive [`ExtractComponent`] on [`PostProcessingSettings`] - // for this plugin to work correctly. - ExtractComponentPlugin::::default(), - // The settings will also be the data used in the shader. - // This plugin will prepare the component for the GPU by creating a uniform buffer - // and writing the data to that buffer every frame. - UniformComponentPlugin::::default(), - )); - - // We need to get the render app from the main app - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { - return; - }; - - render_app - // Bevy's renderer uses a render graph which is a collection of nodes in a directed acyclic graph. - // It currently runs on each view/camera and executes each node in the specified order. - // It will make sure that any node that needs a dependency from another node - // only runs when that dependency is done. - // - // Each node can execute arbitrary work, but it generally runs at least one render pass. - // A node only has access to the render world, so if you need data from the main world - // you need to extract it manually or with the plugin like above. - // Add a [`Node`] to the [`RenderGraph`] - // The Node needs to impl FromWorld - // - // The [`ViewNodeRunner`] is a special [`Node`] that will automatically run the node for each view - // matching the [`ViewQuery`] - .add_render_graph_node::>( - // Specify the name of the graph, in this case we want the graph for 3d - core_2d::graph::NAME, - // It also needs the name of the node - PostProcessNode::NAME, - ) - .add_render_graph_edges( - core_2d::graph::NAME, - // Specify the node ordering. - // This will automatically create all required node edges to enforce the given ordering. - &[ - core_2d::graph::node::TONEMAPPING, - PostProcessNode::NAME, - core_2d::graph::node::END_MAIN_PASS_POST_PROCESSING, - ], - ); - } - - fn finish(&self, app: &mut App) { - // We need to get the render app from the main app - let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { - return; - }; - - render_app - // Initialize the pipeline - .init_resource::(); - } -} - -// The post process node used for the render graph -#[derive(Default)] -struct PostProcessNode; -impl PostProcessNode { - pub const NAME: &str = "post_process"; -} - -// The ViewNode trait is required by the ViewNodeRunner -impl ViewNode for PostProcessNode { - // The node needs a query to gather data from the ECS in order to do its rendering, - // but it's not a normal system so we need to define it manually. - // - // This query will only run on the view entity - type ViewQuery = &'static ViewTarget; - - // Runs the node logic - // This is where you encode draw commands. - // - // This will run on every view on which the graph is running. - // If you don't want your effect to run on every camera, - // you'll need to make sure you have a marker component as part of [`ViewQuery`] - // to identify which camera(s) should run the effect. - fn run( - &self, - _graph: &mut RenderGraphContext, - render_context: &mut RenderContext, - view_target: QueryItem, - world: &World, - ) -> Result<(), NodeRunError> { - // Get the pipeline resource that contains the global data we need - // to create the render pipeline - let post_process_pipeline = world.resource::(); - - // The pipeline cache is a cache of all previously created pipelines. - // It is required to avoid creating a new pipeline each frame, - // which is expensive due to shader compilation. - let pipeline_cache = world.resource::(); - - // Get the pipeline from the cache - let Some(pipeline) = pipeline_cache.get_render_pipeline(post_process_pipeline.pipeline_id) - else { - return Ok(()); - }; - - // Get the settings uniform binding - let settings_uniforms = world.resource::>(); - let Some(settings_binding) = settings_uniforms.uniforms().binding() else { - return Ok(()); - }; - - // This will start a new "post process write", obtaining two texture - // views from the view target - a `source` and a `destination`. - // `source` is the "current" main texture and you _must_ write into - // `destination` because calling `post_process_write()` on the - // [`ViewTarget`] will internally flip the [`ViewTarget`]'s main - // texture to the `destination` texture. Failing to do so will cause - // the current main texture information to be lost. - let post_process = view_target.post_process_write(); - - // The bind_group gets created each frame. - // - // Normally, you would create a bind_group in the Queue set, - // but this doesn't work with the post_process_write(). - // The reason it doesn't work is because each post_process_write will alternate the source/destination. - // The only way to have the correct source/destination for the bind_group - // is to make sure you get it during the node execution. - let bind_group = render_context - .render_device() - .create_bind_group(&BindGroupDescriptor { - label: Some("post_process_bind_group"), - layout: &post_process_pipeline.layout, - // It's important for this to match the BindGroupLayout defined in the PostProcessPipeline - entries: &[ - BindGroupEntry { - binding: 0, - // Make sure to use the source view - resource: BindingResource::TextureView(post_process.source), - }, - BindGroupEntry { - binding: 1, - // Use the sampler created for the pipeline - resource: BindingResource::Sampler(&post_process_pipeline.sampler), - }, - BindGroupEntry { - binding: 2, - // Set the settings binding - resource: settings_binding.clone(), - }, - ], - }); - - // Begin the render pass - let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor { - label: Some("post_process_pass"), - color_attachments: &[Some(RenderPassColorAttachment { - // We need to specify the post process destination view here - // to make sure we write to the appropriate texture. - view: post_process.destination, - resolve_target: None, - ops: Operations::default(), - })], - depth_stencil_attachment: None, - }); - - // This is mostly just wgpu boilerplate for drawing a fullscreen triangle, - // using the pipeline/bind_group created above - render_pass.set_render_pipeline(pipeline); - render_pass.set_bind_group(0, &bind_group, &[]); - render_pass.draw(0..3, 0..1); - - Ok(()) - } -} - -// This contains global data used by the render pipeline. This will be created once on startup. -#[derive(Resource)] -struct PostProcessPipeline { - layout: BindGroupLayout, - sampler: Sampler, - pipeline_id: CachedRenderPipelineId, -} - -impl FromWorld for PostProcessPipeline { - fn from_world(world: &mut World) -> Self { - let render_device = world.resource::(); - - // We need to define the bind group layout used for our pipeline - let layout = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("post_process_bind_group_layout"), - entries: &[ - // The screen texture - BindGroupLayoutEntry { - binding: 0, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Texture { - sample_type: TextureSampleType::Float { filterable: true }, - view_dimension: TextureViewDimension::D2, - multisampled: false, - }, - count: None, - }, - // The sampler that will be used to sample the screen texture - BindGroupLayoutEntry { - binding: 1, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Sampler(SamplerBindingType::Filtering), - count: None, - }, - // The settings uniform that will control the effect - BindGroupLayoutEntry { - binding: 2, - visibility: ShaderStages::FRAGMENT, - ty: BindingType::Buffer { - ty: bevy::render::render_resource::BufferBindingType::Uniform, - has_dynamic_offset: false, - min_binding_size: Some(PixelateSettings::min_size()), - }, - count: None, - }, - ], - }); - - // We can create the sampler here since it won't change at runtime and doesn't depend on the view - let sampler = render_device.create_sampler(&SamplerDescriptor::default()); - - // Get the shader handle - let shader = world - .resource::() - .load("shaders/pixelate.wgsl"); - - let pipeline_id = world - .resource_mut::() - // This will add the pipeline to the cache and queue it's creation - .queue_render_pipeline(RenderPipelineDescriptor { - label: Some("post_process_pipeline".into()), - layout: vec![layout.clone()], - // This will setup a fullscreen triangle for the vertex state - vertex: fullscreen_shader_vertex_state(), - fragment: Some(FragmentState { - shader, - shader_defs: vec![], - // Make sure this matches the entry point of your shader. - // It can be anything as long as it matches here and in the shader. - entry_point: "fragment".into(), - targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), - blend: None, - write_mask: ColorWrites::ALL, - })], - }), - // All of the following properties are not important for this effect so just use the default values. - // This struct doesn't have the Default trait implemented because not all field can have a default value. - primitive: PrimitiveState::default(), - depth_stencil: None, - multisample: MultisampleState::default(), - push_constant_ranges: vec![], - }); - - Self { - layout, - sampler, - pipeline_id, - } - } -} - -// This is the component that will get passed to the shader -#[derive(Component, Default, Clone, Copy, ExtractComponent, ShaderType)] -pub struct PixelateSettings { - pub block_size: f32, - // WebGL2 structs must be 16 byte aligned. - #[cfg(feature = "webgl2")] - _webgl2_padding: Vec3, -}