Unravel Engine C++ Reference
Loading...
Searching...
No Matches
bloom_pass.cpp
Go to the documentation of this file.
1#include "bloom_pass.h"
6#include <graphics/texture.h>
7#include <math/math.h>
8
9namespace unravel
10{
11
13{
14 auto& am = ctx.get_cached<asset_manager>();
15
16 auto vs_clip_quad = am.get_asset<gfx::shader>("engine:/data/shaders/vs_clip_quad.sc");
17 auto fs_downsample = am.get_asset<gfx::shader>("engine:/data/shaders/bloom/fs_bloom_downsample.sc");
18 auto fs_upsample = am.get_asset<gfx::shader>("engine:/data/shaders/bloom/fs_bloom_upsample.sc");
19 auto fs_combine = am.get_asset<gfx::shader>("engine:/data/shaders/bloom/fs_bloom_combine.sc");
20
21 downsample_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_downsample);
22 downsample_program_.cache_uniforms();
23
24 upsample_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_upsample);
25 upsample_program_.cache_uniforms();
26
27 combine_program_.program = std::make_unique<gpu_program>(vs_clip_quad, fs_combine);
28 combine_program_.cache_uniforms();
29
30 return downsample_program_.program->is_valid() && upsample_program_.program->is_valid() &&
31 combine_program_.program->is_valid();
32}
33
34auto bloom_pass::create_or_resize_mip_chain(gfx::render_view& rview,
35 const usize32_t& viewport_size,
36 int mip_count) -> void
37{
38 const uint64_t flags = BGFX_TEXTURE_RT | BGFX_SAMPLER_U_CLAMP | BGFX_SAMPLER_V_CLAMP;
39
40 for(int i = 0; i < mip_count; ++i)
41 {
42 uint32_t w = viewport_size.width >> (i + 1);
43 uint32_t h = viewport_size.height >> (i + 1);
44 if(w < 1)
45 w = 1;
46 if(h < 1)
47 h = 1;
48
49 auto tex_name = "BLOOM_MIP_" + std::to_string(i);
50 auto& tex = rview.tex_get_or_emplace(tex_name);
51
52 bool tex_changed = false;
53 if(gfx::needs_recreate(tex, {w, h}))
54 {
55 tex.reset();
56 tex = std::make_shared<gfx::texture>(w, h, false, 1, gfx::texture_format::RGBA16F, flags);
57 tex_changed = true;
58 }
59
60 auto fbo_name = "BLOOM_MIP_FBO_" + std::to_string(i);
61 auto& fbo = rview.fbo_get_or_emplace(fbo_name);
62 if(!fbo || tex_changed)
63 {
64 fbo.reset();
65 fbo = std::make_shared<gfx::frame_buffer>();
67 att.texture = tex;
68 att.generate_mips = false;
69 fbo->populate({att});
70 }
71 }
72}
73
74auto bloom_pass::get_mip_fbo(gfx::render_view& rview, int mip_index) -> const gfx::frame_buffer::ptr&
75{
76 return rview.fbo_get("BLOOM_MIP_FBO_" + std::to_string(mip_index));
77}
78
79auto bloom_pass::create_or_update_output_fb(gfx::render_view& rview,
82{
83 if(output)
84 {
85 return output;
86 }
87 auto input_sz = input->get_size();
88 auto& output_tex = rview.tex_get_or_emplace("BLOOM_OUTPUT");
89 if(!output_tex || output_tex->get_size() != input_sz)
90 {
91 output_tex = std::make_shared<gfx::texture>(input_sz.width,
92 input_sz.height,
93 false,
94 1,
95 gfx::texture_format::RGBA16F,
96 BGFX_TEXTURE_RT);
97 }
98 auto& output_fbo = rview.fbo_get_or_emplace("BLOOM_OUTPUT");
99 if(!output_fbo || output_fbo->get_size() != input_sz)
100 {
101 output_fbo = std::make_shared<gfx::frame_buffer>();
103 att.texture = output_tex;
104 att.generate_mips = false;
105 output_fbo->populate({att});
106 }
107 return output_fbo;
108}
109
111{
112 APP_SCOPE_PERF("Rendering/Bloom Pass");
113
114 const auto& input = params.input;
115 const auto& config = params.config;
116
117 int mip_count = math::clamp(config.mip_count, 2, max_mip_count);
118 const auto viewport_size = input->get_size();
119
120 create_or_resize_mip_chain(rview, viewport_size, mip_count);
121
122 // First downsample: full-res input -> half-res MIP_0.
123 // Uses Karis-weighted 13-tap to suppress sub-pixel specular flicker,
124 // combined with threshold/soft-knee prefilter.
125 {
126 const auto& mip0_fbo = get_mip_fbo(rview, 0);
127 auto mip0_size = mip0_fbo->get_size();
128
129 gfx::render_pass pass("Bloom/Karis Downsample");
130 pass.bind(mip0_fbo.get());
131 pass.set_view_proj({}, {});
132 pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
133
134 downsample_program_.program->begin();
135
136 float pixel_size[4] = {1.0f / float(viewport_size.width),
137 1.0f / float(viewport_size.height),
138 0.0f,
139 0.0f};
140 gfx::set_uniform(downsample_program_.u_pixel_size, pixel_size);
141
142 float params_data[4] = {config.threshold, 0.0f, config.soft_knee, config.clamp};
143 gfx::set_uniform(downsample_program_.u_params, params_data);
144
145 gfx::set_texture(downsample_program_.s_tex, 0, input->get_texture());
146 gfx::set_texture(downsample_program_.s_exposure, 1,
147 params.exposure_texture ? params.exposure_texture : default_textures::get().white_texture());
148
149 irect32_t rect(0, 0, mip0_size.width, mip0_size.height);
151 auto topology = gfx::clip_quad(1.0f);
152 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
153 gfx::submit(pass.id, downsample_program_.program->native_handle());
154 gfx::set_state(BGFX_STATE_DEFAULT);
155 downsample_program_.program->end();
156 }
157
158 // Downsample pyramid: standard 13-tap tent filter, no threshold.
159 for(int i = 0; i < mip_count - 1; ++i)
160 {
161 uint32_t src_w = viewport_size.width >> (i + 1);
162 uint32_t src_h = viewport_size.height >> (i + 1);
163 uint32_t out_w = viewport_size.width >> (i + 2);
164 uint32_t out_h = viewport_size.height >> (i + 2);
165 if(src_w < 1)
166 src_w = 1;
167 if(src_h < 1)
168 src_h = 1;
169 if(out_w < 1)
170 out_w = 1;
171 if(out_h < 1)
172 out_h = 1;
173
174 const auto& fbo = get_mip_fbo(rview, i + 1);
175
176 gfx::render_pass pass("Bloom/Downsample Pass");
177 pass.bind(fbo.get());
178 pass.set_view_proj({}, {});
179 pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
180
181 downsample_program_.program->begin();
182
183 float pixel_size[4] = {1.0f / float(src_w), 1.0f / float(src_h), 0.0f, 0.0f};
184 gfx::set_uniform(downsample_program_.u_pixel_size, pixel_size);
185
186 float params_data[4] = {0.0f, 1.0f, 0.0f, 0.0f};
187 gfx::set_uniform(downsample_program_.u_params, params_data);
188
189 gfx::set_texture(downsample_program_.s_tex, 0, rview.tex_get("BLOOM_MIP_" + std::to_string(i)));
190
191 irect32_t rect(0, 0, out_w, out_h);
193 auto topology = gfx::clip_quad(1.0f);
194 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
195 gfx::submit(pass.id, downsample_program_.program->native_handle());
196 gfx::set_state(BGFX_STATE_DEFAULT);
197 downsample_program_.program->end();
198 }
199
200 // Clear MIP_0 before upsample accumulation so the result is purely
201 // the multi-scale bloom contribution from smaller mips.
202 {
203 const auto& mip0_fbo = get_mip_fbo(rview, 0);
204 gfx::render_pass clear_pass("Bloom/Clear MIP0 Pass");
205 clear_pass.bind(mip0_fbo.get());
206 clear_pass.set_view_proj(nullptr, nullptr);
207 clear_pass.clear(BGFX_CLEAR_COLOR, 0, 0.0f, 0);
208 }
209
210 // Upsample and accumulate from smallest mip back to MIP_0.
211 for(int i = 0; i < mip_count - 1; ++i)
212 {
213 int src_idx = mip_count - 1 - i;
214 int out_idx = mip_count - 2 - i;
215 uint32_t out_w = viewport_size.width >> (out_idx + 1);
216 uint32_t out_h = viewport_size.height >> (out_idx + 1);
217 if(out_w < 1)
218 out_w = 1;
219 if(out_h < 1)
220 out_h = 1;
221
222 const auto& fbo = get_mip_fbo(rview, out_idx);
223
224 gfx::render_pass pass("Bloom/Upsample Pass");
225 pass.bind(fbo.get());
226 pass.set_view_proj({}, {});
227
228 upsample_program_.program->begin();
229
230 float pixel_size[4] = {1.0f / float(out_w), 1.0f / float(out_h), 0.0f, 0.0f};
231 gfx::set_uniform(upsample_program_.u_pixel_size, pixel_size);
232
233 const auto& mip_tint = config.get_mip_tint(src_idx);
234 float effective_weight = config.intensity * mip_tint.value.w;
235 float tint[4] = {mip_tint.value.x, mip_tint.value.y, mip_tint.value.z, effective_weight};
236 gfx::set_uniform(upsample_program_.u_tint, tint);
237
238 gfx::set_texture(upsample_program_.s_tex, 0, rview.tex_get("BLOOM_MIP_" + std::to_string(src_idx)));
239
240 irect32_t rect(0, 0, out_w, out_h);
242 auto topology = gfx::clip_quad(1.0f);
243 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_BLEND_ADD);
244 gfx::submit(pass.id, upsample_program_.program->native_handle());
245 gfx::set_state(BGFX_STATE_DEFAULT);
246 upsample_program_.program->end();
247 }
248
249 // Combine: add half-res bloom (MIP_0) to full-res scene.
250 // The texture sampler bilinearly upscales the half-res bloom.
251 auto output = create_or_update_output_fb(rview, input, params.output);
252
253 gfx::render_pass pass("Bloom/Combine Pass");
254 pass.bind(output.get());
255 pass.set_view_proj({}, {});
256
257 combine_program_.program->begin();
258
259 gfx::set_texture(combine_program_.s_scene, 0, input->get_texture());
260 gfx::set_texture(combine_program_.s_bloom, 1, rview.tex_get("BLOOM_MIP_0"));
261
262 const auto output_size = output->get_size();
263 irect32_t rect(0, 0, output_size.width, output_size.height);
265 auto topology = gfx::clip_quad(1.0f);
266 gfx::set_state(topology | BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A);
267 gfx::submit(pass.id, combine_program_.program->native_handle());
268 gfx::set_state(BGFX_STATE_DEFAULT);
269 combine_program_.program->end();
270
271 gfx::discard();
272
273 return output;
274}
275
277{
278 for(int i = 0; i < max_mip_count; ++i)
279 {
280 rview.fbo_remove("BLOOM_MIP_FBO_" + std::to_string(i));
281 rview.tex_remove("BLOOM_MIP_" + std::to_string(i));
282 }
283 rview.fbo_remove("BLOOM_OUTPUT");
284 rview.tex_remove("BLOOM_OUTPUT");
285}
286
287} // namespace unravel
void tex_remove(const hpp::string_view &id)
void fbo_remove(const hpp::string_view &id)
Manages assets, including loading, unloading, and storage.
auto get_asset(const std::string &key, load_flags flags=load_flags::standard, load_mode mode=load_mode::immediate) -> asset_handle< T >
Gets an asset by its key.
auto run(gfx::render_view &rview, const run_params &params) -> gfx::frame_buffer::ptr
auto init(rtti::context &ctx) -> bool
void release_resources(gfx::render_view &rview)
static auto get() -> default_textures &
void submit(view_id _id, program_handle _handle, int32_t _depth, bool _preserveState)
uint16_t set_scissor(uint16_t _x, uint16_t _y, uint16_t _width, uint16_t _height)
Definition graphics.cpp:952
void set_state(uint64_t _state, uint32_t _rgba)
Definition graphics.cpp:937
auto needs_recreate(const gfx::frame_buffer::ptr &fbo, const usize32_t &size) -> bool
void discard(uint8_t _flags)
void set_uniform(uniform_handle _handle, const void *_value, uint16_t _num)
Definition graphics.cpp:977
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)
#define APP_SCOPE_PERF(name_literal)
Create a scoped performance timer that records to the timeline profiler. Only accepts string literals...
Definition profiler.h:675
std::shared_ptr< gfx::texture > texture
Texture handle.
void set_view_proj(const float *v, const float *p)
gfx::view_id id
void clear(uint16_t _flags, uint32_t _rgba=0x000000ff, float _depth=1.0f, uint8_t _stencil=0) const
void bind(const frame_buffer *fb=nullptr) const
T width() const
T height() const