1use crate::{
2 light::{LightNode, LightPlugin},
3 mesh_material::MeshMaterialPlugin,
4 overlay::{OverlayNode, OverlayPlugin},
5 post_process::{PostProcessNode, PostProcessPlugin},
6 prepass::{PrepassNode, PrepassPlugin},
7 transform::TransformPlugin,
8 view::ViewPlugin,
9};
10use bevy::{
11 asset::{load_internal_asset, load_internal_binary_asset},
12 core_pipeline::{
13 bloom::BloomNode, fxaa::FxaaNode, tonemapping::TonemappingNode, upscaling::UpscalingNode,
14 },
15 ecs::query::QueryItem,
16 prelude::*,
17 reflect::TypeUuid,
18 render::{
19 extract_component::{ExtractComponent, ExtractComponentPlugin},
20 extract_resource::{ExtractResource, ExtractResourcePlugin},
21 render_asset::RenderAssets,
22 render_graph::{RenderGraph, SlotInfo, SlotType},
23 render_resource::*,
24 renderer::RenderDevice,
25 texture::{CompressedImageFormats, FallbackImage, ImageType},
26 RenderApp,
27 },
28};
29use std::num::NonZeroU32;
30
31#[macro_use]
32extern crate num_derive;
33
34pub mod light;
35pub mod mesh_material;
36pub mod overlay;
37pub mod post_process;
38pub mod prelude;
39pub mod prepass;
40pub mod transform;
41pub mod view;
42
43pub mod graph {
44 pub const NAME: &str = "hikari";
45 pub mod node {
46 pub const PREPASS: &str = "hikari_prepass";
47 pub const LIGHT: &str = "hikari_light";
48 pub const POST_PROCESS: &str = "hikari_post_process";
49 pub const OVERLAY: &str = "hikari_overlay";
50 }
51}
52
53pub const WORKGROUP_SIZE: u32 = 8;
54pub const NOISE_TEXTURE_COUNT: usize = 16;
55
56pub const UTILS_SHADER_HANDLE: HandleUntyped =
57 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4462033275253590181);
58pub const MESH_VIEW_TYPES_SHADER_HANDLE: HandleUntyped =
59 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 10086770709483722043);
60pub const MESH_VIEW_BINDINGS_SHADER_HANDLE: HandleUntyped =
61 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 8835349515886344623);
62pub const MESH_MATERIAL_TYPES_SHADER_HANDLE: HandleUntyped =
63 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 15819591594687298858);
64pub const MESH_MATERIAL_BINDINGS_SHADER_HANDLE: HandleUntyped =
65 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 5025976374517268);
66pub const DEFERRED_BINDINGS_SHADER_HANDLE: HandleUntyped =
67 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 14467895678105108252);
68pub const RESERVOIR_TYPES_SHADER_HANDLE: HandleUntyped =
69 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7770589395703787378);
70pub const RESERVOIR_BINDINGS_SHADER_HANDLE: HandleUntyped =
71 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 11658053183743104810);
72pub const RESERVOIR_FUNCTIONS_SHADER_HANDLE: HandleUntyped =
73 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7650021494161056224);
74pub const PREPASS_SHADER_HANDLE: HandleUntyped =
75 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4693612430004931427);
76pub const LIGHT_SHADER_HANDLE: HandleUntyped =
77 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 9657319286592943583);
78pub const DENOISE_SHADER_HANDLE: HandleUntyped =
79 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 5179661212363325472);
80pub const TONE_MAPPING_SHADER_HANDLE: HandleUntyped =
81 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3567017338952956671);
82pub const TAA_SHADER_HANDLE: HandleUntyped =
83 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 1780446804546284);
84pub const SMAA_SHADER_HANDLE: HandleUntyped =
85 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 3793959332758430953);
86pub const FSR1_EASU_SHADER_HANDLE: HandleUntyped =
87 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 11823787237582686663);
88pub const FSR1_RCAS_SHADER_HANDLE: HandleUntyped =
89 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 17003547378277520107);
90pub const OVERLAY_SHADER_HANDLE: HandleUntyped =
91 HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 10969344919103020615);
92pub const QUAD_MESH_HANDLE: HandleUntyped =
93 HandleUntyped::weak_from_u64(Mesh::TYPE_UUID, 4740146776519512271);
94
95pub struct HikariPlugin;
96impl Plugin for HikariPlugin {
97 fn build(&self, app: &mut App) {
98 load_internal_asset!(
99 app,
100 UTILS_SHADER_HANDLE,
101 "shaders/utils.wgsl",
102 Shader::from_wgsl
103 );
104 load_internal_asset!(
105 app,
106 MESH_VIEW_TYPES_SHADER_HANDLE,
107 "shaders/mesh_view_types.wgsl",
108 Shader::from_wgsl
109 );
110 load_internal_asset!(
111 app,
112 MESH_VIEW_BINDINGS_SHADER_HANDLE,
113 "shaders/mesh_view_bindings.wgsl",
114 Shader::from_wgsl
115 );
116 load_internal_asset!(
117 app,
118 MESH_MATERIAL_TYPES_SHADER_HANDLE,
119 "shaders/mesh_material_types.wgsl",
120 Shader::from_wgsl
121 );
122 load_internal_asset!(
123 app,
124 MESH_MATERIAL_BINDINGS_SHADER_HANDLE,
125 "shaders/mesh_material_bindings.wgsl",
126 Shader::from_wgsl
127 );
128 load_internal_asset!(
129 app,
130 DEFERRED_BINDINGS_SHADER_HANDLE,
131 "shaders/deferred_bindings.wgsl",
132 Shader::from_wgsl
133 );
134 load_internal_asset!(
135 app,
136 PREPASS_SHADER_HANDLE,
137 "shaders/prepass.wgsl",
138 Shader::from_wgsl
139 );
140 load_internal_asset!(
141 app,
142 LIGHT_SHADER_HANDLE,
143 "shaders/light.wgsl",
144 Shader::from_wgsl
145 );
146 load_internal_asset!(
147 app,
148 DENOISE_SHADER_HANDLE,
149 "shaders/denoise.wgsl",
150 Shader::from_wgsl
151 );
152 load_internal_asset!(
153 app,
154 TONE_MAPPING_SHADER_HANDLE,
155 "shaders/tone_mapping.wgsl",
156 Shader::from_wgsl
157 );
158 load_internal_asset!(
159 app,
160 TAA_SHADER_HANDLE,
161 "shaders/taa.wgsl",
162 Shader::from_wgsl
163 );
164 load_internal_asset!(
165 app,
166 SMAA_SHADER_HANDLE,
167 "shaders/smaa.wgsl",
168 Shader::from_wgsl
169 );
170 load_internal_asset!(
171 app,
172 OVERLAY_SHADER_HANDLE,
173 "shaders/overlay.wgsl",
174 Shader::from_wgsl
175 );
176 load_internal_binary_asset!(
177 app,
178 FSR1_EASU_SHADER_HANDLE,
179 "shaders/fsr/fsr_pass_easu.spv",
180 Shader::from_spirv
181 );
182 load_internal_binary_asset!(
183 app,
184 FSR1_RCAS_SHADER_HANDLE,
185 "shaders/fsr/fsr_pass_rcas.spv",
186 Shader::from_spirv
187 );
188
189 let noise_load_system = move |mut commands: Commands, mut images: ResMut<Assets<Image>>| {
190 let bytes = [
191 include_bytes!("noise/LDR_RGBA_0.png"),
192 include_bytes!("noise/LDR_RGBA_1.png"),
193 include_bytes!("noise/LDR_RGBA_2.png"),
194 include_bytes!("noise/LDR_RGBA_3.png"),
195 include_bytes!("noise/LDR_RGBA_4.png"),
196 include_bytes!("noise/LDR_RGBA_5.png"),
197 include_bytes!("noise/LDR_RGBA_6.png"),
198 include_bytes!("noise/LDR_RGBA_7.png"),
199 include_bytes!("noise/LDR_RGBA_8.png"),
200 include_bytes!("noise/LDR_RGBA_9.png"),
201 include_bytes!("noise/LDR_RGBA_10.png"),
202 include_bytes!("noise/LDR_RGBA_11.png"),
203 include_bytes!("noise/LDR_RGBA_12.png"),
204 include_bytes!("noise/LDR_RGBA_13.png"),
205 include_bytes!("noise/LDR_RGBA_14.png"),
206 include_bytes!("noise/LDR_RGBA_15.png"),
207 ];
208 let handles = Vec::from(bytes.map(|buffer| {
209 let image = Image::from_buffer(
210 buffer,
211 ImageType::Extension("png"),
212 CompressedImageFormats::NONE,
213 false,
214 )
215 .unwrap();
216 images.add(image)
217 }));
218 commands.insert_resource(NoiseTextures(handles));
219 };
220
221 app.register_type::<HikariUniversalSettings>()
222 .register_type::<HikariSettings>()
223 .register_type::<Taa>()
224 .register_type::<Upscale>()
225 .init_resource::<HikariUniversalSettings>()
226 .add_plugin(ExtractResourcePlugin::<NoiseTextures>::default())
227 .add_plugin(ExtractResourcePlugin::<HikariUniversalSettings>::default())
228 .add_plugin(ExtractComponentPlugin::<HikariSettings>::default())
229 .add_plugin(TransformPlugin)
230 .add_plugin(ViewPlugin)
231 .add_plugin(MeshMaterialPlugin)
232 .add_plugin(PrepassPlugin)
233 .add_plugin(LightPlugin)
234 .add_plugin(PostProcessPlugin)
235 .add_plugin(OverlayPlugin)
236 .add_startup_system(noise_load_system);
237
238 if let Ok(render_app) = app.get_sub_app_mut(RenderApp) {
239 use bevy::core_pipeline::core_3d;
240
241 let prepass_node = PrepassNode::new(&mut render_app.world);
242 let light_node = LightNode::new(&mut render_app.world);
243 let post_process_node = PostProcessNode::new(&mut render_app.world);
244 let overlay_node = OverlayNode::new(&mut render_app.world);
245 let bloom_node = BloomNode::new(&mut render_app.world);
246 let tonemapping_node = TonemappingNode::new(&mut render_app.world);
247 let fxaa_node = FxaaNode::new(&mut render_app.world);
248 let upscaling_node = UpscalingNode::new(&mut render_app.world);
249
250 let mut graph = render_app.world.resource_mut::<RenderGraph>();
251
252 let mut sub_graph = RenderGraph::default();
253 sub_graph.set_input(vec![SlotInfo::new(
254 core_3d::graph::input::VIEW_ENTITY,
255 SlotType::Entity,
256 )]);
257
258 sub_graph.add_node(graph::node::PREPASS, prepass_node);
259 sub_graph.add_node(graph::node::LIGHT, light_node);
260 sub_graph.add_node(graph::node::POST_PROCESS, post_process_node);
261 sub_graph.add_node(graph::node::OVERLAY, overlay_node);
262 sub_graph.add_node(core_3d::graph::node::BLOOM, bloom_node);
263 sub_graph.add_node(core_3d::graph::node::TONEMAPPING, tonemapping_node);
264 sub_graph.add_node(core_3d::graph::node::FXAA, fxaa_node);
265 sub_graph.add_node(core_3d::graph::node::UPSCALING, upscaling_node);
266
267 sub_graph
268 .add_slot_edge(
269 sub_graph.input_node().unwrap().id,
270 core_3d::graph::input::VIEW_ENTITY,
271 graph::node::PREPASS,
272 PrepassNode::IN_VIEW,
273 )
274 .unwrap();
275 sub_graph
276 .add_slot_edge(
277 sub_graph.input_node().unwrap().id,
278 core_3d::graph::input::VIEW_ENTITY,
279 graph::node::LIGHT,
280 LightNode::IN_VIEW,
281 )
282 .unwrap();
283 sub_graph
284 .add_slot_edge(
285 sub_graph.input_node().unwrap().id,
286 core_3d::graph::input::VIEW_ENTITY,
287 graph::node::POST_PROCESS,
288 PostProcessNode::IN_VIEW,
289 )
290 .unwrap();
291 sub_graph
292 .add_slot_edge(
293 sub_graph.input_node().unwrap().id,
294 core_3d::graph::input::VIEW_ENTITY,
295 graph::node::OVERLAY,
296 OverlayNode::IN_VIEW,
297 )
298 .unwrap();
299 sub_graph
300 .add_slot_edge(
301 sub_graph.input_node().unwrap().id,
302 core_3d::graph::input::VIEW_ENTITY,
303 core_3d::graph::node::BLOOM,
304 BloomNode::IN_VIEW,
305 )
306 .unwrap();
307 sub_graph
308 .add_slot_edge(
309 sub_graph.input_node().unwrap().id,
310 core_3d::graph::input::VIEW_ENTITY,
311 core_3d::graph::node::TONEMAPPING,
312 TonemappingNode::IN_VIEW,
313 )
314 .unwrap();
315 sub_graph
316 .add_slot_edge(
317 sub_graph.input_node().unwrap().id,
318 core_3d::graph::input::VIEW_ENTITY,
319 core_3d::graph::node::FXAA,
320 FxaaNode::IN_VIEW,
321 )
322 .unwrap();
323 sub_graph
324 .add_slot_edge(
325 sub_graph.input_node().unwrap().id,
326 core_3d::graph::input::VIEW_ENTITY,
327 core_3d::graph::node::UPSCALING,
328 UpscalingNode::IN_VIEW,
329 )
330 .unwrap();
331
332 sub_graph
334 .add_node_edge(graph::node::PREPASS, graph::node::LIGHT)
335 .unwrap();
336 sub_graph
337 .add_node_edge(graph::node::LIGHT, graph::node::POST_PROCESS)
338 .unwrap();
339 sub_graph
340 .add_node_edge(graph::node::POST_PROCESS, graph::node::OVERLAY)
341 .unwrap();
342 sub_graph
343 .add_node_edge(
344 graph::node::OVERLAY,
345 bevy::core_pipeline::core_3d::graph::node::BLOOM,
346 )
347 .unwrap();
348 sub_graph
349 .add_node_edge(
350 bevy::core_pipeline::core_3d::graph::node::BLOOM,
351 bevy::core_pipeline::core_3d::graph::node::TONEMAPPING,
352 )
353 .unwrap();
354 sub_graph
355 .add_node_edge(
356 bevy::core_pipeline::core_3d::graph::node::TONEMAPPING,
357 bevy::core_pipeline::core_3d::graph::node::FXAA,
358 )
359 .unwrap();
360 sub_graph
361 .add_node_edge(
362 bevy::core_pipeline::core_3d::graph::node::FXAA,
363 bevy::core_pipeline::core_3d::graph::node::UPSCALING,
364 )
365 .unwrap();
366
367 graph.add_sub_graph(graph::NAME, sub_graph);
368 }
369 }
370}
371
372#[derive(Debug, Clone, Resource, Reflect)]
374#[reflect(Resource)]
375pub struct HikariUniversalSettings {
376 pub build_mesh_acceleration_structure: bool,
378 pub build_instance_acceleration_structure: bool,
380}
381
382impl Default for HikariUniversalSettings {
383 fn default() -> Self {
384 Self {
385 build_mesh_acceleration_structure: true,
386 build_instance_acceleration_structure: true,
387 }
388 }
389}
390
391impl ExtractResource for HikariUniversalSettings {
392 type Source = Self;
393
394 fn extract_resource(source: &Self::Source) -> Self {
395 source.clone()
396 }
397}
398
399#[derive(Debug, Clone, Component, Reflect)]
401#[reflect(Component)]
402pub struct HikariSettings {
403 pub direct_validate_interval: usize,
405 pub emissive_validate_interval: usize,
407 pub max_temporal_reuse_count: usize,
409 pub max_spatial_reuse_count: usize,
411 pub max_reservoir_lifetime: f32,
413 pub solar_angle: f32,
415 pub indirect_bounces: usize,
417 pub max_indirect_luminance: f32,
419 pub clear_color: Color,
421 pub temporal_reuse: bool,
423 pub emissive_spatial_reuse: bool,
425 pub indirect_spatial_reuse: bool,
427 pub denoise: bool,
429 pub taa: Taa,
431 pub upscale: Upscale,
433}
434
435impl Default for HikariSettings {
436 fn default() -> Self {
437 Self {
438 direct_validate_interval: 3,
439 emissive_validate_interval: 5,
440 max_temporal_reuse_count: 50,
441 max_spatial_reuse_count: 800,
442 max_reservoir_lifetime: 100.0,
443 solar_angle: 0.046,
444 clear_color: Color::rgb(0.4, 0.4, 0.4),
445 indirect_bounces: 1,
446 max_indirect_luminance: 10.0,
447 temporal_reuse: true,
448 emissive_spatial_reuse: false,
449 indirect_spatial_reuse: true,
450 denoise: true,
451 taa: Taa::default(),
452 upscale: Upscale::default(),
453 }
454 }
455}
456
457impl ExtractComponent for HikariSettings {
458 type Query = &'static Self;
459 type Filter = ();
460
461 fn extract_component(item: QueryItem<Self::Query>) -> Self {
462 item.clone()
463 }
464}
465
466#[derive(Debug, Default, Clone, Copy, Hash, PartialEq, Eq, Reflect)]
468pub enum Taa {
469 #[default]
470 Jasmine,
471 None,
472}
473
474#[derive(Debug, Clone, Copy, Reflect)]
476pub enum Upscale {
477 Fsr1 {
479 ratio: f32,
481 sharpness: f32,
483 },
484 SmaaTu4x {
486 ratio: f32,
488 },
489 None,
490}
491
492impl Default for Upscale {
493 fn default() -> Self {
494 Self::SmaaTu4x { ratio: 2.0 }
495 }
496}
497
498impl Upscale {
499 pub fn ratio(&self) -> f32 {
500 match self {
501 Upscale::Fsr1 { ratio, .. } | Upscale::SmaaTu4x { ratio } => ratio.clamp(1.0, 2.0),
502 Upscale::None => 1.0,
503 }
504 }
505
506 pub fn sharpness(&self) -> f32 {
507 match self {
508 Upscale::Fsr1 { sharpness, .. } => *sharpness,
509 _ => 0.0,
510 }
511 }
512}
513
514#[derive(Clone, Deref, Resource, ExtractResource)]
515pub struct NoiseTextures(pub Vec<Handle<Image>>);
516
517impl AsBindGroup for NoiseTextures {
518 type Data = ();
519
520 fn as_bind_group(
521 &self,
522 layout: &BindGroupLayout,
523 render_device: &RenderDevice,
524 images: &RenderAssets<Image>,
525 fallback_image: &FallbackImage,
526 ) -> Result<PreparedBindGroup<Self>, AsBindGroupError> {
527 let images: Vec<_> = self
528 .iter()
529 .map(|handle| match images.get(handle) {
530 Some(image) => image,
531 None => fallback_image,
532 })
533 .collect();
534 let texture_views: Vec<_> = images.iter().map(|image| &*image.texture_view).collect();
535 let bindings = images
536 .iter()
537 .map(|image| OwnedBindingResource::TextureView(image.texture_view.clone()))
538 .collect();
539
540 let sampler = render_device.create_sampler(&SamplerDescriptor {
541 label: None,
542 address_mode_u: AddressMode::Repeat,
543 address_mode_v: AddressMode::Repeat,
544 address_mode_w: AddressMode::Repeat,
545 mag_filter: FilterMode::Nearest,
546 min_filter: FilterMode::Nearest,
547 mipmap_filter: FilterMode::Nearest,
548 ..Default::default()
549 });
550
551 let bind_group = render_device.create_bind_group(&BindGroupDescriptor {
552 label: None,
553 layout,
554 entries: &[
555 BindGroupEntry {
556 binding: 0,
557 resource: BindingResource::TextureViewArray(&texture_views),
558 },
559 BindGroupEntry {
560 binding: 1,
561 resource: BindingResource::Sampler(&sampler),
562 },
563 ],
564 });
565
566 Ok(PreparedBindGroup {
567 bindings,
568 bind_group,
569 data: (),
570 })
571 }
572
573 fn bind_group_layout(render_device: &RenderDevice) -> BindGroupLayout {
574 render_device.create_bind_group_layout(&BindGroupLayoutDescriptor {
575 label: None,
576 entries: &[
577 BindGroupLayoutEntry {
579 binding: 0,
580 visibility: ShaderStages::COMPUTE,
581 ty: BindingType::Texture {
582 sample_type: TextureSampleType::Float { filterable: false },
583 view_dimension: TextureViewDimension::D2,
584 multisampled: false,
585 },
586 count: NonZeroU32::new(NOISE_TEXTURE_COUNT as u32),
587 },
588 BindGroupLayoutEntry {
589 binding: 1,
590 visibility: ShaderStages::COMPUTE,
591 ty: BindingType::Sampler(SamplerBindingType::NonFiltering),
592 count: None,
593 },
594 ],
595 })
596 }
597}