Unravel Engine C++ Reference
Loading...
Searching...
No Matches
assao_pass.cpp
Go to the documentation of this file.
1#include "assao_pass.h"
2#include "graphics/graphics.h"
5#include <graphics/texture.h>
6#include <string>
7
8namespace unravel
9{
10
11#define SAMPLER_POINT_CLAMP (BGFX_SAMPLER_POINT | BGFX_SAMPLER_UVW_CLAMP)
12#define SAMPLER_POINT_MIRROR (BGFX_SAMPLER_POINT | BGFX_SAMPLER_UVW_MIRROR)
13#define SAMPLER_LINEAR_CLAMP (BGFX_SAMPLER_UVW_CLAMP)
14
15#define SSAO_DEPTH_MIP_LEVELS 4
16
17void vec2Set(float* _v, float _x, float _y)
18{
19 _v[0] = _x;
20 _v[1] = _y;
21}
22
23void vec4Set(float* _v, float _x, float _y, float _z, float _w)
24{
25 _v[0] = _x;
26 _v[1] = _y;
27 _v[2] = _z;
28 _v[3] = _w;
29}
30
31void vec4iSet(int32_t* _v, int32_t _x, int32_t _y, int32_t _z, int32_t _w)
32{
33 _v[0] = _x;
34 _v[1] = _y;
35 _v[2] = _z;
36 _v[3] = _w;
37}
38
39static const int32_t cMaxBlurPassCount = 6;
40
42{
43 auto& am = ctx.get_cached<asset_manager>();
44
45 auto loadProgram = [&](const std::string& cs)
46 {
47 auto cs_shader = am.get_asset<gfx::shader>("engine:/data/shaders/assao/" + cs + ".sc");
48
49 auto prog = std::make_shared<gpu_program>(cs_shader);
50 m_programs.emplace_back(prog);
51 return prog->native_handle();
52 };
53
54 // Create uniforms
55 u_rect = bgfx::createUniform("u_rect", bgfx::UniformType::Vec4); // viewport/scissor rect for compute
56 m_uniforms.init();
57
58 // Create texture sampler uniforms (used when we bind textures)
59 s_normal = bgfx::createUniform("s_normal", bgfx::UniformType::Sampler); // Normal gbuffer
60 s_depth = bgfx::createUniform("s_depth", bgfx::UniformType::Sampler); // Depth gbuffer
61
62 // clang-format off
63 s_ao = bgfx::createUniform("s_ao", bgfx::UniformType::Sampler);
64 s_blurInput = bgfx::createUniform("s_blurInput", bgfx::UniformType::Sampler);
65 s_finalSSAO = bgfx::createUniform("s_finalSSAO", bgfx::UniformType::Sampler);
66 s_depthSource = bgfx::createUniform("s_depthSource", bgfx::UniformType::Sampler);
67 s_viewspaceDepthSource = bgfx::createUniform("s_viewspaceDepthSource", bgfx::UniformType::Sampler);
68 s_viewspaceDepthSourceMirror = bgfx::createUniform("s_viewspaceDepthSourceMirror", bgfx::UniformType::Sampler);
69 s_importanceMap = bgfx::createUniform("s_importanceMap", bgfx::UniformType::Sampler);
70 // clang-format on
71
72 // Create program from shaders.
73
74 // clang-format off
75 m_prepareDepthsProgram = loadProgram("cs_assao_prepare_depths");
76 m_prepareDepthsAndNormalsProgram = loadProgram("cs_assao_prepare_depths_and_normals");
77 m_prepareDepthsHalfProgram = loadProgram("cs_assao_prepare_depths_half");
78 m_prepareDepthsAndNormalsHalfProgram = loadProgram("cs_assao_prepare_depths_and_normals_half");
79 m_prepareDepthMipProgram = loadProgram("cs_assao_prepare_depth_mip");
80 m_generateQ0Program = loadProgram("cs_assao_generate_q0");
81 m_generateQ1Program = loadProgram("cs_assao_generate_q1");
82 m_generateQ2Program = loadProgram("cs_assao_generate_q2");
83 m_generateQ3Program = loadProgram("cs_assao_generate_q3");
84 m_generateQ3BaseProgram = loadProgram("cs_assao_generate_q3base");
85 m_generateQ0ProgramRgba16f = loadProgram("cs_assao_generate_q0_normal_rgba16f");
86 m_generateQ1ProgramRgba16f = loadProgram("cs_assao_generate_q1_normal_rgba16f");
87 m_generateQ2ProgramRgba16f = loadProgram("cs_assao_generate_q2_normal_rgba16f");
88 m_generateQ3ProgramRgba16f = loadProgram("cs_assao_generate_q3_normal_rgba16f");
89 m_generateQ3BaseProgramRgba16f = loadProgram("cs_assao_generate_q3base_normal_rgba16f");
90 m_smartBlurProgram = loadProgram("cs_assao_smart_blur");
91 m_smartBlurWideProgram = loadProgram("cs_assao_smart_blur_wide");
92 m_nonSmartBlurProgram = loadProgram("cs_assao_non_smart_blur");
93 m_applyProgram = loadProgram("cs_assao_apply");
94 m_nonSmartApplyProgram = loadProgram("cs_assao_non_smart_apply");
95 m_nonSmartHalfApplyProgram = loadProgram("cs_assao_non_smart_half_apply");
96 m_generateImportanceMapProgram = loadProgram("cs_assao_generate_importance_map");
97 m_postprocessImportanceMapAProgram = loadProgram("cs_assao_postprocess_importance_map_a");
98 m_postprocessImportanceMapBProgram = loadProgram("cs_assao_postprocess_importance_map_b");
99 m_loadCounterClearProgram = loadProgram("cs_assao_load_counter_clear");
100 m_updateGBufferProgram = loadProgram("cs_assao_update_g_buffer");
101 // clang-format on
102
103 m_loadCounter = bgfx::createDynamicIndexBuffer(1, BGFX_BUFFER_COMPUTE_READ_WRITE | BGFX_BUFFER_INDEX32);
104
105 return true;
106}
107
108void assao_pass::run(const camera& cam, gfx::render_view& rview, const run_params& params)
109{
110 m_settings = params.params;
111
112 const auto size = params.depth->get_size();
113 const int32_t width = static_cast<int32_t>(size.width);
114 const int32_t height = static_cast<int32_t>(size.height);
115
116 dimensions dims;
117 const int32_t border = 0;
118 dims.size[0] = width + 2 * border;
119 dims.size[1] = height + 2 * border;
120 dims.halfSize[0] = (dims.size[0] + 1) / 2;
121 dims.halfSize[1] = (dims.size[1] + 1) / 2;
122 dims.quarterSize[0] = (dims.halfSize[0] + 1) / 2;
123 dims.quarterSize[1] = (dims.halfSize[1] + 1) / 2;
124 vec4iSet(dims.fullResOutScissorRect, border, border, width + border, height + border);
125 vec4iSet(dims.halfResOutScissorRect,
126 dims.fullResOutScissorRect[0] / 2,
127 dims.fullResOutScissorRect[1] / 2,
128 (dims.fullResOutScissorRect[2] + 1) / 2,
129 (dims.fullResOutScissorRect[3] + 1) / 2);
130 const int32_t blurEnlarge =
131 cMaxBlurPassCount + bx::max(0, cMaxBlurPassCount - 2);
132 vec4iSet(dims.halfResOutScissorRect,
133 bx::max(0, dims.halfResOutScissorRect[0] - blurEnlarge),
134 bx::max(0, dims.halfResOutScissorRect[1] - blurEnlarge),
135 bx::min(dims.halfSize[0], dims.halfResOutScissorRect[2] + blurEnlarge),
136 bx::min(dims.halfSize[1], dims.halfResOutScissorRect[3] + blurEnlarge));
137
138 create_or_update_frame_buffers(rview, dims);
139
140 const float* viewMtx = cam.get_view();
141 float projMtx[16];
142
143 float n = cam.get_near_clip();
144 float f = cam.get_far_clip();
145
147 {
148 auto fovy = cam.get_fov();
149 bx::mtxProj(projMtx, fovy, float(dims.size[0]) / float(dims.size[1]), n, f, false);
150 }
151 else
152 {
153 float zoom = cam.get_zoom_factor();
154 const frect_t rect = {-(float(dims.size[0]) / 2.0f) * zoom,
155 (float(dims.size[1]) / 2.0f) * zoom,
156 (float(dims.size[0]) / 2.0f) * zoom,
157 -(float(dims.size[1]) / 2.0f) * zoom};
158
159 bx::mtxOrtho(projMtx, rect.left, rect.right, rect.bottom, rect.top, n, f, 0, false);
160 }
161
162 // ASSAO passes
163#if USE_ASSAO == 0
164 const auto& halfDepths0 = rview.tex_get("ASSAO_HALF_DEPTH_0");
165 const auto& halfDepths1 = rview.tex_get("ASSAO_HALF_DEPTH_1");
166 const auto& halfDepths2 = rview.tex_get("ASSAO_HALF_DEPTH_2");
167 const auto& halfDepths3 = rview.tex_get("ASSAO_HALF_DEPTH_3");
168 const gfx::texture::ptr halfDepthsArr[4] = {halfDepths0, halfDepths1, halfDepths2, halfDepths3};
169 const auto& pingPongA = rview.tex_get("ASSAO_PING_PONG_A");
170 const auto& pingPongB = rview.tex_get("ASSAO_PING_PONG_B");
171 const auto& finalResults = rview.tex_get("ASSAO_FINAL_RESULTS");
172 const auto& normals = rview.tex_get("ASSAO_NORMALS");
173 const auto& importanceMap = rview.tex_get("ASSAO_IMPORTANCE_MAP");
174 const auto& importanceMapPong = rview.tex_get("ASSAO_IMPORTANCE_MAP_PONG");
175 const auto& aoMap = rview.tex_get("ASSAO_AO_MAP");
176
177 update_uniforms(0, viewMtx, projMtx, dims);
178
179 gfx::render_pass pass("ASSAO/Pass");
180 auto view = pass.id;
181
182 {
183 bgfx::setTexture(0, s_depthSource, params.depth->native_handle(), SAMPLER_POINT_CLAMP);
184 m_uniforms.submit();
185
186 if(m_settings.generate_normals)
187 {
188 bgfx::setImage(5, normals->native_handle(), 0, bgfx::Access::Write);
189 }
190
191 if(m_settings.quality_level < 0)
192 {
193 for(int32_t j = 0; j < 2; ++j)
194 {
195 bgfx::setImage((uint8_t)(j + 1),
196 (j == 0 ? halfDepths0 : halfDepths3)->native_handle(),
197 0,
198 bgfx::Access::Write);
199 }
200
201 bgfx::dispatch(view,
202 m_settings.generate_normals ? m_prepareDepthsAndNormalsHalfProgram
203 : m_prepareDepthsHalfProgram,
204 (dims.halfSize[0] + 7) / 8,
205 (dims.halfSize[1] + 7) / 8);
206 }
207 else
208 {
209 bgfx::setImage(1, halfDepths0->native_handle(), 0, bgfx::Access::Write);
210 bgfx::setImage(2, halfDepths1->native_handle(), 0, bgfx::Access::Write);
211 bgfx::setImage(3, halfDepths2->native_handle(), 0, bgfx::Access::Write);
212 bgfx::setImage(4, halfDepths3->native_handle(), 0, bgfx::Access::Write);
213
214 bgfx::dispatch(view,
215 m_settings.generate_normals ? m_prepareDepthsAndNormalsProgram : m_prepareDepthsProgram,
216 (dims.halfSize[0] + 7) / 8,
217 (dims.halfSize[1] + 7) / 8);
218 }
219 }
220
221 // only do mipmaps for higher quality levels (not beneficial on quality level 1, and detrimental on quality level 0)
222 if(m_settings.quality_level > 1)
223 {
224 uint16_t mipWidth = (uint16_t)dims.halfSize[0];
225 uint16_t mipHeight = (uint16_t)dims.halfSize[1];
226
227 for(uint8_t i = 1; i < SSAO_DEPTH_MIP_LEVELS; i++)
228 {
229 mipWidth = (uint16_t)bx::max(1, mipWidth >> 1);
230 mipHeight = (uint16_t)bx::max(1, mipHeight >> 1);
231
232 bgfx::setImage(0, halfDepths0->native_handle(), i - 1, bgfx::Access::Read);
233 bgfx::setImage(4, halfDepths0->native_handle(), i, bgfx::Access::Write);
234 bgfx::setImage(1, halfDepths1->native_handle(), i - 1, bgfx::Access::Read);
235 bgfx::setImage(5, halfDepths1->native_handle(), i, bgfx::Access::Write);
236 bgfx::setImage(2, halfDepths2->native_handle(), i - 1, bgfx::Access::Read);
237 bgfx::setImage(6, halfDepths2->native_handle(), i, bgfx::Access::Write);
238 bgfx::setImage(3, halfDepths3->native_handle(), i - 1, bgfx::Access::Read);
239 bgfx::setImage(7, halfDepths3->native_handle(), i, bgfx::Access::Write);
240
241 m_uniforms.submit();
242 float rect[4] = {0.0f, 0.0f, (float)mipWidth, (float)mipHeight};
243 bgfx::setUniform(u_rect, rect);
244
245 bgfx::dispatch(view, m_prepareDepthMipProgram, (mipWidth + 7) / 8, (mipHeight + 7) / 8);
246 }
247 }
248
249 // for adaptive quality, importance map pass
250 for(int32_t ssaoPass = 0; ssaoPass < 2; ++ssaoPass)
251 {
252 if(ssaoPass == 0 && m_settings.quality_level < 3)
253 {
254 continue;
255 }
256
257 if(ssaoPass == 1 && m_settings.quality_level == 3)
258 {
259 gfx::render_pass pass(fmt::format("ASSAO/Importance Map Pass {}", ssaoPass).c_str());
260 view = pass.id;
261 }
262
263 bool adaptiveBasePass = (ssaoPass == 0);
264
265 BX_UNUSED(adaptiveBasePass);
266
267 int32_t passCount = 4;
268
269 int32_t halfResNumX = (dims.halfResOutScissorRect[2] - dims.halfResOutScissorRect[0] + 7) / 8;
270 int32_t halfResNumY = (dims.halfResOutScissorRect[3] - dims.halfResOutScissorRect[1] + 7) / 8;
271 float halfResRect[4] = {(float)dims.halfResOutScissorRect[0],
272 (float)dims.halfResOutScissorRect[1],
273 (float)dims.halfResOutScissorRect[2],
274 (float)dims.halfResOutScissorRect[3]};
275
276 for(int32_t pass = 0; pass < passCount; pass++)
277 {
278 if(m_settings.quality_level < 0 && (pass == 1 || pass == 2))
279 {
280 continue;
281 }
282
283 int32_t blurPasses = m_settings.blur_pass_count;
284 blurPasses = bx::min(blurPasses, cMaxBlurPassCount);
285
286 if(m_settings.quality_level == 3)
287 {
288 // if adaptive, at least one blur pass needed as the first pass needs to read the final texture results
289 // - kind of awkward
290 if(adaptiveBasePass)
291 {
292 blurPasses = 0;
293 }
294 else
295 {
296 blurPasses = bx::max(1, blurPasses);
297 }
298 }
299 else if(m_settings.quality_level <= 0)
300 {
301 // just one blur pass allowed for minimum quality
302 blurPasses = bx::min(1, m_settings.blur_pass_count);
303 }
304
305 update_uniforms(pass, viewMtx, projMtx, dims);
306
307 bgfx::TextureHandle pPingRT = pingPongA->native_handle();
308 bgfx::TextureHandle pPongRT = pingPongB->native_handle();
309
310 // Generate
311 {
312 bgfx::setImage(6,
313 blurPasses == 0 ? finalResults->native_handle() : pPingRT,
314 0,
315 bgfx::Access::Write);
316
317 bgfx::setUniform(u_rect, halfResRect);
318
319 bgfx::setTexture(0,
320 s_viewspaceDepthSource,
321 halfDepthsArr[pass]->native_handle(),
323 bgfx::setTexture(1,
324 s_viewspaceDepthSourceMirror,
325 halfDepthsArr[pass]->native_handle(),
327 if(m_settings.generate_normals)
328 {
329 bgfx::setImage(2, normals->native_handle(), 0, bgfx::Access::Read);
330 }
331 else
332 {
333 bgfx::setImage(2, params.normal->native_handle(), 0, bgfx::Access::Read);
334 }
335
336 if(!adaptiveBasePass && (m_settings.quality_level == 3))
337 {
338 bgfx::setBuffer(3, m_loadCounter, bgfx::Access::Read);
339 bgfx::setTexture(4, s_importanceMap, importanceMap->native_handle(), SAMPLER_LINEAR_CLAMP);
340 bgfx::setImage(5, finalResults->native_handle(), 0, bgfx::Access::Read);
341 }
342
343 bgfx::ProgramHandle programsNormal[5] = {m_generateQ0Program,
344 m_generateQ1Program,
345 m_generateQ2Program,
346 m_generateQ3Program,
347 m_generateQ3BaseProgram};
348
349 bgfx::ProgramHandle programsRgba16f[5] = {m_generateQ0ProgramRgba16f,
350 m_generateQ1ProgramRgba16f,
351 m_generateQ2ProgramRgba16f,
352 m_generateQ3ProgramRgba16f,
353 m_generateQ3BaseProgramRgba16f};
354
355 bgfx::ProgramHandle* programs = programsNormal;
356
357 if(!m_settings.generate_normals)
358 {
359 programs = programsRgba16f;
360 }
361
362 int32_t programIndex = bx::max(0, (!adaptiveBasePass) ? (m_settings.quality_level) : (4));
363
364 m_uniforms.m_layer = blurPasses == 0 ? (float)pass : 0.0f;
365 m_uniforms.submit();
366 bgfx::dispatch(view, programs[programIndex], halfResNumX, halfResNumY);
367 }
368
369 // Blur
370 if(blurPasses > 0)
371 {
372 int32_t wideBlursRemaining = bx::max(0, blurPasses - 2);
373
374 for(int32_t i = 0; i < blurPasses; i++)
375 {
376 bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE);
377 bgfx::touch(view);
378
379 m_uniforms.m_layer = ((i == (blurPasses - 1)) ? (float)pass : 0.0f);
380 m_uniforms.submit();
381
382 bgfx::setUniform(u_rect, halfResRect);
383
384 bgfx::setImage(0,
385 i == (blurPasses - 1) ? finalResults->native_handle() : pPongRT,
386 0,
387 bgfx::Access::Write);
388 bgfx::setTexture(1,
389 s_blurInput,
390 pPingRT,
392
393 if(m_settings.quality_level > 0)
394 {
395 if(wideBlursRemaining > 0)
396 {
397 bgfx::dispatch(view, m_smartBlurWideProgram, halfResNumX, halfResNumY);
398 wideBlursRemaining--;
399 }
400 else
401 {
402 bgfx::dispatch(view, m_smartBlurProgram, halfResNumX, halfResNumY);
403 }
404 }
405 else
406 {
407 bgfx::dispatch(view,
408 m_nonSmartBlurProgram,
409 halfResNumX,
410 halfResNumY); // just for quality level 0 (and -1)
411 }
412
413 bgfx::TextureHandle temp = pPingRT;
414 pPingRT = pPongRT;
415 pPongRT = temp;
416 }
417 }
418 }
419
420 if(ssaoPass == 0 && m_settings.quality_level == 3)
421 { // Generate importance map
422 m_uniforms.submit();
423 bgfx::setImage(0, importanceMap->native_handle(), 0, bgfx::Access::Write);
424 bgfx::setTexture(1, s_finalSSAO, finalResults->native_handle(), SAMPLER_POINT_CLAMP);
425 bgfx::dispatch(view,
426 m_generateImportanceMapProgram,
427 (dims.quarterSize[0] + 7) / 8,
428 (dims.quarterSize[1] + 7) / 8);
429
430 m_uniforms.submit();
431 bgfx::setImage(0, importanceMapPong->native_handle(), 0, bgfx::Access::Write);
432 bgfx::setTexture(1, s_importanceMap, importanceMap->native_handle());
433 bgfx::dispatch(view,
434 m_postprocessImportanceMapAProgram,
435 (dims.quarterSize[0] + 7) / 8,
436 (dims.quarterSize[1] + 7) / 8);
437
438 bgfx::setBuffer(0, m_loadCounter, bgfx::Access::ReadWrite);
439 bgfx::dispatch(view, m_loadCounterClearProgram, 1, 1);
440
441 m_uniforms.submit();
442 bgfx::setImage(0, importanceMap->native_handle(), 0, bgfx::Access::Write);
443 bgfx::setTexture(1, s_importanceMap, importanceMapPong->native_handle());
444 bgfx::setBuffer(2, m_loadCounter, bgfx::Access::ReadWrite);
445 bgfx::dispatch(view,
446 m_postprocessImportanceMapBProgram,
447 (dims.quarterSize[0] + 7) / 8,
448 (dims.quarterSize[1] + 7) / 8);
449 }
450 }
451
452 // Apply
453 {
454 bgfx::setImage(0, aoMap->native_handle(), 0, bgfx::Access::Write);
455 bgfx::setTexture(1, s_finalSSAO, finalResults->native_handle());
456
457 m_uniforms.submit();
458
459 float rect[4] = {(float)dims.fullResOutScissorRect[0],
460 (float)dims.fullResOutScissorRect[1],
461 (float)dims.fullResOutScissorRect[2],
462 (float)dims.fullResOutScissorRect[3]};
463 bgfx::setUniform(u_rect, rect);
464
465 bgfx::ProgramHandle program;
466 if(m_settings.quality_level < 0)
467 program = m_nonSmartHalfApplyProgram;
468 else if(m_settings.quality_level == 0)
469 program = m_nonSmartApplyProgram;
470 else
471 program = m_applyProgram;
472 bgfx::dispatch(view,
473 program,
474 (dims.fullResOutScissorRect[2] - dims.fullResOutScissorRect[0] + 7) / 8,
475 (dims.fullResOutScissorRect[3] - dims.fullResOutScissorRect[1] + 7) / 8);
476 }
477
478#endif
479
480 {
481 gfx::render_pass pass("ASSAO/Update G-Buffer AO Pass");
482 const auto& aoMapTex = rview.tex_get("ASSAO_AO_MAP");
483 bgfx::setImage(0, params.color_ao->native_handle(), 0, bgfx::Access::ReadWrite);
484 bgfx::setImage(1, aoMapTex->native_handle(), 0, bgfx::Access::Read);
485
486 bgfx::dispatch(pass.id, m_updateGBufferProgram, (dims.size[0] + 7) / 8, (dims.size[1] + 7) / 8);
487 }
488
489 gfx::discard();
490}
491
492auto assao_pass::shutdown() -> int32_t
493{
494 // Cleanup.
495
496 m_uniforms.destroy();
497
498 bgfx::destroy(u_rect);
499
500 bgfx::destroy(s_normal);
501 bgfx::destroy(s_depth);
502 bgfx::destroy(s_ao);
503 bgfx::destroy(s_blurInput);
504 bgfx::destroy(s_finalSSAO);
505 bgfx::destroy(s_depthSource);
506 bgfx::destroy(s_viewspaceDepthSource);
507 bgfx::destroy(s_viewspaceDepthSourceMirror);
508 bgfx::destroy(s_importanceMap);
509
510 bgfx::destroy(m_loadCounter);
511
512 m_programs.clear();
513
514 return 0;
515}
516
517void assao_pass::create_or_update_frame_buffers(gfx::render_view& rview, const dimensions& dims)
518{
519 const uint64_t halfDepthFlags = BGFX_TEXTURE_COMPUTE_WRITE | SAMPLER_POINT_CLAMP;
520 const usize32_t halfSz{static_cast<uint32_t>(dims.halfSize[0]),
521 static_cast<uint32_t>(dims.halfSize[1])};
522 const usize32_t fullSz{static_cast<uint32_t>(dims.size[0]),
523 static_cast<uint32_t>(dims.size[1])};
524 const usize32_t quarterSz{static_cast<uint32_t>(dims.quarterSize[0]),
525 static_cast<uint32_t>(dims.quarterSize[1])};
526
527 for(int32_t i = 0; i < 4; i++)
528 {
529 auto key = "ASSAO_HALF_DEPTH_" + std::to_string(i);
530 auto& tex = rview.tex_get_or_emplace(key);
531 if(gfx::needs_recreate(tex, halfSz))
532 {
533 tex.reset();
534 tex = std::make_shared<gfx::texture>(uint16_t(dims.halfSize[0]),
535 uint16_t(dims.halfSize[1]),
536 true,
537 1,
538 gfx::texture_format::R16F,
539 halfDepthFlags);
540 }
541 }
542
543 auto& pingA = rview.tex_get_or_emplace("ASSAO_PING_PONG_A");
544 if(gfx::needs_recreate(pingA, halfSz))
545 {
546 pingA.reset();
547 pingA = std::make_shared<gfx::texture>(uint16_t(dims.halfSize[0]),
548 uint16_t(dims.halfSize[1]),
549 false,
550 2,
551 gfx::texture_format::RG8,
552 BGFX_TEXTURE_COMPUTE_WRITE);
553 }
554 auto& pingB = rview.tex_get_or_emplace("ASSAO_PING_PONG_B");
555 if(gfx::needs_recreate(pingB, halfSz))
556 {
557 pingB.reset();
558 pingB = std::make_shared<gfx::texture>(uint16_t(dims.halfSize[0]),
559 uint16_t(dims.halfSize[1]),
560 false,
561 2,
562 gfx::texture_format::RG8,
563 BGFX_TEXTURE_COMPUTE_WRITE);
564 }
565
566 auto& finalRes = rview.tex_get_or_emplace("ASSAO_FINAL_RESULTS");
567 if(gfx::needs_recreate(finalRes, halfSz))
568 {
569 finalRes.reset();
570 finalRes = std::make_shared<gfx::texture>(uint16_t(dims.halfSize[0]),
571 uint16_t(dims.halfSize[1]),
572 false,
573 4,
574 gfx::texture_format::RG8,
575 BGFX_TEXTURE_COMPUTE_WRITE | SAMPLER_LINEAR_CLAMP);
576 }
577
578 auto& normals = rview.tex_get_or_emplace("ASSAO_NORMALS");
579 if(gfx::needs_recreate(normals, fullSz))
580 {
581 normals.reset();
582 normals = std::make_shared<gfx::texture>(uint16_t(dims.size[0]),
583 uint16_t(dims.size[1]),
584 false,
585 1,
586 gfx::texture_format::RGBA8,
587 BGFX_TEXTURE_COMPUTE_WRITE);
588 }
589
590 auto& importanceMap = rview.tex_get_or_emplace("ASSAO_IMPORTANCE_MAP");
591 if(gfx::needs_recreate(importanceMap, quarterSz))
592 {
593 importanceMap.reset();
594 importanceMap = std::make_shared<gfx::texture>(uint16_t(dims.quarterSize[0]),
595 uint16_t(dims.quarterSize[1]),
596 false,
597 1,
598 gfx::texture_format::R8,
599 BGFX_TEXTURE_COMPUTE_WRITE | SAMPLER_LINEAR_CLAMP);
600 }
601 auto& importanceMapPong = rview.tex_get_or_emplace("ASSAO_IMPORTANCE_MAP_PONG");
602 if(gfx::needs_recreate(importanceMapPong, quarterSz))
603 {
604 importanceMapPong.reset();
605 importanceMapPong = std::make_shared<gfx::texture>(uint16_t(dims.quarterSize[0]),
606 uint16_t(dims.quarterSize[1]),
607 false,
608 1,
609 gfx::texture_format::R8,
610 BGFX_TEXTURE_COMPUTE_WRITE | SAMPLER_LINEAR_CLAMP);
611 }
612
613 auto& aoMap = rview.tex_get_or_emplace("ASSAO_AO_MAP");
614 if(gfx::needs_recreate(aoMap, fullSz))
615 {
616 aoMap.reset();
617 aoMap = std::make_shared<gfx::texture>(uint16_t(dims.size[0]),
618 uint16_t(dims.size[1]),
619 false,
620 1,
621 gfx::texture_format::R8,
622 BGFX_TEXTURE_COMPUTE_WRITE | SAMPLER_POINT_CLAMP);
623 }
624}
625
626void assao_pass::update_uniforms(int32_t _pass, const float* view, const float* proj, const dimensions& dims)
627{
628 vec2Set(m_uniforms.m_viewportPixelSize, 1.0f / (float)dims.size[0], 1.0f / (float)dims.size[1]);
629 vec2Set(m_uniforms.m_halfViewportPixelSize, 1.0f / (float)dims.halfSize[0], 1.0f / (float)dims.halfSize[1]);
630
631 vec2Set(m_uniforms.m_viewport2xPixelSize,
632 m_uniforms.m_viewportPixelSize[0] * 2.0f,
633 m_uniforms.m_viewportPixelSize[1] * 2.0f);
634 vec2Set(m_uniforms.m_viewport2xPixelSize_x_025,
635 m_uniforms.m_viewport2xPixelSize[0] * 0.25f,
636 m_uniforms.m_viewport2xPixelSize[1] * 0.25f);
637
638 float depthLinearizeMul =
639 -proj[3 * 4 + 2]; // float depthLinearizeMul = ( clipFar * clipNear ) / ( clipFar - clipNear );
640 float depthLinearizeAdd =
641 proj[2 * 4 + 2]; // float depthLinearizeAdd = clipFar / ( clipFar - clipNear );
642 // correct the handedness issue. need to make sure this below is correct, but I think it is.
643
644 if(depthLinearizeMul * depthLinearizeAdd < 0)
645 {
646 depthLinearizeAdd = -depthLinearizeAdd;
647 }
648
649 vec2Set(m_uniforms.m_depthUnpackConsts, depthLinearizeMul, depthLinearizeAdd);
650
651 float tanHalfFOVY = 1.0f / proj[1 * 4 + 1]; // = tanf( drawContext.Camera.GetYFOV( ) * 0.5f );
652 float tanHalfFOVX = 1.0F / proj[0]; // = tanHalfFOVY * drawContext.Camera.GetAspect( );
653
654 if(bgfx::getRendererType() == bgfx::RendererType::OpenGL)
655 {
656 vec2Set(m_uniforms.m_ndcToViewMul, tanHalfFOVX * 2.0f, tanHalfFOVY * 2.0f);
657 vec2Set(m_uniforms.m_ndcToViewAdd, tanHalfFOVX * -1.0f, tanHalfFOVY * -1.0f);
658 }
659 else
660 {
661 vec2Set(m_uniforms.m_ndcToViewMul, tanHalfFOVX * 2.0f, tanHalfFOVY * -2.0f);
662 vec2Set(m_uniforms.m_ndcToViewAdd, tanHalfFOVX * -1.0f, tanHalfFOVY * 1.0f);
663 }
664
665 m_uniforms.m_effectRadius = bx::clamp(m_settings.radius, 0.0f, 100000.0f);
666 m_uniforms.m_effectShadowStrength = bx::clamp(m_settings.shadow_multiplier * 4.3f, 0.0f, 10.0f);
667 m_uniforms.m_effectShadowPow = bx::clamp(m_settings.shadow_power, 0.0f, 10.0f);
668 m_uniforms.m_effectShadowClamp = bx::clamp(m_settings.shadow_clamp, 0.0f, 1.0f);
669 m_uniforms.m_effectFadeOutMul = -1.0f / (m_settings.fade_out_to - m_settings.fade_out_from);
670 m_uniforms.m_effectFadeOutAdd =
671 m_settings.fade_out_from / (m_settings.fade_out_to - m_settings.fade_out_from) + 1.0f;
672 m_uniforms.m_effectHorizonAngleThreshold = bx::clamp(m_settings.horizon_angle_threshold, 0.0f, 1.0f);
673
674 // 1.2 seems to be around the best trade off - 1.0 means on-screen radius will stop/slow growing when the camera is
675 // at 1.0 distance, so, depending on FOV, basically filling up most of the screen This setting is
676 // viewspace-dependent and not screen size dependent intentionally, so that when you change FOV the effect stays
677 // (relatively) similar.
678 float effectSamplingRadiusNearLimit = (m_settings.radius * 1.2f);
679
680 // if the depth precision is switched to 32bit float, this can be set to something closer to 1 (0.9999 is fine)
681 m_uniforms.m_depthPrecisionOffsetMod = 0.9992f;
682
683 // used to get average load per pixel; 9.0 is there to compensate for only doing every 9th InterlockedAdd in
684 // PSPostprocessImportanceMapB for performance reasons
685 m_uniforms.m_loadCounterAvgDiv = 9.0f / (float)(dims.quarterSize[0] * dims.quarterSize[1] * 255.0);
686
687 // Special settings for lowest quality level - just nerf the effect a tiny bit
688 if(m_settings.quality_level <= 0)
689 {
690 effectSamplingRadiusNearLimit *= 1.50f;
691
692 if(m_settings.quality_level < 0)
693 {
694 m_uniforms.m_effectRadius *= 0.8f;
695 }
696 }
697
698 effectSamplingRadiusNearLimit /= tanHalfFOVY; // to keep the effect same regardless of FOV
699
700 m_uniforms.m_effectSamplingRadiusNearLimitRec = 1.0f / effectSamplingRadiusNearLimit;
701
702 m_uniforms.m_adaptiveSampleCountLimit = m_settings.adaptive_quality_limit;
703
704 m_uniforms.m_negRecEffectRadius = -1.0f / m_uniforms.m_effectRadius;
705
706 if(bgfx::getCaps()->originBottomLeft)
707 {
708 vec2Set(m_uniforms.m_perPassFullResCoordOffset, (float)(_pass % 2), 1.0f - (float)(_pass / 2));
709 vec2Set(m_uniforms.m_perPassFullResUVOffset,
710 ((_pass % 2) - 0.0f) / dims.size[0],
711 (1.0f - ((_pass / 2) - 0.0f)) / dims.size[1]);
712 }
713 else
714 {
715 vec2Set(m_uniforms.m_perPassFullResCoordOffset, (float)(_pass % 2), (float)(_pass / 2));
716 vec2Set(m_uniforms.m_perPassFullResUVOffset,
717 ((_pass % 2) - 0.0f) / dims.size[0],
718 ((_pass / 2) - 0.0f) / dims.size[1]);
719 }
720
721 m_uniforms.m_invSharpness = bx::clamp(1.0f - m_settings.sharpness, 0.0f, 1.0f);
722 m_uniforms.m_passIndex = (float)_pass;
723 vec2Set(m_uniforms.m_quarterResPixelSize, 1.0f / (float)dims.quarterSize[0], 1.0f / (float)dims.quarterSize[1]);
724
725 float additionalAngleOffset =
726 m_settings.temporal_supersampling_angle_offset; // if using temporal supersampling approach (like "Progressive
727 // Rendering Using Multi-frame Sampling" from GPU Pro 7, etc.)
728 float additionalRadiusScale =
729 m_settings.temporal_supersampling_radius_offset; // if using temporal supersampling approach (like "Progressive
730 // Rendering Using Multi-frame Sampling" from GPU Pro 7, etc.)
731 const int32_t subPassCount = 5;
732 for(int32_t subPass = 0; subPass < subPassCount; subPass++)
733 {
734 int32_t a = _pass;
735
736 int32_t spmap[5]{0, 1, 4, 3, 2};
737 int32_t b = spmap[subPass];
738
739 float ca, sa;
740 float angle0 = ((float)a + (float)b / (float)subPassCount) * (3.1415926535897932384626433832795f) * 0.5f;
741 angle0 += additionalAngleOffset;
742
743 ca = bx::cos(angle0);
744 sa = bx::sin(angle0);
745
746 float scale = 1.0f + (a - 1.5f + (b - (subPassCount - 1.0f) * 0.5f) / (float)subPassCount) * 0.07f;
747 scale *= additionalRadiusScale;
748
749 vec4Set(m_uniforms.m_patternRotScaleMatrices[subPass], scale * ca, scale * -sa, -scale * sa, -scale * ca);
750 }
751
752 m_uniforms.m_normalsUnpackMul = 2.0f;
753 m_uniforms.m_normalsUnpackAdd = -1.0f;
754
755 m_uniforms.m_detailAOStrength = m_settings.detail_shadow_strength;
756
757 if(m_settings.generate_normals)
758 {
759 bx::mtxIdentity(m_uniforms.m_normalsWorldToViewspaceMatrix);
760 }
761 else
762 {
763 bx::mtxTranspose(m_uniforms.m_normalsWorldToViewspaceMatrix, view);
764 }
765}
766
768{
769 for(int i = 0; i < 4; ++i)
770 {
771 rview.tex_remove("ASSAO_HALF_DEPTH_" + std::to_string(i));
772 }
773 rview.tex_remove("ASSAO_PING_PONG_A");
774 rview.tex_remove("ASSAO_PING_PONG_B");
775 rview.tex_remove("ASSAO_FINAL_RESULTS");
776 rview.tex_remove("ASSAO_NORMALS");
777 rview.tex_remove("ASSAO_IMPORTANCE_MAP");
778 rview.tex_remove("ASSAO_IMPORTANCE_MAP_PONG");
779 rview.tex_remove("ASSAO_AO_MAP");
780}
781
782} // namespace unravel
#define SSAO_DEPTH_MIP_LEVELS
#define SAMPLER_POINT_CLAMP
#define SAMPLER_POINT_MIRROR
#define SAMPLER_LINEAR_CLAMP
uint32_t width
uint32_t height
bgfx::ProgramHandle loadProgram(bx::FileReaderI *_reader, const bx::StringView &_vsName, const bx::StringView &_fsName)
entt::handle b
entt::handle a
auto native_handle() const -> T
Definition handle_impl.h:53
void tex_remove(const hpp::string_view &id)
auto tex_get(const hpp::string_view &id) const -> const texture::ptr &
auto tex_get_or_emplace(const hpp::string_view &id) -> texture::ptr &
auto init(rtti::context &ctx) -> bool
void run(const camera &camera, gfx::render_view &rview, const run_params &params)
void release_resources(gfx::render_view &rview)
auto shutdown() -> int32_t
Manages assets, including loading, unloading, and storage.
Class representing a camera. Contains functionality for manipulating and updating a camera....
Definition camera.h:62
auto get_far_clip() const -> float
Retrieves the distance from the camera to the far clip plane.
Definition camera.cpp:69
auto get_fov() const -> float
Retrieves the current field of view angle in degrees.
Definition camera.cpp:59
auto get_projection_mode() const -> projection_mode
Retrieves the current projection mode for this camera.
Definition camera.cpp:54
auto get_zoom_factor() const -> float
Retrieves the zoom factor.
Definition camera.cpp:11
auto get_view() const -> const math::transform &
Retrieves the current view matrix.
Definition camera.cpp:278
auto get_near_clip() const -> float
Retrieves the distance from the camera to the near clip plane.
Definition camera.cpp:64
uint16_t view
auto needs_recreate(const gfx::frame_buffer::ptr &fbo, const usize32_t &size) -> bool
void discard(uint8_t _flags)
void vec4Set(float *_v, float _x, float _y, float _z, float _w)
void vec4iSet(int32_t *_v, int32_t _x, int32_t _y, int32_t _z, int32_t _w)
static const int32_t cMaxBlurPassCount
void vec2Set(float *_v, float _x, float _y)
std::vector< float > scale
gfx::view_id id
auto get_size() const -> usize32_t
Definition texture.cpp:339
T width
Definition basetypes.hpp:55
T height
Definition basetypes.hpp:56