Unravel Engine C++ Reference
Loading...
Searching...
No Matches
pipeline.cpp
Go to the documentation of this file.
1#include "pipeline.h"
2#include "glm/ext/scalar_integer.hpp"
14
15#include <engine/engine.h>
21
25#include <graphics/texture.h>
27
28namespace unravel
29{
30namespace rendering
31{
32
33namespace
34{
35
36auto create_or_resize_d_buffer(gfx::render_view& rview,
37 const usize32_t& viewport_size,
38 const pipeline::run_params& params) -> const gfx::texture::ptr&
39{
40 auto& depth = rview.tex_get_or_emplace("DEPTH");
41 if(!depth || (depth && depth->get_size() != viewport_size))
42 {
43 depth = std::make_shared<gfx::texture>(viewport_size.width,
44 viewport_size.height,
45 false,
46 1,
47 gfx::texture_format::D32F,
48 BGFX_TEXTURE_RT);
49 }
50
51 return depth;
52}
53
54auto create_or_resize_hiz_buffer(gfx::render_view& rview, const usize32_t& viewport_size) -> const gfx::texture::ptr&
55{
56 auto& hiz = rview.tex_get_or_emplace("HIZBUFFER");
57 if(!hiz || (hiz && hiz->get_size() != viewport_size))
58 {
59 // Create Hi-Z texture with compute shader support
60 hiz = std::make_shared<gfx::texture>(viewport_size.width,
61 viewport_size.height,
62 true, // generate mips
63 1, // one layer
64 gfx::texture_format::R32F, // R32F for better precision
65 BGFX_TEXTURE_RT | // Render target
66 BGFX_TEXTURE_COMPUTE_WRITE | // Allow compute writes
67 BGFX_SAMPLER_MIN_POINT | // Point sampling for min filter
68 BGFX_SAMPLER_MAG_POINT | // Point sampling for mag filter
69 BGFX_SAMPLER_MIP_POINT | // Point sampling for mips
70 BGFX_SAMPLER_U_CLAMP | // Clamp UVs
71 BGFX_SAMPLER_V_CLAMP // Clamp UVs
72 );
73 }
74
75 return hiz;
76}
77
78auto create_or_resize_g_buffer(gfx::render_view& rview,
79 const usize32_t& viewport_size,
80 const pipeline::run_params& params) -> const gfx::frame_buffer::ptr&
81{
82 auto& depth = create_or_resize_d_buffer(rview, viewport_size, params);
83
84 auto& fbo = rview.fbo_get_or_emplace("GBUFFER");
85 if(!fbo || (fbo && fbo->get_size() != viewport_size))
86 {
87 auto format = params.fill_hdr_params ? gfx::texture_format::RGBA16F : gfx::texture_format::RGBA8;
88
89 auto tex0 = std::make_shared<gfx::texture>(viewport_size.width,
90 viewport_size.height,
91 false,
92 1,
93 gfx::texture_format::RGBA8,
94 BGFX_TEXTURE_COMPUTE_WRITE | BGFX_TEXTURE_RT);
95
96 auto tex1 = std::make_shared<gfx::texture>(viewport_size.width,
97 viewport_size.height,
98 false,
99 1,
100 format,
101 BGFX_TEXTURE_RT);
102
103 auto tex2 = std::make_shared<gfx::texture>(viewport_size.width,
104 viewport_size.height,
105 false,
106 1,
107 gfx::texture_format::RGBA8,
108 BGFX_TEXTURE_RT);
109
110 auto tex3 = std::make_shared<gfx::texture>(viewport_size.width,
111 viewport_size.height,
112 false,
113 1,
114 gfx::texture_format::RGBA8,
115 BGFX_TEXTURE_RT);
116
117 fbo = std::make_shared<gfx::frame_buffer>();
118 fbo->populate({tex0, tex1, tex2, tex3, depth});
119 }
120
121 return fbo;
122}
123
124auto create_or_resize_l_buffer(gfx::render_view& rview,
125 const usize32_t& viewport_size,
126 const pipeline::run_params& params) -> const gfx::frame_buffer::ptr&
127{
128 auto& depth = create_or_resize_d_buffer(rview, viewport_size, params);
129
130 auto& fbo = rview.fbo_get_or_emplace("LBUFFER");
131 if(!fbo || (fbo && fbo->get_size() != viewport_size))
132 {
133 auto format = params.fill_hdr_params ? gfx::texture_format::RGBA16F : gfx::texture_format::RGBA8;
134
135 auto tex = std::make_shared<gfx::texture>(viewport_size.width,
136 viewport_size.height,
137 false,
138 1,
139 format,
140 BGFX_TEXTURE_RT);
141
142 fbo = std::make_shared<gfx::frame_buffer>();
143 fbo->populate({tex});
144
145 auto& fbo_depth = rview.fbo_get_or_emplace("LBUFFER_DEPTH");
146 fbo_depth = std::make_shared<gfx::frame_buffer>();
147 fbo_depth->populate({tex, depth});
148 }
149
150 return fbo;
151}
152
153auto create_or_resize_r_buffer(gfx::render_view& rview,
154 const usize32_t& viewport_size,
155 const pipeline::run_params& params) -> const gfx::frame_buffer::ptr&
156{
157 auto& fbo = rview.fbo_get_or_emplace("RBUFFER");
158 if(!fbo || (fbo && fbo->get_size() != viewport_size))
159 {
160 auto format = params.fill_hdr_params ? gfx::texture_format::RGBA16F : gfx::texture_format::RGBA8;
161
162 auto tex = std::make_shared<gfx::texture>(viewport_size.width,
163 viewport_size.height,
164 false,
165 1,
166 format,
167 BGFX_TEXTURE_RT | BGFX_TEXTURE_COMPUTE_WRITE);
168
169 fbo = std::make_shared<gfx::frame_buffer>();
170 fbo->populate({tex});
171 }
172
173 return fbo;
174}
175auto create_or_resize_o_buffer(gfx::render_view& rview,
176 const usize32_t& viewport_size,
177 const pipeline::run_params& params) -> const gfx::frame_buffer::ptr&
178{
179 auto& depth = create_or_resize_d_buffer(rview, viewport_size, params);
180
181 auto& tex = rview.tex_get_or_emplace("OBUFFER");
182 tex = std::make_shared<gfx::texture>(viewport_size.width,
183 viewport_size.height,
184 false,
185 1,
186 gfx::texture_format::RGBA8,
187 BGFX_TEXTURE_COMPUTE_WRITE | BGFX_TEXTURE_RT);
188
189 {
190 auto& fbo = rview.fbo_get_or_emplace("OBUFFER_DEPTH");
191 if(!fbo || (fbo && fbo->get_size() != viewport_size))
192 {
193 fbo = std::make_shared<gfx::frame_buffer>();
194 fbo->populate({tex, depth});
195 }
196 }
197
198 auto& fbo = rview.fbo_get_or_emplace("OBUFFER");
199 if(!fbo || (fbo && fbo->get_size() != viewport_size))
200 {
201 fbo = std::make_shared<gfx::frame_buffer>();
202 fbo->populate({tex});
203 }
204
205 return fbo;
206}
207
208auto update_lod_data(lod_data& data,
209 const std::vector<urange32_t>& lod_limits,
210 std::size_t total_lods,
211 float transition_time,
212 float dt,
214 const math::transform& world,
215 const camera& cam) -> bool
216{
217 if(!mesh)
218 return false;
219
220 if(total_lods <= 1)
221 return true;
222
223 const auto& viewport = cam.get_viewport_size();
224 auto rect = mesh.get()->calculate_screen_rect(world, cam);
225
226 float percent = math::clamp((float(rect.height()) / float(viewport.height)) * 100.0f, 0.0f, 100.0f);
227
228 std::size_t lod = 0;
229 for(size_t i = 0; i < lod_limits.size(); ++i)
230 {
231 const auto& range = lod_limits[i];
233 {
234 lod = i;
235 }
236 }
237
238 lod = math::clamp<std::size_t>(lod, 0, total_lods - 1);
239 if(data.target_lod_index != lod && data.target_lod_index == data.current_lod_index)
240 data.target_lod_index = static_cast<std::uint32_t>(lod);
241
242 if(data.current_lod_index != data.target_lod_index)
243 data.current_time += dt;
244
245 if(data.current_time >= transition_time)
246 {
248 data.current_time = 0.0f;
249 }
250
251 if(percent < 1.0f)
252 return false;
253
254 return true;
255}
256
257auto should_rebuild_shadows(const visibility_set_models_t& visibility_set,
258 const light& light,
259 const math::bbox& light_bounds,
260 const math::transform& light_transform) -> bool
261{
262 APP_SCOPE_PERF("Rendering/Shadow Rebuild Check Per Light");
263
264 auto light_world_bounds = math::bbox::mul(light_bounds, light_transform);
265 for(const auto& element : visibility_set)
266 {
267 const auto& transform_comp_ref = element.get<transform_component>();
268 const auto& model_comp_ref = element.get<model_component>();
269 const auto& model_world_bounds = model_comp_ref.get_world_bounds();
270
271 bool result = light_world_bounds.intersect(model_world_bounds);
272
273 if(result)
274 return true;
275 }
276
277 return false;
278}
279} // namespace
280
281auto deferred::get_light_program(const light& l) const -> const color_lighting&
282{
283 return color_lighting_[uint8_t(l.type)][uint8_t(l.shadow_params.depth)][uint8_t(l.shadow_params.type)];
284}
285
286auto deferred::get_light_program_no_shadows(const light& l) const -> const color_lighting&
287{
288 return color_lighting_no_shadow_[uint8_t(l.type)];
289}
290
291void deferred::submit_pbr_material(geom_program& program, const pbr_material& mat)
292{
293 const auto& color_map = mat.get_color_map();
294 const auto& normal_map = mat.get_normal_map();
295 const auto& roughness_map = mat.get_roughness_map();
296 const auto& metalness_map = mat.get_metalness_map();
297 const auto& ao_map = mat.get_ao_map();
298 const auto& emissive_map = mat.get_emissive_map();
299
300 const auto& albedo = color_map ? color_map : mat.default_color_map();
301 const auto& normal = normal_map ? normal_map : mat.default_normal_map();
302 const auto& roughness = roughness_map ? roughness_map : mat.default_color_map();
303 const auto& metalness = metalness_map ? metalness_map : mat.default_color_map();
304 const auto& ao = ao_map ? ao_map : mat.default_color_map();
305 const auto& emissive = emissive_map ? emissive_map : mat.default_color_map();
306
307 const auto& base_color = mat.get_base_color();
308 const auto& subsurface_color = mat.get_subsurface_color();
309 const auto& emissive_color = mat.get_emissive_color();
310 const auto& surface_data = mat.get_surface_data();
311 const auto& tiling = mat.get_tiling();
312 const auto& dither_threshold = mat.get_dither_threshold();
313 const auto& surface_data2 = mat.get_surface_data2();
314
315 gfx::set_texture(program.s_tex_color, 0, albedo.get());
316 gfx::set_texture(program.s_tex_normal, 1, normal.get());
317 gfx::set_texture(program.s_tex_roughness, 2, roughness.get());
318 gfx::set_texture(program.s_tex_metalness, 3, metalness.get());
319 gfx::set_texture(program.s_tex_ao, 4, ao.get());
320 gfx::set_texture(program.s_tex_emissive, 5, emissive.get());
321
322 gfx::set_uniform(program.u_base_color, base_color);
323 gfx::set_uniform(program.u_subsurface_color, subsurface_color);
324 gfx::set_uniform(program.u_emissive_color, emissive_color);
325 gfx::set_uniform(program.u_surface_data, surface_data);
326 gfx::set_uniform(program.u_tiling, tiling);
327 gfx::set_uniform(program.u_dither_threshold, dither_threshold);
328 gfx::set_uniform(program.u_surface_data2, surface_data2);
329
330 auto state = mat.get_render_states(true, true, true);
331
332 gfx::set_state(state);
333}
334
336{
337 APP_SCOPE_PERF("Rendering/Reflection Generation Pass");
338
340 [&](auto e, auto&& transform_comp, auto&& reflection_probe_comp, auto&& active)
341 {
342 if(reflection_probe_comp.already_generated())
343 {
344 return;
345 }
346
347 // reflection_probe_comp.set_generation_frame(gfx::get_render_frame());
348
349 const auto& world_transform = transform_comp.get_transform_global();
350
351 const auto& bounds = reflection_probe_comp.get_bounds();
352 if(!camera.test_obb(bounds, world_transform))
353 {
354 return;
355 }
356
357 const auto& probe = reflection_probe_comp.get_probe();
358
359 auto handle = scn.create_handle(e);
360 {
361 gfx::render_pass::push_scope("build.reflecitons");
362
363 // iterate trough each cube face
364 for(std::uint32_t face = 0; face < 6; ++face)
365 {
366 if(reflection_probe_comp.already_generated(face))
367 {
368 continue;
369 }
370
371 reflection_probe_comp.set_generation_frame(face, gfx::get_render_frame());
372
373 auto camera = camera::get_face_camera(face, world_transform);
374 camera.set_far_clip(probe.get_face_extents(face, world_transform));
375 auto& rview = reflection_probe_comp.get_render_view(face);
376 const auto& cubemap_fbo = reflection_probe_comp.get_cubemap_fbo(face);
377
378 camera.set_viewport_size(usize32_t(cubemap_fbo->get_size()));
379
380 bool not_environment = probe.method != reflect_method::environment;
381
384
385 if(not_environment)
386 {
388 }
389
390 auto params = create_run_params(handle);
391 params.vflags = vflags;
392
393 run_pipeline_impl(cubemap_fbo, scn, camera, rview, dt, params, pflags);
394 }
395
396 auto env_cube = reflection_probe_comp.get_cubemap();
397 auto env_cube_prefiltered = reflection_probe_comp.get_cubemap_prefiltered();
398
399 {
400 prefilter_pass::run_params prefilter_params;
401
402 prefilter_params.apply_prefilter = reflection_probe_comp.get_apply_prefilter();
403
404 for(std::uint32_t face = 0; face < 6; ++face)
405 {
406 const auto& cubemap_fbo = reflection_probe_comp.get_cubemap_fbo(face);
407 prefilter_params.input_faces[face] = cubemap_fbo->get_texture();
408 }
409
410 prefilter_params.output_cube = env_cube;
411 prefilter_params.output_cube_prefiltered = env_cube_prefiltered;
412
413 prefilter_pass_.run(prefilter_params);
414 }
415
417 }
418 });
419}
420
422{
423 APP_SCOPE_PERF("Rendering/Shadow Generation Pass");
424
426
427 bool queried = false;
428 visibility_set_models_t dirty_models;
429
430 const auto& view = camera.get_view();
431 const auto& proj = camera.get_projection();
432 const auto& camera_pos = camera.get_position();
433
435 [&](auto e, auto&& transform_comp, auto&& light_comp)
436 {
437 const auto& light = light_comp.get_light();
438
439 bool is_directional = light.type == light_type::directional;
440 bool has_render_mask = render_mask.mask != layer_reserved::everything_layer;
441 bool camera_dependant = is_directional || has_render_mask;
442 bool is_active = scn.registry->all_of<active_component>(e);
443
444 auto& generator = light_comp.get_shadowmap_generator();
445 generator.enable_adaptive_shadows(true);
446 generator.set_altitude_scale_factor(0.4f);
447 if(!camera_dependant && generator.already_updated())
448 {
449 return;
450 }
451
452 APP_SCOPE_PERF("Rendering/Shadow Generation Pass Per Light");
453
454 auto world_transform = transform_comp.get_transform_global();
455 world_transform.reset_scale();
456 const auto& light_direction = world_transform.z_unit_axis();
457
458 generator.update(camera, light, world_transform, is_active);
459
460 if(!is_active)
461 {
462 return;
463 }
464
465 const auto& bounds = light_comp.get_bounds_precise(light_direction);
466 if(!camera.test_obb(bounds, world_transform))
467 {
468 return;
469 }
470
472 {
473 return;
474 }
475
476 if(!queried)
477 {
478 dirty_models = gather_visible_models(scn, nullptr, query, render_mask);
479 queried = true;
480 }
481
482 bool should_rebuild = should_rebuild_shadows(dirty_models, light, bounds, world_transform);
483
484 // If shadows shouldn't be rebuilt - continue.
485 if(!should_rebuild)
486 return;
487
488 APP_SCOPE_PERF("Rendering/Shadow Generation Pass Per Light After Cull");
489
490 generator.generate_shadowmaps(dirty_models);
491 });
492}
493
495 const camera& camera,
496 gfx::render_view& rview,
497 delta_t dt,
498 const run_params& params,
499 layer_mask render_mask) -> gfx::frame_buffer::ptr
500{
501 const auto& viewport_size = camera.get_viewport_size();
502 const auto& obuffer = create_or_resize_o_buffer(rview, viewport_size, params);
503
504 run_pipeline_impl(obuffer, scn, camera, rview, dt, params, pipeline_steps::full, render_mask);
505
506 return obuffer;
507}
508
510 scene& scn,
511 const camera& camera,
512 gfx::render_view& rview,
513 delta_t dt,
514 const run_params& params,
515 layer_mask render_mask)
516{
517 auto obuffer = run_pipeline(scn, camera, rview, dt, params, render_mask);
518
519 blit_pass::run_params pass_params;
520 pass_params.input = obuffer;
521 pass_params.output = output;
522 blit_pass_.run(pass_params);
523}
524
526{
527 debug_pass_ = pass;
528}
529
531 scene& scn,
532 const camera& camera,
533 gfx::render_view& rview,
534 delta_t dt,
535 const run_params& params,
536 pipeline_flags pflags,
537 layer_mask render_mask)
538{
539 APP_SCOPE_PERF("Rendering/Run Pipeline");
540
541 if(pipeline_steps::full == pflags)
542 {
543 stats_ = {};
544 }
545
546 visibility_set_models_t visibility_set;
547 gfx::frame_buffer::ptr target = nullptr;
548
549 bool apply_reflecitons = pflags & pipeline_steps::reflection_probe;
550 bool apply_shadows = pflags & pipeline_steps::shadow_pass;
551
552 if(apply_reflecitons)
553 {
554 build_reflections(scn, camera, dt);
555 }
556
557 if(apply_shadows)
558 {
560 }
561
562 const auto& viewport_size = camera.get_viewport_size();
563 create_or_resize_d_buffer(rview, viewport_size, params);
564 create_or_resize_g_buffer(rview, viewport_size, params);
565 create_or_resize_l_buffer(rview, viewport_size, params);
566 create_or_resize_r_buffer(rview, viewport_size, params);
567
569 {
570 visibility_set = gather_visible_models(scn, &camera.get_frustum(), params.vflags, render_mask);
571 }
572
573 run_g_buffer_pass(visibility_set, camera, rview, dt);
574
575 run_assao_pass(visibility_set, camera, rview, dt, params);
576
577 run_reflection_probe_pass(scn, camera, rview, dt);
578
579 if(apply_reflecitons)
580 {
581 run_ssr_pass(camera, rview, target, params);
582 }
583
584 target = run_lighting_pass(scn, camera, rview, apply_shadows, dt);
585
586 target = run_atmospherics_pass(target, scn, camera, rview, dt);
587
589 {
590 particle_pass(scn, camera, rview, target);
591 }
592
593 target = run_tonemapping_pass(rview, target, output, params);
594
595 run_fxaa_pass(rview, target, output, params);
596
597 if(pflags == pipeline_steps::full)
598 {
599 ui_pass(scn, camera, rview, output);
600
601 if(debug_pass_ >= 0)
602 {
603 run_debug_visualization_pass(camera, rview, output);
604 }
605 }
606}
607
609 const camera& camera,
610 gfx::render_view& rview,
611 delta_t dt)
612{
613 APP_SCOPE_PERF("Rendering/G-Buffer Pass");
614
615 const auto& view = camera.get_view();
616 const auto& proj = camera.get_projection();
617 const auto& viewport_size = camera.get_viewport_size();
618
619 const auto& gbuffer = rview.fbo_get("GBUFFER");
620
621 gfx::render_pass pass("g_buffer_pass");
622 pass.clear();
623 pass.set_view_proj(view, proj);
624 pass.bind(gbuffer.get());
625
626 // Clear batch collector for this frame
627 batch_collector_.clear();
628
629 for(const auto& e : visibility_set)
630 {
631 const auto& transform_comp = e.get<transform_component>();
632 auto& model_comp = e.get<model_component>();
633
634 const auto& model = model_comp.get_model();
635 if(!model.is_valid())
636 continue;
637
638 const auto& world_transform = transform_comp.get_transform_global();
639 const auto clip_planes = math::vec2(camera.get_near_clip(), camera.get_far_clip());
640
641 lod_data lod_runtime_data{}; // camera_lods[e];
642 const auto transition_time = 0.0f;
643 const auto lod_count = model.get_lods().size();
644 const auto& lod_limits = model.get_lod_limits();
645
646 const auto base_mesh = model.get_lod(0);
647 if(!base_mesh)
648 continue;
649
650 if(!update_lod_data(lod_runtime_data,
651 lod_limits,
652 lod_count,
653 transition_time,
654 dt.count(),
655 base_mesh,
656 world_transform,
657 camera))
658 continue;
659
660 const auto current_time = lod_runtime_data.current_time;
661 const auto current_lod_index = lod_runtime_data.current_lod_index;
662 const auto target_lod_index = lod_runtime_data.target_lod_index;
663
664 const auto params = math::vec3{0.0f, -1.0f, (transition_time - current_time) / transition_time};
665
666 const auto params_inv = math::vec3{1.0f, 1.0f, current_time / transition_time};
667
668 const auto& submesh_transforms = model_comp.get_submesh_transforms();
669 const auto& bone_transforms = model_comp.get_bone_transforms();
670 const auto& skinning_matrices = model_comp.get_skinning_transforms();
671
672 auto camera_pos = camera.get_position();
673
674
675 model::submit_callbacks callbacks;
676 callbacks.setup_begin = [&](const model::submit_callbacks::params& submit_params)
677 {
679 stats_.drawn_skinned_models += uint32_t(submit_params.skinned);
680
681 geom_program& prog = submit_params.skinned ? geom_program_skinned_ : geom_program_;
682
683 prog.program->begin();
684
685 gfx::set_uniform(prog.u_camera_wpos, camera_pos);
686 gfx::set_uniform(prog.u_camera_clip_planes, clip_planes);
687 };
688 callbacks.setup_params_per_instance = [&](const model::submit_callbacks::params& submit_params)
689 {
690 geom_program& prog = submit_params.skinned ? geom_program_skinned_ : geom_program_;
691
692 gfx::set_uniform(prog.u_lod_params, params);
693 };
694 callbacks.setup_params_per_submesh =
695 [&](const model::submit_callbacks::params& submit_params, const material& mat)
696 {
697 geom_program& prog = submit_params.skinned ? geom_program_skinned_ : geom_program_;
698
699 bool submitted = mat.submit(prog.program.get());
700 if(!submitted)
701 {
702 if(mat.is<pbr_material>())
703 {
704 const auto& pbr = static_cast<const pbr_material&>(mat);
705 submit_pbr_material(prog, pbr);
706 }
707 }
708
709 gfx::submit(pass.id, prog.program->native_handle(), 0, submit_params.preserve_state);
710 };
711 callbacks.setup_end = [&](const model::submit_callbacks::params& submit_params)
712 {
713 geom_program& prog = submit_params.skinned ? geom_program_skinned_ : geom_program_;
714
715 prog.program->end();
716 };
717
718 model_comp.set_last_render_frame(gfx::get_render_frame());
719
720 // Check if this model can be batched (static mesh, no skinning)
721 const bool is_skinned = !skinning_matrices.empty();
722 const bool can_batch = batch_collector::is_static_mesh_batching_enabled() && !is_skinned;
723
724 if (can_batch)
725 {
726 // Collect this model for batching with appropriate transforms
727 collect_model_for_batching(model, world_transform, submesh_transforms, current_lod_index, params.x);
728
729 // Handle LOD transitions for batched models
730 if(math::epsilonNotEqual(current_time, 0.0f, math::epsilon<float>()))
731 {
732 collect_model_for_batching(model, world_transform, submesh_transforms, target_lod_index, params_inv.x);
733 }
734 }
735 else
736 {
737 // Render individually (skinned meshes, complex transforms, etc.)
738 model.submit(world_transform,
739 submesh_transforms,
740 bone_transforms,
741 skinning_matrices,
742 current_lod_index,
743 callbacks);
744 if(math::epsilonNotEqual(current_time, 0.0f, math::epsilon<float>()))
745 {
746 callbacks.setup_params_per_instance = [&](const model::submit_callbacks::params& submit_params)
747 {
748 geom_program& prog = submit_params.skinned ? geom_program_skinned_ : geom_program_;
749
750 gfx::set_uniform(prog.u_lod_params, params_inv);
751 };
752
753 model.submit(world_transform,
754 submesh_transforms,
755 bone_transforms,
756 skinning_matrices,
757 target_lod_index,
758 callbacks);
759 }
760 }
761 }
762
763 // Submit all collected batches
765 {
766 submit_batched_geometry(pass, camera);
767 }
768 gfx::discard();
769}
770
771void deferred::collect_model_for_batching(const model& model_asset,
772 const math::mat4& world_transform,
773 const pose_mat4& submesh_transforms,
774 uint32_t lod_index,
775 float lod_param)
776{
777 // Get the mesh for the specified LOD
778 const auto& lods = model_asset.get_lods();
779 if (lod_index >= lods.size())
780 {
781 return;
782 }
783
784 const auto& mesh_asset = lods[lod_index];
785 if (!mesh_asset)
786 {
787 return;
788 }
789
790 // Get materials for each submesh
791 const auto submesh_count = mesh_asset.get()->get_data_groups_count();
792
793 // Collect each submesh as a separate batch entry
794 for (uint32_t submesh_index = 0; submesh_index < submesh_count; ++submesh_index)
795 {
796 // Get material for this submesh
797 auto material_ptr = model_asset.get_material_instance(submesh_index);
798 if(!material_ptr)
799 {
800 continue; // Skip submeshes without valid materials
801 }
802
803 // Determine the transform pointer to use for this submesh
804 const math::mat4* transform_ptr = nullptr;
805 if (submesh_index < submesh_transforms.transforms.size())
806 {
807 // Use the specific submesh transform
808 transform_ptr = &submesh_transforms.transforms[submesh_index];
809 }
810 else
811 {
812 // Fall back to world transform if no specific submesh transform
813 transform_ptr = &world_transform;
814 }
815
816 // Create batch key
817 batch_key key(mesh_asset.get(), material_ptr, lod_index, submesh_index);
818 if (!key.is_valid())
819 {
820 continue;
821 }
822
823 // Create batch instance with the appropriate transform pointer
824 batch_instance instance(transform_ptr);
825 instance.lod_params.x = lod_param;
826
827 // Collect for batching
828 batch_collector_.collect_renderable(key, instance);
829 }
830}
831
832void deferred::submit_batched_geometry(gfx::render_pass& pass, const camera& camera)
833{
834 APP_SCOPE_PERF("Rendering/Submit Batched Geometry");
835
836 // Prepare batches for rendering
837 submit_context context;
838 context.view_id = pass.id;
839 context.camera_position = camera.get_position();
840 context.enable_distance_sorting = false; // Opaque objects don't need distance sorting
841 context.max_instances_per_batch = 1024; // BGFX instance limit
842 context.enable_profiling = true;
843
844 batch_collector_.prepare_batches(context);
845
846 const auto& prepared_batches = batch_collector_.get_prepared_batches();
847 if (prepared_batches.empty())
848 {
849 return;
850 }
851
852 // Set up common uniforms
853 const auto camera_pos = camera.get_position();
854 const auto clip_planes = math::vec2(camera.get_near_clip(), camera.get_far_clip());
855
856 geom_program_instanced_.program->begin();
857 gfx::set_uniform(geom_program_instanced_.u_camera_wpos, camera_pos);
858 gfx::set_uniform(geom_program_instanced_.u_camera_clip_planes, clip_planes);
859
860 // Submit each batch
861 for (const auto* batch : prepared_batches)
862 {
863 if (!batch->is_valid() || batch->instances.empty())
864 {
865 continue;
866 }
867
869
870 // Get mesh and material from batch key
871 const auto& mesh_ptr = batch->key.mesh_ptr;
872 const auto& material_ptr = batch->key.material_ptr;
873 const auto lod_index = batch->key.lod_index;
874 const auto submesh_index = batch->key.submesh_index;
875
876 if (!mesh_ptr || !material_ptr)
877 {
878 continue;
879 }
880
881 const auto submesh = mesh_ptr->get_submesh(submesh_index);
882 if(!submesh)
883 {
884 continue;
885 }
886
887 // Create instance buffer from batch instances
888 const auto instance_count = static_cast<uint32_t>(batch->instances.size());
889 const auto instance_data_size = static_cast<uint16_t>(instance_vertex_data::packed_size());
890
891 // Allocate instance buffer
892 bgfx::InstanceDataBuffer instance_buffer;
893 bgfx::allocInstanceDataBuffer(&instance_buffer, instance_count, instance_data_size);
894 if (!instance_buffer.data)
895 {
896 continue; // Skip this batch if allocation failed
897 }
898
899
900 // Submit the mesh with instancing
901 // Bind vertex and index buffers for the specific submesh
902 mesh_ptr->bind_render_buffers_for_submesh(submesh);
903
904 // Pack instance data into buffer
905 auto* buffer_data = reinterpret_cast<instance_vertex_data*>(instance_buffer.data);
906 for (size_t i = 0; i < batch->instances.size(); ++i)
907 {
908 buffer_data[i] = instance_vertex_data(batch->instances[i]);
909 }
910
911 // Set instance data buffer
912 bgfx::setInstanceDataBuffer(&instance_buffer);
913
914 // Submit material properties
915 bool material_submitted = material_ptr->submit(geom_program_instanced_.program.get());
916 if (!material_submitted)
917 {
918 if (material_ptr->is<pbr_material>())
919 {
920 const auto& pbr = static_cast<const pbr_material&>(*material_ptr);
921 submit_pbr_material(geom_program_instanced_, pbr);
922 }
923 }
924
925 // Set LOD parameters (using global LOD settings for now)
926 const auto lod_params = math::vec3{0.0f, -1.0f, 1.0f}; // Default LOD params
927 gfx::set_uniform(geom_program_instanced_.u_lod_params, lod_params);
928
929 // Submit the instanced draw call
930 gfx::submit(pass.id, geom_program_instanced_.program->native_handle(), 0, false);
931 }
932
933 geom_program_instanced_.program->end();
934
935 // Update statistics
936 const auto& batch_stats = batch_collector_.get_stats();
937 stats_.add_batch_stats(batch_stats);
938
939 // Clear batches to invalidate all transform pointers and free memory
940 batch_collector_.clear();
941}
942
944 const camera& camera,
945 gfx::render_view& rview,
946 delta_t dt,
947 const run_params& rparams)
948{
949 if(!rparams.fill_assao_params)
950 {
951 return;
952 }
953 APP_SCOPE_PERF("Rendering/ASSAO Pass");
954
955 const auto& gbuffer = rview.fbo_get("GBUFFER");
956
957 auto color_ao = gbuffer->get_texture(0);
958 auto normal = gbuffer->get_texture(1);
959 auto depth = gbuffer->get_texture(4);
960
962 params.depth = depth.get();
963 params.normal = normal.get();
964 params.color_ao = color_ao.get();
965
966 rparams.fill_assao_params(params);
967
968 assao_pass_.run(camera, rview, params);
969}
970
972 const camera& camera,
973 gfx::render_view& rview,
974 bool apply_shadows,
976{
977 APP_SCOPE_PERF("Rendering/Lighting Pass");
978
979 const auto& view = camera.get_view();
980 const auto& proj = camera.get_projection();
981 const auto& camera_pos = camera.get_position();
982
983 const auto& viewport_size = camera.get_viewport_size();
984
985 const auto& gbuffer = rview.fbo_get("GBUFFER");
986 const auto& rbuffer = rview.fbo_safe_get("RBUFFER");
987 const auto& lbuffer = rview.fbo_get("LBUFFER");
988
989 const auto buffer_size = lbuffer->get_size();
990
991 gfx::render_pass pass("light_buffer_pass");
992 pass.bind(lbuffer.get());
993 pass.set_view_proj(view, proj);
994 pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
995
996 scn.registry->view<transform_component, light_component, active_component>().each(
997 [&](auto e, auto&& transform_comp_ref, auto&& light_comp_ref, auto&& active)
998 {
999 const auto& light = light_comp_ref.get_light();
1000 const auto& generator = light_comp_ref.get_shadowmap_generator();
1001 auto world_transform = transform_comp_ref.get_transform_global();
1002 world_transform.reset_scale();
1003 const auto& light_position = world_transform.get_position();
1004 const auto& light_direction = world_transform.z_unit_axis();
1005
1006 const auto& bounds = light_comp_ref.get_bounds_precise(light_direction);
1007 if(!camera.test_obb(bounds, world_transform))
1008 {
1009 return;
1010 }
1011
1012 irect32_t rect(0, 0, irect32_t::value_type(buffer_size.width), irect32_t::value_type(buffer_size.height));
1013 if(light_comp_ref
1014 .compute_projected_sphere_rect(rect, light_position, light_direction, camera_pos, view, proj) == 0)
1015 return;
1016
1017
1018 APP_SCOPE_PERF("Rendering/Lighting Pass/Per Light");
1019
1020 bool has_shadows = light.casts_shadows && apply_shadows;
1021
1022 stats_.drawn_lights++;
1023 stats_.drawn_lights_casting_shadows += uint32_t(has_shadows);
1024
1025 const auto& lprogram = has_shadows ? get_light_program(light) : get_light_program_no_shadows(light);
1026
1027 lprogram.program->begin();
1028
1030 {
1031 float light_data[4] = {0.0f, 0.0f, 0.0f, light.ambient_intensity};
1032
1033 gfx::set_uniform(lprogram.u_light_direction, light_direction);
1034 gfx::set_uniform(lprogram.u_light_data, light_data);
1035 }
1037 {
1038 float light_data[4] = {light.point_data.range,
1040 0.0f,
1042
1043 gfx::set_uniform(lprogram.u_light_position, light_position);
1044 gfx::set_uniform(lprogram.u_light_data, light_data);
1045 }
1046
1048 {
1049 float light_data[4] = {light.spot_data.get_range(),
1050 math::cos(math::radians(light.spot_data.get_inner_angle() * 0.5f)),
1051 math::cos(math::radians(light.spot_data.get_outer_angle() * 0.5f)),
1053
1054 gfx::set_uniform(lprogram.u_light_direction, light_direction);
1055 gfx::set_uniform(lprogram.u_light_position, light_position);
1056 gfx::set_uniform(lprogram.u_light_data, light_data);
1057 }
1058
1059 float light_color_intensity[4] = {light.color.value.r,
1060 light.color.value.g,
1061 light.color.value.b,
1063
1064 gfx::set_uniform(lprogram.u_light_color_intensity, light_color_intensity);
1065 gfx::set_uniform(lprogram.u_camera_position, camera_pos);
1066
1067 size_t i = 0;
1068 for(; i < gbuffer->get_attachment_count(); ++i)
1069 {
1070 gfx::set_texture(lprogram.s_tex[i], i, gbuffer->get_texture(i));
1071 }
1072 gfx::set_texture(lprogram.s_tex[i], i, rbuffer);
1073 i++;
1074 gfx::set_texture(lprogram.s_tex[i], i, ibl_brdf_lut_.get());
1075 i++;
1076
1077 if(has_shadows)
1078 {
1079 generator.submit_uniforms(i);
1080 }
1082 auto topology = gfx::clip_quad(1.0f);
1083 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_ADD);
1084 gfx::submit(pass.id, lprogram.program->native_handle());
1085 gfx::set_state(BGFX_STATE_DEFAULT);
1086
1087 lprogram.program->end();
1088 });
1089
1090 gfx::discard();
1091
1092 return lbuffer;
1093}
1094
1096{
1097 APP_SCOPE_PERF("Rendering/Reflection Probe Pass");
1098
1099 const auto& view = camera.get_view();
1100 const auto& proj = camera.get_projection();
1101 const auto& camera_pos = camera.get_position();
1102
1103 const auto& viewport_size = camera.get_viewport_size();
1104 const auto& gbuffer = rview.fbo_get("GBUFFER");
1105 const auto& rbuffer = rview.fbo_get("RBUFFER");
1106
1107 const auto buffer_size = rbuffer->get_size();
1108
1109 gfx::render_pass pass("refl_buffer_pass");
1110 pass.bind(rbuffer.get());
1111 pass.set_view_proj(view, proj);
1112 pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
1113 std::vector<entt::entity> sorted_probes;
1114
1115 // Collect all entities with the relevant components
1117 [&](auto e, auto&& transform_comp_ref, auto&& probe_comp_ref, auto&& active)
1118 {
1119 sorted_probes.emplace_back(e);
1120 });
1121
1122 // Sort the probes based on the method and max range
1123 std::sort(std::begin(sorted_probes),
1124 std::end(sorted_probes),
1125 [&](const auto& lhs, const auto& rhs)
1126 {
1127 const auto& lhs_comp = scn.registry->get<reflection_probe_component>(lhs);
1128 const auto& lhs_probe = lhs_comp.get_probe();
1129
1130 const auto& rhs_comp = scn.registry->get<reflection_probe_component>(rhs);
1131 const auto& rhs_probe = rhs_comp.get_probe();
1132
1133 // Environment probes should be last
1134 if(lhs_probe.method != rhs_probe.method)
1135 {
1136 return lhs_probe.method < rhs_probe.method; // Environment method is "greater"
1137 }
1138
1139 // If the reflection methods are the same, compare based on the maximum range
1140 return lhs_probe.get_max_range() > rhs_probe.get_max_range(); // Smaller ranges first
1141 });
1142
1143 // Render or process the sorted probes
1144 for(const auto& e : sorted_probes)
1145 {
1146 auto& transform_comp_ref = scn.registry->get<transform_component>(e);
1147 auto& probe_comp_ref = scn.registry->get<reflection_probe_component>(e);
1148
1149 const auto& probe = probe_comp_ref.get_probe();
1150 const auto& world_transform = transform_comp_ref.get_transform_global();
1151 const auto& probe_position = world_transform.get_position();
1152 const auto& probe_scale = world_transform.get_scale();
1153
1154 irect32_t rect(0, 0, irect32_t::value_type(buffer_size.width), irect32_t::value_type(buffer_size.height));
1155 if(probe_comp_ref.compute_projected_sphere_rect(rect, probe_position, probe_scale, camera_pos, view, proj) == 0)
1156 {
1157 continue;
1158 }
1159
1160 const auto& cubemap = probe_comp_ref.get_cubemap_prefiltered();
1161
1162 ref_probe_program* ref_probe_program = nullptr;
1163 float influence_radius = 0.0f;
1164 if(probe.type == probe_type::sphere && sphere_ref_probe_program_.program)
1165 {
1166 ref_probe_program = &sphere_ref_probe_program_;
1167 influence_radius =
1168 math::max(probe_scale.x, math::max(probe_scale.y, probe_scale.z)) * probe.sphere_data.range;
1169 }
1170
1171 if(probe.type == probe_type::box && box_ref_probe_program_.program)
1172 {
1173 math::transform t = world_transform;
1174 t.scale(probe.box_data.extents);
1175 auto u_inv_world = math::inverse(t).get_matrix();
1176 float data2[4] = {probe.box_data.extents.x,
1177 probe.box_data.extents.y,
1178 probe.box_data.extents.z,
1179 probe.box_data.transition_distance};
1180
1181 ref_probe_program = &box_ref_probe_program_;
1182
1183 gfx::set_uniform(box_ref_probe_program_.u_inv_world, u_inv_world);
1184 gfx::set_uniform(box_ref_probe_program_.u_data2, data2);
1185
1186 influence_radius = math::length(t.get_scale() + probe.box_data.transition_distance);
1187 }
1188
1189 if(ref_probe_program)
1190 {
1191 float mips = cubemap ? float(cubemap->info.numMips) : 1.0f;
1192 float data0[4] = {
1193 probe_position.x,
1194 probe_position.y,
1195 probe_position.z,
1196 influence_radius,
1197 };
1198
1199 float data1[4] = {mips, probe.intensity, 0.0f, 0.0f};
1200
1201 gfx::set_uniform(ref_probe_program->u_data0, data0);
1202 gfx::set_uniform(ref_probe_program->u_data1, data1);
1203
1204 for(size_t i = 0; i < gbuffer->get_attachment_count(); ++i)
1205 {
1206 gfx::set_texture(ref_probe_program->s_tex[i], i, gbuffer->get_texture(i));
1207 }
1208
1209 gfx::set_texture(ref_probe_program->s_tex_cube, 5, cubemap);
1210
1212 auto topology = gfx::clip_quad(1.0f);
1213 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_ALPHA);
1214
1215 ref_probe_program->program->begin();
1216 gfx::submit(pass.id, ref_probe_program->program->native_handle());
1217 gfx::set_state(BGFX_STATE_DEFAULT);
1218 ref_probe_program->program->end();
1219 }
1220 }
1221
1222 gfx::discard();
1223}
1224
1226 scene& scn,
1227 const camera& camera,
1228 gfx::render_view& rview,
1230{
1231 APP_SCOPE_PERF("Rendering/Atmospheric Pass");
1232
1236
1237 bool found_sun = false;
1238
1240 scn.registry->view<transform_component, skylight_component, active_component>().each(
1241 [&](auto e, auto&& transform_comp_ref, auto&& light_comp_ref, auto&& active)
1242 {
1243 auto entity = scn.create_handle(e);
1244
1245 if(found_sun)
1246 {
1247 APPLOG_WARNING("[{}] More than one entity with this component. Others are ignored.", "Skylight");
1248 return;
1249 }
1250 const auto& cubemap = light_comp_ref.get_cubemap();
1251 auto cubemap_texture = cubemap.get();
1252 if(cubemap_texture)
1253 {
1254 if(cubemap_texture->info.cubeMap)
1255 {
1256 params_skybox.cubemap = cubemap;
1257 }
1258 }
1259
1260 mode = light_comp_ref.get_mode();
1261 found_sun = true;
1262 if(auto light_comp = entity.template try_get<light_component>())
1263 {
1264 const auto& light = light_comp->get_light();
1265
1267 {
1268 const auto& world_transform = transform_comp_ref.get_transform_global();
1269 params.light_direction = world_transform.z_unit_axis();
1270 params.turbidity = light_comp_ref.get_turbidity();
1271
1272 params_perez.light_direction = world_transform.z_unit_axis();
1273 params_perez.turbidity = light_comp_ref.get_turbidity();
1274 }
1275 }
1276 });
1277
1278 if(!found_sun)
1279 {
1280 return input;
1281 }
1282 const auto& viewport_size = camera.get_viewport_size();
1283
1284 auto c = camera;
1285 c.set_projection_mode(projection_mode::perspective);
1286
1287 auto lbuffer_depth = rview.fbo_get("LBUFFER_DEPTH");
1288
1289 switch(mode)
1290 {
1292 atmospheric_pass_perez_.run(lbuffer_depth, c, rview, dt, params_perez);
1293 break;
1295 atmospheric_pass_.run(lbuffer_depth, c, rview, dt, params);
1296 break;
1297 default:
1298 atmospheric_pass_skybox_.run(lbuffer_depth, c, rview, dt, params_skybox);
1299 break;
1300 }
1301
1302 return input;
1303}
1304
1306 gfx::render_view& rview,
1307 const gfx::frame_buffer::ptr& output,
1308 const run_params& rparams) -> gfx::frame_buffer::ptr
1309{
1310 if(!rparams.fill_ssr_params)
1311 {
1312 return output;
1313 }
1314
1315 ssr_pass::run_params ssr_params;
1316
1317 ssr_params.output = rview.fbo_get("RBUFFER");
1318 ssr_params.g_buffer = rview.fbo_get("GBUFFER");
1319
1320 ssr_params.previous_frame = rview.fbo_get("LBUFFER")->get_texture();
1321
1322 ssr_params.cam = &camera;
1323
1324 if(rparams.fill_ssr_params)
1325 {
1326 rparams.fill_ssr_params(ssr_params);
1327 }
1328
1329 {
1330 create_or_resize_hiz_buffer(rview, camera.get_viewport_size());
1331 run_hiz_pass(camera, rview, delta_t(0.0f));
1332
1333 ssr_params.hiz_buffer = rview.tex_get("HIZBUFFER");
1334 }
1335
1336 // BUG Cone tracing is not working properly, so we disable it for now.
1337 ssr_params.settings.fidelityfx.enable_cone_tracing = false;
1338
1339 return ssr_pass_.run(rview, ssr_params);
1340}
1341
1344 const gfx::frame_buffer::ptr& output,
1345 const run_params& rparams) -> gfx::frame_buffer::ptr
1346{
1347 if(!rparams.fill_fxaa_params)
1348 {
1349 return input;
1350 }
1351
1352 APP_SCOPE_PERF("Rendering/FXAA Pass");
1353
1354 fxaa_pass::run_params params;
1355 params.input = input;
1356 params.output = output;
1357
1358 rparams.fill_fxaa_params(params);
1359
1360 return fxaa_pass_.run(rview, params);
1361}
1362
1365 const gfx::frame_buffer::ptr& output,
1366 const run_params& rparams) -> gfx::frame_buffer::ptr
1367{
1368 if(!rparams.fill_hdr_params)
1369 {
1370 return input;
1371 }
1372 APP_SCOPE_PERF("Rendering/Tonemapping Pass");
1373
1375 params.input = input;
1376
1377 if(!rparams.fill_fxaa_params)
1378 {
1379 params.output = output;
1380 }
1381
1382 rparams.fill_hdr_params(params);
1383
1384 return tonemapping_pass_.run(rview, params);
1385}
1386
1388 gfx::render_view& rview,
1389 const gfx::frame_buffer::ptr& output)
1390{
1391 const auto& view = camera.get_view();
1392 const auto& proj = camera.get_projection();
1393 const auto& gbuffer = rview.fbo_get("GBUFFER");
1394 const auto& rbuffer = rview.fbo_safe_get("RBUFFER");
1395 // const auto& lbuffer = rview.fbo_get("LBUFFER");
1396
1397 gfx::render_pass pass("debug_visualization_pass");
1398 pass.bind(output.get());
1399 pass.set_view_proj(view, proj);
1400 // pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
1401
1402 const auto output_size = output->get_size();
1403
1404 debug_visualization_program_.program->begin();
1405
1406 float u_params[4] = {float(debug_pass_), 0.0f, 0.0f, 0.0f};
1407
1408 gfx::set_uniform(debug_visualization_program_.u_params, u_params);
1409
1410 size_t i = 0;
1411 for(; i < gbuffer->get_attachment_count(); ++i)
1412 {
1413 gfx::set_texture(debug_visualization_program_.s_tex[i], i, gbuffer->get_texture(i));
1414 }
1415 gfx::set_texture(debug_visualization_program_.s_tex[i], i, rbuffer);
1416
1417 irect32_t rect(0, 0, irect32_t::value_type(output_size.width), irect32_t::value_type(output_size.height));
1419 auto topology = gfx::clip_quad(1.0f);
1420 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
1421 gfx::submit(pass.id, debug_visualization_program_.program->native_handle());
1422 gfx::set_state(BGFX_STATE_DEFAULT);
1423 debug_visualization_program_.program->end();
1424
1425 gfx::discard();
1426}
1427
1429{
1430 APP_SCOPE_PERF("Rendering/SSR/Hi-Z Pass");
1431
1432 auto& gbuffer = rview.fbo_get("GBUFFER");
1433 if(!gbuffer)
1434 return nullptr;
1435
1436 // Run Hi-Z pass using the base class's pass
1437 hiz_pass::run_params params;
1438 params.depth_buffer = gbuffer->get_texture(4);
1439 ;
1440 params.output_hiz = rview.tex_get("HIZBUFFER");
1441 params.cam = &camera;
1442
1443 hiz_pass_.run(rview, params);
1444 return params.output_hiz;
1445}
1446
1451
1456
1458{
1459 auto& am = ctx.get_cached<asset_manager>();
1460
1461 auto load_program = [&](const std::string& vs, const std::string& fs)
1462 {
1463 auto vs_shader = am.get_asset<gfx::shader>("engine:/data/shaders/" + vs + ".sc");
1464 auto fs_shadfer = am.get_asset<gfx::shader>("engine:/data/shaders/" + fs + ".sc");
1465
1466 return std::make_unique<gpu_program>(vs_shader, fs_shadfer);
1467 };
1468
1469 geom_program_.program = load_program("vs_deferred_geom", "fs_deferred_geom");
1470 geom_program_.cache_uniforms();
1471
1472 geom_program_skinned_.program = load_program("vs_deferred_geom_skinned", "fs_deferred_geom");
1473 geom_program_skinned_.cache_uniforms();
1474
1475 geom_program_instanced_.program = load_program("vs_deferred_geom_instanced", "fs_deferred_geom");
1476 geom_program_instanced_.cache_uniforms();
1477
1478 sphere_ref_probe_program_.program = load_program("vs_clip_quad_ex", "reflection_probe/fs_sphere_reflection_probe");
1479 sphere_ref_probe_program_.cache_uniforms();
1480
1481 box_ref_probe_program_.program = load_program("vs_clip_quad_ex", "reflection_probe/fs_box_reflection_probe");
1482 box_ref_probe_program_.cache_uniforms();
1483
1484 debug_visualization_program_.program = load_program("vs_clip_quad", "gbuffer/fs_gbuffer_visualize");
1485 debug_visualization_program_.cache_uniforms();
1486
1487 // Color lighting.
1488
1489 // clang-format off
1490 color_lighting_no_shadow_[uint8_t(light_type::spot)].program = load_program("vs_clip_quad", "fs_deferred_spot_light");
1491 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_spot_light_hard");
1492 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_pcf");
1493 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_pcss");
1494 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_vsm");
1495 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_esm");
1496
1497 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_spot_light_hard_linear");
1498 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_pcf_linear");
1499 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_pcss_linear");
1500 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_vsm_linear");
1501 color_lighting_[uint8_t(light_type::spot)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_spot_light_esm_linear");
1502
1503 color_lighting_no_shadow_[uint8_t(light_type::point)].program = load_program("vs_clip_quad", "fs_deferred_point_light");
1504 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_point_light_hard");
1505 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_pcf");
1506 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_pcss");
1507 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_vsm");
1508 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_esm");
1509
1510 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_point_light_hard_linear");
1511 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_pcf_linear");
1512 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_pcss_linear");
1513 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_vsm_linear");
1514 color_lighting_[uint8_t(light_type::point)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_point_light_esm_linear");
1515
1516 color_lighting_no_shadow_[uint8_t(light_type::directional)].program = load_program("vs_clip_quad", "fs_deferred_directional_light");
1517 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_directional_light_hard");
1518 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_pcf");
1519 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_pcss");
1520 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_vsm");
1521 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::invz)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_esm");
1522
1523 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::hard)].program = load_program("vs_clip_quad", "fs_deferred_directional_light_hard_linear");
1524 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcf) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_pcf_linear");
1525 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::pcss) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_pcss_linear");
1526 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::vsm) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_vsm_linear");
1527 color_lighting_[uint8_t(light_type::directional)][uint8_t(sm_depth::linear)][uint8_t(sm_impl::esm) ].program = load_program("vs_clip_quad", "fs_deferred_directional_light_esm_linear");
1528 // clang-format on
1529
1530 for(auto& byLightType : color_lighting_no_shadow_)
1531 {
1532 if(byLightType.program)
1533 {
1534 byLightType.cache_uniforms();
1535 }
1536 }
1537 for(auto& byLightType : color_lighting_)
1538 {
1539 for(auto& byDepthType : byLightType)
1540 {
1541 for(auto& bySmImpl : byDepthType)
1542 {
1543 if(bySmImpl.program)
1544 {
1545 bySmImpl.cache_uniforms();
1546 }
1547 }
1548 }
1549 }
1550
1551 ibl_brdf_lut_ = am.get_asset<gfx::texture>("engine:/data/textures/ibl_brdf_lut.png");
1552
1553 return pipeline::init(ctx);
1554}
1555
1557{
1558 return true;
1559}
1560
1561
1562} // namespace rendering
1563} // namespace unravel
btVector3 normal
auto fbo_safe_get(const hpp::string_view &id) const -> const frame_buffer::ptr &
auto fbo_get_or_emplace(const hpp::string_view &id) -> frame_buffer::ptr &
auto fbo_get(const hpp::string_view &id) const -> const frame_buffer::ptr &
auto tex_get_or_emplace(const hpp::string_view &id) -> texture::ptr &
General purpose transformation class designed to maintain each component of the transformation separa...
Definition transform.hpp:27
void run(const camera &camera, gfx::render_view &rview, const run_params &params)
Manages assets, including loading, unloading, and storage.
void clear()
Clear all collected data and reset for next frame.
auto get_stats() const -> const batch_stats &
Get current statistics.
void prepare_batches(const submit_context &context)
Prepare batches for rendering (sort, split, optimize)
static auto is_static_mesh_batching_enabled() -> bool
Check if static mesh batching is enabled globally.
void collect_renderable(const batch_key &key, const batch_instance &instance)
Collect a renderable object for batching.
auto get_prepared_batches() const -> const batch_list_t &
Get prepared batches for rendering.
auto run(const run_params &params) -> gfx::frame_buffer::ptr
Executes the blit: copies params.input → params.output. Returns the actual output framebuffer.
Definition blit_pass.cpp:85
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:35
auto get_far_clip() const -> float
Retrieves the distance from the camera to the far clip plane.
Definition camera.cpp:67
auto test_obb(const math::bbox &bounds, const math::transform &t) const -> bool
Tests if the specified OBB is within the frustum.
Definition camera.cpp:444
auto get_viewport_size() const -> const usize32_t &
Retrieves the size of the viewport.
Definition camera.cpp:35
auto get_projection() const -> const math::transform &
Retrieves the current projection matrix.
Definition camera.cpp:203
static auto get_face_camera(std::uint32_t face, const math::transform &transform) -> camera
Retrieves a camera for one of six cube faces.
Definition camera.cpp:822
auto get_view() const -> const math::transform &
Retrieves the current view matrix.
Definition camera.cpp:276
void set_far_clip(float distance)
Sets the far plane distance.
Definition camera.cpp:125
auto get_position() const -> const math::vec3 &
Retrieves the current position of the camera.
Definition camera.cpp:346
void set_viewport_size(const usize32_t &viewportSize)
Sets the size of the viewport.
Definition camera.cpp:24
auto get_frustum() const -> const math::frustum &
Retrieves the current camera object frustum.
Definition camera.cpp:365
auto get_near_clip() const -> float
Retrieves the distance from the camera to the near clip plane.
Definition camera.cpp:62
Class that contains core light data, used for rendering and other purposes.
Base class for materials used in rendering.
Definition material.h:32
Main class representing a 3D mesh with support for different LODs, submeshes, and skinning.
Definition mesh.h:310
auto calculate_screen_rect(const math::transform &world, const camera &cam) const -> irect32_t
Calculates the screen rectangle of the mesh based on its world transform and the camera.
Definition mesh.cpp:1414
Class that contains core data for meshes.
Structure describing a LOD group (set of meshes), LOD transitions, and their materials.
Definition model.h:42
auto get_lod(uint32_t lod) const -> asset_handle< mesh >
Gets the LOD (Level of Detail) mesh for the specified level.
Definition model.cpp:14
auto is_valid() const -> bool
Checks if the model is valid.
Definition model.cpp:9
auto get_lod_limits() const -> const std::vector< urange32_t > &
Gets the LOD limits.
Definition model.cpp:183
void submit(const math::mat4 &world_transform, const pose_mat4 &submesh_transforms, const pose_mat4 &bone_transforms, const std::vector< pose_mat4 > &skinning_matrices, unsigned int lod, const submit_callbacks &callbacks) const
Submits the model for rendering.
Definition model.cpp:193
auto get_material_instance(uint32_t index) const -> material::sptr
Definition model.cpp:131
auto get_lods() const -> const std::vector< asset_handle< mesh > > &
Gets all the LOD meshes.
Definition model.cpp:76
Class for physically-based rendering (PBR) materials.
Definition material.h:100
auto get_surface_data2() const -> math::vec4
Gets additional surface data for the material.
Definition material.h:245
auto get_dither_threshold() const -> const math::vec2 &
Gets the dither threshold of the material.
Definition material.h:281
auto get_emissive_map() const -> const asset_handle< gfx::texture > &
Gets the emissive map of the material.
Definition material.h:389
auto get_subsurface_color() const -> const math::color &
Gets the subsurface color of the material.
Definition material.h:128
auto get_color_map() const -> const asset_handle< gfx::texture > &
Gets the color map of the material.
Definition material.h:299
auto get_surface_data() const -> const math::vec4 &
Gets the surface data of the material.
Definition material.h:236
auto get_roughness_map() const -> const asset_handle< gfx::texture > &
Gets the roughness map of the material.
Definition material.h:335
auto get_metalness_map() const -> const asset_handle< gfx::texture > &
Gets the metalness map of the material.
Definition material.h:353
auto get_tiling() const -> const math::vec2 &
Gets the tiling factor of the material.
Definition material.h:263
auto get_normal_map() const -> const asset_handle< gfx::texture > &
Gets the normal map of the material.
Definition material.h:317
auto get_base_color() const -> const math::color &
Gets the base color of the material.
Definition material.h:110
auto get_emissive_color() const -> const math::color &
Gets the emissive color of the material.
Definition material.h:146
auto get_ao_map() const -> const asset_handle< gfx::texture > &
Gets the ambient occlusion map of the material.
Definition material.h:371
auto run(const run_params &params) -> gfx::texture::ptr
Execute prefilter. Returns the filtered cubemap (output_cube or created internally).
Class that contains core reflection probe data, used for rendering and other purposes.
auto get_probe() const -> const reflection_probe &
Gets the reflection probe object.
auto deinit(rtti::context &ctx) -> bool
void set_debug_pass(int pass) override
Definition pipeline.cpp:525
void build_shadows(scene &scn, const camera &camera, visibility_flags query=visibility_query::not_specified, layer_mask render_mask=layer_mask{layer_reserved::everything_layer})
Definition pipeline.cpp:421
void run_assao_pass(const visibility_set_models_t &visibility_set, const camera &camera, gfx::render_view &rview, delta_t dt, const run_params &rparams)
Definition pipeline.cpp:943
void run_reflection_probe_pass(scene &scn, const camera &camera, gfx::render_view &rview, delta_t dt)
auto run_lighting_pass(scene &scn, const camera &camera, gfx::render_view &rview, bool apply_shadows, delta_t dt) -> gfx::frame_buffer::ptr
Definition pipeline.cpp:971
void run_debug_visualization_pass(const camera &camera, gfx::render_view &rview, const gfx::frame_buffer::ptr &output)
auto run_ssr_pass(const camera &camera, gfx::render_view &rview, const gfx::frame_buffer::ptr &output, const run_params &rparams) -> gfx::frame_buffer::ptr
void build_reflections(scene &scn, const camera &camera, delta_t dt)
Definition pipeline.cpp:335
auto run_fxaa_pass(gfx::render_view &rview, const gfx::frame_buffer::ptr &input, const gfx::frame_buffer::ptr &output, const run_params &rparams) -> gfx::frame_buffer::ptr
void run_g_buffer_pass(const visibility_set_models_t &visibility_set, const camera &camera, gfx::render_view &rview, delta_t dt)
Definition pipeline.cpp:608
auto run_atmospherics_pass(gfx::frame_buffer::ptr input, scene &scn, const camera &camera, gfx::render_view &rview, delta_t dt) -> gfx::frame_buffer::ptr
auto run_tonemapping_pass(gfx::render_view &rview, const gfx::frame_buffer::ptr &input, const gfx::frame_buffer::ptr &output, const run_params &rparams) -> gfx::frame_buffer::ptr
auto run_hiz_pass(const camera &camera, gfx::render_view &rview, delta_t dt) -> gfx::texture::ptr
auto init(rtti::context &ctx) -> bool override
auto run_pipeline(scene &scn, const camera &camera, gfx::render_view &rview, delta_t dt, const run_params &params, layer_mask render_mask=layer_mask{layer_reserved::everything_layer}) -> gfx::frame_buffer::ptr override
Renders the entire scene from the camera's perspective.
Definition pipeline.cpp:494
void run_pipeline_impl(const gfx::frame_buffer::ptr &output, scene &scn, const camera &camera, gfx::render_view &rview, delta_t dt, const run_params &params, pipeline_flags pflags, layer_mask render_mask=layer_mask{layer_reserved::everything_layer})
Definition pipeline.cpp:530
virtual void particle_pass(scene &scn, const camera &camera, gfx::render_view &rview, const gfx::frame_buffer::ptr &output)
Definition pipeline.cpp:215
virtual auto init(rtti::context &ctx) -> bool
Definition pipeline.cpp:25
virtual auto gather_visible_models(scene &scn, const math::frustum *frustum, visibility_flags query, const layer_mask &render_mask) -> visibility_set_models_t
Gathers visible models from the scene based on the given query.
Definition pipeline.cpp:55
@ not_specified
No specific visibility query.
Definition pipeline.h:91
@ is_dirty
Query for dirty entities.
Definition pipeline.h:92
@ is_reflection_caster
Query for reflection casting entities.
Definition pipeline.h:95
@ is_shadow_caster
Query for shadow casting entities.
Definition pipeline.h:94
prefilter_pass prefilter_pass_
Definition pipeline.h:184
virtual void ui_pass(scene &scn, const camera &camera, gfx::render_view &rview, const gfx::frame_buffer::ptr &output)
Definition pipeline.cpp:184
uint32_t visibility_flags
Type alias for visibility flags.
Definition pipeline.h:98
virtual auto create_run_params(entt::handle camera_ent) const -> rendering::pipeline::run_params
Definition pipeline.cpp:125
Class that contains sky light data.
sky_mode
Enumeration for sky modes.
Component that handles transformations (position, rotation, scale, etc.) in the ACE framework.
auto get_transform_global() const noexcept -> const math::transform &
Gets the global transform.
size< std::uint32_t > usize32_t
std::chrono::duration< float > delta_t
#define APPLOG_WARNING(...)
Definition logging.h:19
Definition cache.hpp:11
void submit(view_id _id, program_handle _handle, int32_t _depth, bool _preserveState)
Definition graphics.cpp:900
uint16_t set_scissor(uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height)
Definition graphics.cpp:779
void set_state(uint64_t _state, uint32_t _rgba)
Definition graphics.cpp:764
void discard(uint8_t _flags)
Definition graphics.cpp:969
void set_uniform(uniform_handle _handle, const void *_value, uint16_t _num)
Definition graphics.cpp:804
uint32_t get_render_frame()
auto clip_quad(float depth, float width, float height) -> uint64_t
void set_texture(uint8_t _stage, uniform_handle _sampler, texture_handle _handle, uint32_t _flags)
Definition graphics.cpp:890
auto inverse(transform_t< T, Q > const &t) noexcept -> transform_t< T, Q >
hpp::small_vector< entt::handle > visibility_set_models_t
Definition pipeline.h:48
@ everything_layer
Definition layer_mask.h:16
@ sphere
Sphere type reflection probe.
@ box
Box type reflection probe.
@ environment
Environment reflection method.
#define APP_SCOPE_PERF(name)
Create a scoped performance timer that only accepts string literals.
Definition profiler.h:160
entt::entity entity
Represents a handle to an asset, providing access and management functions.
auto is() const -> bool
static void pop_scope()
void set_view_proj(const float *v, const float *p)
gfx::view_id id
Definition render_pass.h:98
void clear(uint16_t _flags, uint32_t _rgba=0x000000ff, float _depth=1.0f, uint8_t _stencil=0) const
static void push_scope(const char *name)
void bind(const frame_buffer *fb=nullptr) const
Storage for box vector values and wraps up common functionality.
Definition bbox.h:21
bbox & mul(const transform &t)
Transforms an axis aligned bounding box by the specified matrix.
Definition bbox.cpp:876
vec4 value
Definition color.h:80
std::uint32_t value_type
Definition basetypes.hpp:11
bool contains(const T &val) const
Definition basetypes.hpp:25
T width() const
std::int32_t value_type
T height() const
T width
Definition basetypes.hpp:55
T height
Definition basetypes.hpp:56
gfx::frame_buffer::ptr input
Source framebuffer (must have a color texture).
Definition blit_pass.h:18
gfx::frame_buffer::ptr output
Optional destination framebuffer. If null, will be created to match input.
Definition blit_pass.h:19
static auto context() -> rtti::context &
Definition engine.cpp:116
gfx::frame_buffer::ptr output
Definition fxaa_pass.h:18
gfx::frame_buffer::ptr input
Definition fxaa_pass.h:17
gfx::texture::ptr depth_buffer
Source depth buffer.
Definition hiz_pass.h:19
const camera * cam
Camera for near/far plane information.
Definition hiz_pass.h:21
gfx::texture::ptr output_hiz
Output Hi-Z texture (must be R32F or R16F format with mips)
Definition hiz_pass.h:20
static auto packed_size() -> size_t
float range
The range of the point light.
Definition light.h:162
float exponent_falloff
The exponent falloff for the point light.
Definition light.h:164
float get_range() const
Gets the range of the spot light.
Definition light.h:106
float get_outer_angle() const
Gets the outer angle of the spot light.
Definition light.h:121
float get_inner_angle() const
Gets the inner angle of the spot light.
Definition light.h:136
Struct representing a light.
Definition light.h:87
bool casts_shadows
Whether the light casts shadows.
Definition light.h:215
float intensity
The intensity of the light.
Definition light.h:209
light_type type
The type of the light.
Definition light.h:89
math::color color
The color of the light.
Definition light.h:207
point point_data
Data specific to point lights.
Definition light.h:203
float ambient_intensity
The ambient intensity of the light.
Definition light.h:212
spot spot_data
Data specific to spot lights.
Definition light.h:201
Parameters for the submit callbacks.
Definition model.h:132
bool skinned
Indicates if the model is skinned.
Definition model.h:134
Callbacks for submitting the model for rendering.
Definition model.h:126
std::function< void(const params &info, const material &)> setup_params_per_submesh
Callback for setting up per submesh.
Definition model.h:143
std::function< void(const params &info)> setup_begin
Callback for setup begin.
Definition model.h:139
std::function< void(const params &info)> setup_params_per_instance
Callback for setting up per instance.
Definition model.h:141
std::function< void(const params &info)> setup_end
Callback for setup end.
Definition model.h:145
std::vector< math::mat4 > transforms
Vector of bone transforms.
Definition model.h:27
std::array< gfx::texture::ptr, 6 > input_faces
gfx::texture::ptr output_cube_prefiltered
Optional destination cubemap (will be created if null).
gfx::texture::ptr output_cube
Optional destination cubemap (will be created if null).
bool apply_prefilter
If false, copies mips from input to output.
Contains level of detail (LOD) data for an entity.
Definition pipeline.h:41
float current_time
Current time for LOD transition.
Definition pipeline.h:44
std::uint32_t target_lod_index
Target LOD index.
Definition pipeline.h:43
std::uint32_t current_lod_index
Current LOD index.
Definition pipeline.h:42
std::function< void(assao_pass::run_params &params)> fill_assao_params
Definition pipeline.h:104
std::function< void(tonemapping_pass::run_params &params)> fill_hdr_params
Definition pipeline.h:105
void add_batch_stats(const batch_stats &stats)
Add batch statistics to the pipeline stats.
Definition pipeline.cpp:327
Represents a scene in the ACE framework, managing entities and their relationships.
Definition scene.h:21
std::unique_ptr< entt::registry > registry
The registry that manages all entities in the scene.
Definition scene.h:117
auto create_handle(entt::entity e) -> entt::handle
Creates an entity in the scene.
Definition scene.cpp:307
bool enable_cone_tracing
Enable cone tracing for glossy reflections.
Definition ssr_pass.h:51
gfx::frame_buffer::ptr output
Optional output buffer.
Definition ssr_pass.h:68
gfx::texture::ptr previous_frame
Previous frame color for reflection sampling.
Definition ssr_pass.h:71
gfx::frame_buffer::ptr g_buffer
G-buffer containing normals.
Definition ssr_pass.h:69
gfx::texture::ptr hiz_buffer
Hi-Z buffer texture.
Definition ssr_pass.h:70
fidelityfx_ssr_settings fidelityfx
FidelityFX SSR settings.
Definition ssr_pass.h:63
gfx::uniform_handle handle
Definition uniform.cpp:9