post_effects/
post-effects.rs1use mirage_engine::prelude::*;
8
9const GROUND_SIZE: f32 = 10.0;
11
12const GLOW_POSITION: Vec3 = Vec3::new(0.0, 0.6, 0.0);
14const GLOW_SIZE: f32 = 0.9;
15const GLOW_COLOR: Color = Color::rgb(4.0, 2.2, 0.6);
16
17const SPHERE_POSITIONS: [Vec3; 2] = [Vec3::new(-1.6, 0.5, 0.4), Vec3::new(1.6, 0.5, -0.4)];
19const SPHERE_SUBDIVISIONS: u32 = 3;
20
21const SUN_DIRECTION: Vec3 = Vec3::new(0.5, -1.0, -0.3);
22const SUN_COLOR: Color = Color::rgb(0.85, 0.8, 0.7);
23
24const GROUND_COLOR: Color = Color::rgb(0.16, 0.17, 0.15);
25const SPHERE_COLOR: Color = Color::rgb(0.5, 0.52, 0.55);
26
27const SCENE_BLOOM: f32 = 0.5;
29
30meshes! { enum Shape { Plane, Cube, Sphere } }
31
32#[derive(ShaderValues)]
35struct Vignette {
36 strength: f32,
37}
38
39impl PostEffect for Vignette {
40 const STAGE: EffectStage = EffectStage::ToneMapped;
41 const SHADER: &'static str = include_str!("post_effects_vignette.wgsl");
42}
43
44#[derive(ShaderValues)]
47struct Grain {
48 strength: f32,
49 seed: u32,
50}
51
52impl PostEffect for Grain {
53 const STAGE: EffectStage = EffectStage::ToneMapped;
54 const SHADER: &'static str = include_str!("post_effects_grain.wgsl");
55}
56
57#[derive(ShaderValues)]
60struct Scanlines {
61 strength: f32,
62}
63
64impl PostEffect for Scanlines {
65 const STAGE: EffectStage = EffectStage::ToneMapped;
66 const SHADER: &'static str = include_str!("post_effects_scanlines.wgsl");
67}
68
69post_effects! { enum Look { Vignette, Grain, Scanlines } }
70
71struct PostEffectEffects {
72 ticks: u32,
73 vignette: f32,
74 grain: f32,
75 scanlines: f32,
76}
77
78impl PostEffectEffects {
79 fn init(_ctx: &mut InitContext<'_, Self>) -> Result<Self, Error> {
80 Ok(Self {
81 ticks: 0,
82 vignette: 0.5,
83 grain: 0.08,
84 scanlines: 0.3,
85 })
86 }
87
88 fn draw_scene(&self, ctx: &mut FrameContext<'_, Self>) {
91 ctx.light(Light::directional(SUN_DIRECTION, SUN_COLOR).shadow());
92
93 ctx.draw(
94 Plane
95 .at(Transform::from_scale(Vec3::new(
96 GROUND_SIZE,
97 1.0,
98 GROUND_SIZE,
99 )))
100 .material(Material::lit(GROUND_COLOR)),
101 );
102 ctx.draw(
103 Cube.at(Transform::from_scale_rotation_translation(
104 Vec3::splat(GLOW_SIZE),
105 Quat::IDENTITY,
106 GLOW_POSITION,
107 ))
108 .material(Material::color(Color::BLACK).emissive(GLOW_COLOR)),
109 );
110 for position in SPHERE_POSITIONS {
111 ctx.draw(
112 Sphere {
113 subdivisions: SPHERE_SUBDIVISIONS,
114 }
115 .at(position)
116 .material(Material::lit(SPHERE_COLOR)),
117 );
118 }
119 }
120
121 fn panel(&mut self, ctx: &mut FrameContext<'_, Self>) {
125 ctx.ui(|ui| {
126 ui.add(egui::Slider::new(&mut self.vignette, 0.0..=1.0).text("vignette"));
127 ui.add(egui::Slider::new(&mut self.grain, 0.0..=1.0).text("grain"));
128 ui.add(egui::Slider::new(&mut self.scanlines, 0.0..=1.0).text("scanlines"));
129 });
130
131 ctx.set_post_effect(Vignette {
132 strength: self.vignette,
133 });
134 ctx.set_post_effect(Grain {
135 strength: self.grain,
136 seed: self.ticks,
137 });
138 ctx.set_post_effect(Scanlines {
139 strength: self.scanlines,
140 });
141 }
142}
143
144impl Game for PostEffectEffects {
145 type Meshes = Shape;
146 type Sounds = NoSounds;
147 type InputActions = Key;
148 type Skyboxes = NoSkyboxes;
149 type SurfaceStyles = NoSurfaceStyles;
150 type PostEffects = Look;
151
152 fn tick(&mut self, _ctx: &mut TickContext<'_, Self>) {
153 self.ticks += 1;
154 }
155
156 fn frame(&mut self, ctx: &mut FrameContext<'_, Self>) {
157 ctx.set_camera(Camera::new(
158 View::look_at(Vec3::new(0.0, 3.4, 4.6), Vec3::new(0.0, 0.2, 0.0)),
159 Projection::perspective(45.0),
160 ));
161 ctx.set_bloom(SCENE_BLOOM);
162
163 self.draw_scene(ctx);
164 self.panel(ctx);
165 }
166}
167
168fn main() {
169 run(
170 Config::new("Mirage: screen effects").with_size(1280, 720),
171 PostEffectEffects::init,
172 );
173}