Skip to main content

fullscreen_material/
fullscreen_material.rs

1//! Demonstrates how to write a custom fullscreen shader
2//!
3//! This example demonstrates working in 3d. To make the example work in 2d,
4//! replace 3d components with their 2d counterparts, and schedule the work
5//! to run in the `Core2d` schedule as described in the `FullscreenMaterial`
6//! comment in this file.
7
8use bevy::{
9    core_pipeline::fullscreen_material::{FullscreenMaterial, FullscreenMaterialPlugin},
10    prelude::*,
11    render::{extract_component::ExtractComponent, render_resource::ShaderType},
12    shader::ShaderRef,
13};
14
15fn main() {
16    App::new()
17        .add_plugins((
18            DefaultPlugins,
19            FullscreenMaterialPlugin::<FullscreenEffect>::default(),
20        ))
21        .add_systems(Startup, setup)
22        .add_systems(Update, update_intensity)
23        .run();
24}
25
26fn setup(
27    mut commands: Commands,
28    mut meshes: ResMut<Assets<Mesh>>,
29    mut materials: ResMut<Assets<StandardMaterial>>,
30) {
31    commands.spawn((
32        Camera3d::default(),
33        Transform::from_translation(Vec3::new(0.0, 0.0, 5.0)).looking_at(Vec3::default(), Vec3::Y),
34        FullscreenEffect::new(FullscreenEffect::MAX_INTENSITY),
35    ));
36
37    commands.spawn((
38        Mesh3d(meshes.add(Cuboid::default())),
39        MeshMaterial3d(materials.add(Color::srgb(0.8, 0.7, 0.6))),
40        Transform::default(),
41    ));
42
43    commands.spawn(DirectionalLight {
44        illuminance: 1_000.,
45        ..default()
46    });
47}
48
49fn update_intensity(effects: Query<&mut FullscreenEffect>, time: Res<Time>) {
50    for mut effect in effects {
51        let phase = time.elapsed_secs() * FullscreenEffect::FREQUENCY;
52        // Make it loop periodically
53        let mut intensity = ops::sin(phase);
54
55        // We need to remap the intensity to be between 0 and 1 instead of -1 and 1
56        intensity = (intensity + 1.0) / 2.0;
57        effect.intensity = intensity * FullscreenEffect::MAX_INTENSITY;
58    }
59}
60
61#[derive(Component, ExtractComponent, Clone, Copy, ShaderType, Default)]
62struct FullscreenEffect {
63    intensity: f32,
64    // WebGL2 structs must be 16 byte aligned.
65    // Intensity is an `f32`, which is 4 bytes, so 12 more bytes (3 floats) are needed.
66    #[cfg(feature = "webgl2")]
67    _webgl2_padding: Vec3,
68}
69
70impl FullscreenEffect {
71    const FREQUENCY: f32 = 2.0;
72    const MAX_INTENSITY: f32 = 0.015;
73
74    fn new(intensity: f32) -> Self {
75        Self {
76            intensity,
77            ..Default::default()
78        }
79    }
80}
81
82impl FullscreenMaterial for FullscreenEffect {
83    fn fragment_shader() -> ShaderRef {
84        "shaders/fullscreen_effect.wgsl".into()
85    }
86
87    // The `FullscreenMaterial` uses 3d schedules by default.
88    // To make this work in 2d, you would need to schedule to
89    // run in `Core2d` and in a `Core2dSystems` set.
90    //
91    // fn schedule() -> impl bevy::ecs::schedule::ScheduleLabel + Clone {
92    //     bevy::core_pipeline::Core2d
93    // }
94    // fn schedule_configs(
95    //     system: bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem>,
96    // ) -> bevy::ecs::schedule::ScheduleConfigs<bevy::ecs::system::BoxedSystem> {
97    //     system
98    //         .in_set(bevy::core_pipeline::Core2dSystems::PostProcess)
99    //         .before(bevy::core_pipeline::tonemapping::tonemapping)
100    // }
101}