GCC Code Coverage Report


./
Coverage:
low: ≥ 0%
medium: ≥ 75.0%
high: ≥ 90.0%
Lines:
0 of 375, 0 excluded
0.0%
Functions:
0 of 40, 0 excluded
0.0%
Branches:
0 of 458, 0 excluded
0.0%

libs/render/src/eu/render/postproc.cc
Line Branch Exec Source
1 #include "eu/render/postproc.h"
2
3 #include "eu/assert/assert.h"
4
5 #include "eu/log/log.h"
6
7 #include "eu/render/opengl_utils.h"
8 #include "eu/render/postproc.internal.h"
9 #include "eu/render/renderer.h"
10 #include "eu/render/renderer.pimpl.h"
11 #include "eu/render/shader_resource.h"
12 #include "eu/render/shader.h"
13 #include "eu/render/state.h"
14 #include "eu/render/camera.h"
15 #include "eu/render/shadow.h"
16
17 #if FF_HAS(EU_DEBUG_RUNNER)
18 #include "eu/imgui/ui.h"
19 #include "dear_imgui/imgui.h"
20 #endif
21
22
23 namespace eu::render
24 {
25
26
27 ///////////////////////////////////////////////////////////////////////////////////////////////////
28 // Effect
29
30 bool Effect::enabled() const
31 {
32 return is_enabled;
33 }
34
35 void Effect::set_enabled(bool n)
36 {
37 if (is_enabled == n)
38 {
39 return;
40 }
41
42 is_enabled = n;
43 if (owner != nullptr)
44 {
45 owner->dirty = true;
46 }
47 }
48
49
50 ///////////////////////////////////////////////////////////////////////////////////////////////////
51 // RenderWorld
52
53 // todo(Gustav): should this be a user config option? evaluate higher/lower bits, probably 16 or 32 since it needs to be floating point for hdr
54 constexpr ColorBitsPerPixel render_world_color_bits_per_pixel = ColorBitsPerPixel::use_16;
55
56 std::optional<BloomRender> build_bloom(const Size& size, ExtractShader* sh, PingPongBlurShader* ping_sh)
57 {
58 if (sh == nullptr) { return std::nullopt; }
59
60 auto bloom_buffer = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("bloom extraction buffer") size);
61
62 auto ping_one = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("ping-pong bloom buffer one") size);
63 auto ping_two = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("ping-pong bloom buffer two") size);
64
65 return BloomRender{sh, bloom_buffer, ping_sh, {ping_one, ping_two}};
66 }
67
68 RenderWorld::RenderWorld(const Size size, RealizeShader* re_sh, ExtractShader* ex_sh, PingPongBlurShader* ping_sh, int msaa_samples, bool* h, float* e)
69 : window_size(size)
70 , use_hdr(h)
71 , exposure(e)
72 , msaa_buffer(build_msaa_framebuffer(USE_DEBUG_LABEL_MANY("msaa buffer")
73 size, msaa_samples, render_world_color_bits_per_pixel))
74 , realized_buffer(build_hdr_floating_framebuffer(USE_DEBUG_LABEL_MANY("realized msaa buffer") size, render_world_color_bits_per_pixel))
75 , realize_shader(re_sh)
76 , bloom_render(build_bloom(size, ex_sh, ping_sh))
77 , last_bloom_blur_index(0)
78 {
79 ASSERT(use_hdr);
80 ASSERT(exposure);
81 }
82
83 void RenderWorld::update(const PostProcArg& arg)
84 {
85 // render shadow buffer
86 const auto shadow_size = arg.renderer->settings.shadow_map_resolution;
87 {
88 bool update_shadow_buffer = shadow_buffer == nullptr;
89 if (shadow_buffer && shadow_buffer->size != shadow_size)
90 {
91 update_shadow_buffer = true;
92 }
93
94 if (update_shadow_buffer)
95 {
96 shadow_buffer = build_shadow_framebuffer(USE_DEBUG_LABEL_MANY("shadow buffer") shadow_size);
97 }
98 }
99
100 std::optional<CompiledCamera> opt_compiled_shadow_camera;
101
102 if (arg.world->lights.directional_lights.empty() == false)
103 {
104 SCOPED_DEBUG_GROUP("render shadow buffer"sv);
105
106 const auto compiled_shadow_camera = compile_the_shadow_camera(
107 *arg.camera, window_size, arg.world->lights.directional_lights[0], arg.renderer->settings, *arg.world
108 );
109
110 auto bound = BoundFbo{shadow_buffer};
111 set_gl_viewport({shadow_buffer->size.width, shadow_buffer->size.height});
112 arg.renderer->render_shadows(shadow_size, *arg.world, compiled_shadow_camera);
113 }
114
115 // render into msaa buffer
116 {
117 SCOPED_DEBUG_GROUP("rendering into msaa buffer"sv);
118
119 const ShadowContext shadow_context = opt_compiled_shadow_camera ? ShadowContext{
120 .directional_shadow_map = shadow_buffer.get(),
121 .directional_shadow_clip_from_world = opt_compiled_shadow_camera->clip_from_view * opt_compiled_shadow_camera->view_from_world
122 } : ShadowContext{
123 .directional_shadow_map = nullptr,
124 .directional_shadow_clip_from_world = m4_identity
125 };
126
127 auto bound = BoundFbo{msaa_buffer};
128 set_gl_viewport({msaa_buffer->size.width, msaa_buffer->size.height});
129 arg.renderer->render_world(window_size, *arg.world, compile(*arg.camera, window_size), shadow_context);
130 }
131
132 // copy msaa buffer to realized
133 {
134 SCOPED_DEBUG_GROUP("resolving msaa buffer"sv);
135 resolve_multisampled_buffer(*msaa_buffer, realized_buffer.get());
136 }
137
138 // extract overexposed
139 if (bloom_render.has_value())
140 {
141 // extract overexposed pixels
142 {
143 SCOPED_DEBUG_GROUP("extracting to overexposed buffer"sv);
144 StateChanger{arg.renderer->pimpl->states}
145 .cull_face(false)
146 .stencil_test(false)
147 .depth_test(false)
148 .depth_mask(false)
149 .blending(false);
150
151 glClearColor(0, 0, 0, 1.0f);
152 glClear(GL_COLOR_BUFFER_BIT);
153
154 // todo(Gustav): why bind this late... shouldn't this be first???
155 auto bound = BoundFbo{bloom_render->bloom_buffer};
156
157 {
158 const auto& container = bloom_render->extract_shader;
159 const auto& shader = container->shader;
160 const auto& program = shader->program;
161 program->use();
162 program->set_float(container->cutoff_uniform, arg.renderer->settings.bloom_cutoff);
163 program->set_float(container->softness_uniform, arg.renderer->settings.bloom_softness);
164 bind_texture_2d(arg.renderer->pimpl->states, shader->tex_input_uniform, *realized_buffer);
165
166 render_geom(*arg.renderer->pimpl->full_screen_geom);
167 }
168 }
169
170 // blur the buffers
171 {
172 SCOPED_DEBUG_GROUP("blur extracted pixels for bloom"sv);
173 bool is_horizontal = true;
174 for (int blur_iteration = 0; blur_iteration < arg.renderer->settings.bloom_blur_steps; blur_iteration += 1)
175 {
176 const auto is_first_iteration = blur_iteration == 0;
177
178 const std::size_t source_index = is_horizontal ? 1 : 0;
179 const std::size_t target_index = is_horizontal ? 0 : 1;
180 last_bloom_blur_index = target_index;
181
182 SCOPED_DEBUG_GROUP(
183 fmt::format("blur pass #{} ({}, {})",
184 blur_iteration,
185 (is_horizontal ? "hori" : "vert"),
186 (is_first_iteration ? "first" : "after"))
187 );
188 auto bound = BoundFbo{bloom_render->ping_pong_buffer[target_index]};
189 StateChanger{arg.renderer->pimpl->states}
190 .cull_face(false)
191 .stencil_test(false)
192 .depth_test(false)
193 .depth_mask(false)
194 .blending(false);
195
196 glClearColor(0, 0, 0, 1.0f);
197 glClear(GL_COLOR_BUFFER_BIT);
198
199 {
200 const auto& container = bloom_render->ping_pong_shader;
201 const auto& shader = container->shader;
202 const auto& program = shader->program;
203 program->use();
204 program->set_bool(container->is_horizontal_uniform, is_horizontal);
205
206 auto& src_texture = is_first_iteration ? bloom_render->bloom_buffer
207 : bloom_render->ping_pong_buffer[source_index];
208 bind_texture_2d(arg.renderer->pimpl->states, shader->tex_input_uniform, *src_texture);
209
210 render_geom(*arg.renderer->pimpl->full_screen_geom);
211 }
212
213 is_horizontal = ! is_horizontal;
214 }
215 }
216 }
217
218 // blur overexposed using ping-pong gaussian blur
219 }
220
221 void RenderWorld::render(const PostProcArg& arg)
222 {
223 // render realized (with shader)
224 SCOPED_DEBUG_GROUP("render realized msaa buffer"sv);
225 StateChanger{arg.renderer->pimpl->states}
226 .cull_face(false)
227 .stencil_test(false)
228 .depth_test(false)
229 .depth_mask(false)
230 .blending(false);
231
232 glClearColor(0, 0, 0, 1.0f);
233 glClear(GL_COLOR_BUFFER_BIT);
234
235 ASSERT(use_hdr);
236 ASSERT(exposure);
237
238 {
239 const auto& container = realize_shader;
240 const auto& program = container->program;
241 program->use();
242 program->set_float(container->gamma_uniform, arg.renderer->settings.gamma);
243 program->set_float(container->exposure_uniform, *use_hdr ? *exposure : -1.0f);
244 program->set_bool(container->use_blur_uniform, bloom_render.has_value());
245 bind_texture_2d(arg.renderer->pimpl->states, container->tex_input_uniform, *realized_buffer);
246 if (bloom_render.has_value())
247 {
248 bind_texture_2d(arg.renderer->pimpl->states, container->tex_blurred_bloom_uniform, *bloom_render->ping_pong_buffer[last_bloom_blur_index]);
249 }
250
251 render_geom(*arg.renderer->pimpl->full_screen_geom);
252 }
253 }
254
255
256
257 void RenderWorld::gui(imgui::ImguiShaderCache* cache)
258 {
259 // todo(Gustav): refactor
260 #if FF_HAS(EU_DEBUG_RUNNER)
261 if (bloom_render && bloom_render->bloom_buffer)
262 {
263 imgui::imgui_image("bloom buffer", *bloom_render->bloom_buffer, cache, imgui::ImageShader::TonemapAndGamma);
264
265 imgui::imgui_image("ping-pong A", *bloom_render->ping_pong_buffer[0], cache, imgui::ImageShader::TonemapAndGamma);
266 imgui::imgui_image("ping-pong B", *bloom_render->ping_pong_buffer[1], cache, imgui::ImageShader::TonemapAndGamma);
267 }
268 #endif
269 }
270
271
272 ///////////////////////////////////////////////////////////////////////////////////////////////////
273 // RenderTextureWithShader
274
275 RenderTextureWithShader::RenderTextureWithShader(std::string n, std::shared_ptr<RenderSource> s, std::shared_ptr<FrameBuffer> f, ShaderPropertyProvider* e)
276 : name(std::move(n))
277 , source(std::move(s))
278 , fbo(std::move(f))
279 , effect(e)
280 {
281 ASSERT(effect);
282 }
283
284 void RenderTextureWithShader::render(const PostProcArg& arg)
285 {
286 ASSERT(effect);
287
288 SCOPED_DEBUG_GROUP(fmt::format("Rendering task {}", name));
289
290 StateChanger{arg.renderer->pimpl->states}
291 .cull_face(false)
292 .stencil_test(false)
293 .depth_test(false)
294 .depth_mask(false)
295 .blending(false);
296
297 glClearColor(0, 0, 0, 1.0f);
298 glClear(GL_COLOR_BUFFER_BIT);
299
300 effect->use_shader(arg, *fbo);
301 render_geom(*arg.renderer->pimpl->full_screen_geom);
302 }
303
304 void RenderTextureWithShader::update(const PostProcArg& arg)
305 {
306 SCOPED_DEBUG_GROUP(fmt::format("Updating task {}", name));
307 auto bound = BoundFbo{fbo};
308 set_gl_viewport(fbo->size);
309 source->render(arg);
310 }
311
312
313 ///////////////////////////////////////////////////////////////////////////////////////////////////
314 // EffectStack
315
316 void EffectStack::update(float dt) const
317 {
318 for (const auto& e: effects)
319 {
320 if (e->is_enabled == false)
321 {
322 continue;
323 }
324
325 e->update(dt);
326 }
327 }
328
329 void EffectStack::render(const PostProcArg& arg)
330 {
331 // todo(Gustav): owners are only used when we have added an effect and the effect can change dirty flag
332 // make this a requirement when creating the effect instead and remove this loop?
333 for (const auto& e: effects)
334 {
335 if (e->owner != this)
336 {
337 e->owner = this;
338 dirty = true;
339 }
340 }
341
342 if (render_world == nullptr)
343 {
344 dirty = true;
345 }
346
347 if (window_size.has_value() == false || *window_size != arg.window_size)
348 {
349 window_size = arg.window_size;
350 dirty = true;
351 }
352
353 {
354 const auto latest_msaa = arg.renderer->settings.msaa;
355 if (current_msaa_setting != latest_msaa)
356 {
357 current_msaa_setting = latest_msaa;
358 dirty = true;
359 }
360 }
361
362 const auto should_use_bloom = arg.renderer->settings.use_bloom;
363 {
364 const auto is_currently_using_bloom = render_world && render_world->bloom_render.has_value();
365 if (is_currently_using_bloom != should_use_bloom)
366 {
367 dirty = true;
368 }
369 }
370
371 // if dirty, update the compiled state
372 if (dirty)
373 {
374 dirty = false;
375 LOG_INFO("Building effects stack");
376
377 compiled.targets.clear();
378
379 auto created_world = std::make_shared<RenderWorld>(
380 arg.window_size,
381 &arg.renderer->pimpl->shaders_resources.pp_realize,
382 should_use_bloom ? &arg.renderer->pimpl->shaders_resources.pp_extract : nullptr,
383 should_use_bloom ? &arg.renderer->pimpl->shaders_resources.pp_ping : nullptr,
384 current_msaa_setting,
385 &use_hdr,
386 &exposure
387 );
388 compiled.last_source = created_world;
389 render_world = created_world;
390
391 for (auto& e: effects)
392 {
393 if (e->is_enabled)
394 {
395 e->build({&compiled, arg.window_size});
396 }
397 }
398 }
399
400 // the stack is now compiled
401 // before rendering, update all targets/fbos and present
402 {
403 SCOPED_DEBUG_GROUP("updating postproc render_world"sv);
404 render_world->update(arg);
405 }
406
407 {
408 SCOPED_DEBUG_GROUP("rendering postproc actions"sv);
409 for (const auto& action: compiled.targets)
410 {
411 action->update(arg);
412 }
413 }
414
415 // render the final image to the screen
416 {
417 SCOPED_DEBUG_GROUP("rendering postproc to screen"sv);
418 set_gl_viewport(arg.final_rect);
419 compiled.last_source->render(arg);
420 }
421 }
422
423 void EffectStack::gui(imgui::ImguiShaderCache* cache)
424 {
425 #if FF_HAS(EU_DEBUG_RUNNER)
426 ImGui::Checkbox("HDR", &use_hdr);
427 ImGui::SliderFloat("Exposure", &exposure, 0.01f, 20.0f);
428
429 if (render_world)
430 {
431 render_world->gui(cache);
432 }
433
434 int index = 0;
435 for (const auto& e: effects)
436 {
437 ImGui::PushID(index);
438 e->gui();
439 ImGui::PopID();
440 index += 1;
441 }
442 #endif
443 }
444
445
446 ///////////////////////////////////////////////////////////////////////////////////////////////////
447 // FactorEffect
448
449 FactorEffect::FactorEffect()
450 {
451 set_enabled(false);
452 }
453
454 float FactorEffect::get_factor() const
455 {
456 return factor;
457 }
458
459 void FactorEffect::set_factor(float f)
460 {
461 factor = f;
462 set_enabled(factor > ALMOST_ZERO);
463 }
464
465
466
467 ///////////////////////////////////////////////////////////////////////////////////////////////////
468 // FloatDragShaderProp
469
470 FloatDragShaderProp::FloatDragShaderProp(const LoadedPostProcShader& shader, const std::string& n, float v, float s)
471 : uniform(shader.program->get_uniform(n))
472 , name(n)
473 , value(v)
474 , speed(s)
475 {
476 }
477
478 void FloatDragShaderProp::use(const PostProcArg&, ShaderProgram& shader)
479 {
480 shader.set_float(uniform, value);
481 }
482
483 void FloatDragShaderProp::gui()
484 {
485 #if FF_HAS(EU_DEBUG_RUNNER)
486 ImGui::DragFloat(name.c_str(), &value, speed);
487 #endif
488 }
489
490
491
492 ///////////////////////////////////////////////////////////////////////////////////////////////////
493 // FloatSliderShaderProp
494
495 FloatSliderShaderProp::FloatSliderShaderProp(
496 const LoadedPostProcShader& shader, const std::string& n, float v, float mi, float ma
497 )
498 : uniform(shader.program->get_uniform(n))
499 , name(n)
500 , value(v)
501 , min(mi)
502 , max(ma)
503 {
504 }
505
506 void FloatSliderShaderProp::use(const PostProcArg&, ShaderProgram& shader)
507 {
508 shader.set_float(uniform, value);
509 }
510
511 void FloatSliderShaderProp::gui()
512 {
513 #if FF_HAS(EU_DEBUG_RUNNER)
514 ImGui::SliderFloat(name.c_str(), &value, min, max);
515 #endif
516 }
517
518
519 ///////////////////////////////////////////////////////////////////////////////////////////////////
520 // SimpleEffect
521
522 SimpleEffect::SimpleEffect(std::string n, std::shared_ptr<LoadedPostProcShader> s)
523 : name(std::move(n))
524 , shader(std::move(s))
525 {
526 ASSERT(shader->factor_uni.has_value());
527 }
528
529 void SimpleEffect::add_float_drag_prop(const std::string& prop_name, float value, float speed)
530 {
531 properties.emplace_back(std::make_shared<FloatDragShaderProp>(*shader, prop_name, value, speed));
532 }
533
534 void SimpleEffect::add_float_slider_prop(const std::string& prop_name, float value, float min, float max)
535 {
536 properties.emplace_back(std::make_shared<FloatSliderShaderProp>(*shader, prop_name, value, min, max));
537 }
538
539 void SimpleEffect::gui()
540 {
541 #if FF_HAS(EU_DEBUG_RUNNER)
542 if (properties.empty())
543 {
544 return;
545 }
546
547 int index = 0;
548
549 if (ImGui::TreeNode(name.c_str()) == false)
550 {
551 return;
552 }
553
554 for (auto& p: properties)
555 {
556 ImGui::PushID(index);
557 p->gui();
558 ImGui::PopID();
559 index += 1;
560 }
561
562 ImGui::TreePop();
563 #endif
564 }
565
566 void SimpleEffect::update(float dt)
567 {
568 time += dt;
569 }
570
571 v2 v2_from_size(const Size& s)
572 {
573 return { float_from_int(s.width), float_from_int(s.height) };
574 }
575
576 void SimpleEffect::use_shader(const PostProcArg& a, const FrameBuffer& t)
577 {
578 shader->program->use();
579
580 const auto& shader_factor = shader->factor_uni;
581 const auto& shader_resolution = shader->resolution_uni;
582 const auto& shader_time = shader->time_uni;
583
584 if (shader_factor)
585 {
586 shader->program->set_float(*shader_factor, get_factor());
587 }
588 if (shader_resolution)
589 {
590 shader->program->set_vec2(*shader_resolution, v2_from_size(a.window_size));
591 }
592 if (shader_time)
593 {
594 shader->program->set_float(*shader_time, time);
595 }
596 for (auto& p: properties)
597 {
598 p->use(a, *shader->program);
599 }
600 bind_texture_2d(a.renderer->pimpl->states, shader->tex_input_uniform, t);
601 }
602
603 void SimpleEffect::build(const BuildArg& arg)
604 {
605 time = 0.0f;
606
607 // todo(Gustav): reuse buffers created from an earlier postproc build
608 // todo(Gustav): reuse buffers from earlier in the postproc stack, that aren't in use
609 auto fbo = build_simple_framebuffer(USE_DEBUG_LABEL_MANY(fmt::format("fbo for {}", name)) arg.window_size);
610
611 auto src = arg.builder->last_source;
612 auto target = std::make_shared<RenderTextureWithShader>(name, src, fbo, this);
613
614 arg.builder->targets.emplace_back(target);
615 arg.builder->last_source = target;
616 }
617
618
619
620 ///////////////////////////////////////////////////////////////////////////////////////////////////
621 // VertProvider
622
623 BlurVerticalProvider::BlurVerticalProvider(BlurEffect* b)
624 : blur(b)
625 {
626 }
627
628
629 void BlurVerticalProvider::use_shader(const PostProcArg& a, const FrameBuffer& t)
630 {
631 blur->use_vert_shader(a, t);
632 }
633
634
635
636 ///////////////////////////////////////////////////////////////////////////////////////////////////
637 // HoriProvider
638
639 BurHorizontalProvider::BurHorizontalProvider(BlurEffect* b)
640 : blur(b)
641 {
642 }
643
644 void BurHorizontalProvider::use_shader(const PostProcArg& a, const FrameBuffer& t)
645 {
646 blur->use_hori_shader(a, t);
647 }
648
649
650
651
652 ///////////////////////////////////////////////////////////////////////////////////////////////////
653 // BlurEffect
654
655
656 BlurEffect::BlurEffect(std::string n, std::shared_ptr<LoadedPostProcShader> v, std::shared_ptr<LoadedPostProcShader> h)
657 : name(std::move(n))
658 , vert_p(this)
659 , hori_p(this)
660 , vert(std::move(v))
661 , hori(std::move(h))
662 , blur_size_v(vert->program->get_uniform("u_blur_size"))
663 , blur_size_h(hori->program->get_uniform("u_blur_size"))
664 #if FF_HAS(BLUR_USE_GAUSS)
665 , std_dev_v(vert->program->get_uniform("u_std_dev"))
666 , std_dev_h(hori->program->get_uniform("u_std_dev"))
667 #endif
668 {
669 ASSERT(vert->factor_uni.has_value());
670 ASSERT(hori->factor_uni.has_value());
671 }
672
673 void BlurEffect::gui()
674 {
675 #if FF_HAS(EU_DEBUG_RUNNER)
676 if (ImGui::TreeNode(name.c_str()) == false)
677 {
678 return;
679 }
680
681 ImGui::SliderFloat("Blur size", &blur_size, 0.0f, 0.2f);
682 #if FF_HAS(BLUR_USE_GAUSS)
683 ImGui::SliderFloat("Standard deviation", &std_dev, 0.0f, 0.1f);
684 #endif
685
686 ImGui::TreePop();
687 #endif
688 }
689
690 void BlurEffect::update(float)
691 {
692 // no update needed
693 }
694
695 void BlurEffect::use_vert_shader(const PostProcArg& a, const FrameBuffer& t) const
696 {
697 const auto& factor_uniform = vert->factor_uni;
698
699 vert->program->use();
700 ASSERT(factor_uniform);
701 if (factor_uniform)
702 {
703 vert->program->set_float(*factor_uniform, get_factor());
704 }
705 vert->program->set_float(blur_size_v, blur_size);
706 #if FF_HAS(BLUR_USE_GAUSS)
707 vert->program->set_float(std_dev_v, std_dev);
708 #endif
709 bind_texture_2d(a.renderer->pimpl->states, vert->tex_input_uniform, t);
710 }
711
712 void BlurEffect::use_hori_shader(const PostProcArg& a, const FrameBuffer& t)
713 {
714 const auto& factor_uniform = hori->factor_uni;
715 const auto& resolution_uniform = hori->resolution_uni;
716
717 hori->program->use();
718 ASSERT(factor_uniform);
719 if (factor_uniform)
720 {
721 hori->program->set_float(*factor_uniform, get_factor());
722 }
723 ASSERT(resolution_uniform);
724 if (resolution_uniform)
725 {
726 hori->program->set_vec2(*resolution_uniform, v2_from_size(a.window_size));
727 }
728 hori->program->set_float(blur_size_h, blur_size);
729 #if FF_HAS(BLUR_USE_GAUSS)
730 hori->program->set_float(std_dev_h, std_dev);
731 #endif
732 bind_texture_2d(a.renderer->pimpl->states, hori->tex_input_uniform, t);
733 }
734
735 void BlurEffect::build(const BuildArg& arg)
736 {
737 auto src = arg.builder->last_source;
738
739 // todo(Gustav): modify resolution to get better blur and at a lower cost!
740
741 // step 1: vertical
742 auto fbo_v = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("blur vertical") arg.window_size);
743 auto target_v = std::make_shared<RenderTextureWithShader>("blur vertical", src, fbo_v, &vert_p);
744 arg.builder->targets.emplace_back(target_v);
745
746 // step 2: horizontal
747 auto fbo_h = build_simple_framebuffer(USE_DEBUG_LABEL_MANY("blur horizontal") arg.window_size);
748 auto target_h = std::make_shared<RenderTextureWithShader>("blur horizontal", target_v, fbo_h, &hori_p);
749 arg.builder->targets.emplace_back(target_h);
750
751 // done
752 arg.builder->last_source = target_h;
753 }
754
755 ///////////////////////////////////////////////////////////////////////////////////////////////////
756 // Renderer
757 #if 0
758 std::shared_ptr<FactorEffect> Renderer::make_invert_effect() const
759 {
760 return std::make_shared<SimpleEffect>("Invert", pimpl->shaders_resources.pp_invert);
761 }
762
763 std::shared_ptr<FactorEffect> Renderer::make_grayscale_effect() const
764 {
765 return std::make_shared<SimpleEffect>("Grayscale", pimpl->shaders_resources.pp_grayscale);
766 }
767
768 std::shared_ptr<FactorEffect> Renderer::make_damage_effect() const
769 {
770 auto r = std::make_shared<SimpleEffect>("Damage", pimpl->shaders_resources.pp_damage);
771 r->add_float_drag_prop("u_vignette_radius", 0.13f, 0.01f);
772 r->add_float_slider_prop("u_vignette_smoothness", 1.0f, 0.001f, 1.0f);
773 r->add_float_slider_prop("u_vignette_darkening", 1.0f, 0.0f, 1.0f);
774 r->add_float_drag_prop("u_noise_scale", 25.0f, 1.0f);
775 return r;
776 }
777
778 std::shared_ptr<FactorEffect> Renderer::make_blur_effect() const
779 {
780 return std::make_shared<BlurEffect>("Blur", pimpl->shaders_resources.pp_blurv, pimpl->shaders_resources.pp_blurh);
781 }
782 #endif
783
784 } // namespace eu::render
785