From a33ca71bcf45366b5cc834b1c927a0a020b56a7c Mon Sep 17 00:00:00 2001 From: elvencache <45753710+elvencache@users.noreply.github.com> Date: Sat, 2 Jan 2021 10:42:02 -0800 Subject: [PATCH 1/2] add denoise example (#2344) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit /* * Implement SVGF style denoising as bgfx example. Goal is to explore various * options and parameters, not produce an optimized, efficient denoiser. * * Starts with deferred rendering scene with very basic lighting. Lighting is * masked out with a noise pattern to provide something to denoise. There are * two options for the noise pattern. One is a fixed 2x2 dither pattern to * stand-in for lighting at quarter resolution. The other is the common * shadertoy random pattern as a stand-in for some fancier lighting without * enough samples per pixel, like ray tracing. * * First a temporal denoising filter is applied. The temporal filter is only * using normals to reject previous samples. The SVGF paper also describes using * depth comparison to reject samples but that is not implemented here. * * Followed by some number of spatial filters. These are implemented like in the * SVGF paper. As an alternative to the 5x5 Edge-Avoiding A-Trous filter, can * select a 3x3 filter instead. The 3x3 filter takes fewer samples and covers a * smaller area, but takes less time to compute. From a loosely eyeballed * comparison, N 5x5 passes looks similar to N+1 3x3 passes. The wider spatial * filters take a fair chunk of time to compute. I wonder if it would be a good * idea to interleave the input texture before computing, after the first pass * which skips zero pixels. * * I have not implemetened the variance guided part. * * There's also an optional TXAA pass to be applied after. I am not happy with * its implementation yet, so it defaults to off here. */ /* * References: * Spatiotemporal Variance-Guided Filtering: Real-Time Reconstruction for * Path-Traced Global Illumination. by Christoph Schied and more. * - SVGF denoising algorithm * * Streaming G-Buffer Compression for Multi-Sample Anti-Aliasing. * by E. Kerzner and M. Salvi. * - details about history comparison for temporal denoising filter * * Edge-Avoiding À-Trous Wavelet Transform for Fast Global Illumination * Filtering. by Holger Dammertz and more. * - details about a-trous algorithm for spatial denoising filter */ --- examples/xx-denoise/denoise.cpp | 1075 +++++++++++++++++ .../xx-denoise/fs_denoise_apply_lighting.sc | 25 + examples/xx-denoise/fs_denoise_copy.sc | 18 + .../xx-denoise/fs_denoise_deferred_combine.sc | 61 + examples/xx-denoise/fs_denoise_gbuffer.sc | 69 ++ examples/xx-denoise/fs_denoise_spatial_3x3.sc | 12 + examples/xx-denoise/fs_denoise_spatial_5x5.sc | 12 + .../fs_denoise_spatial_implementation.sh | 97 ++ examples/xx-denoise/fs_denoise_temporal.sc | 96 ++ examples/xx-denoise/fs_denoise_txaa.sc | 208 ++++ examples/xx-denoise/makefile | 10 + examples/xx-denoise/normal_encoding.sh | 92 ++ examples/xx-denoise/parameters.sh | 32 + examples/xx-denoise/shared_functions.sh | 25 + examples/xx-denoise/varying.def.sc | 9 + examples/xx-denoise/vs_denoise_gbuffer.sc | 54 + examples/xx-denoise/vs_denoise_screenquad.sc | 10 + 17 files changed, 1905 insertions(+) create mode 100644 examples/xx-denoise/denoise.cpp create mode 100644 examples/xx-denoise/fs_denoise_apply_lighting.sc create mode 100644 examples/xx-denoise/fs_denoise_copy.sc create mode 100644 examples/xx-denoise/fs_denoise_deferred_combine.sc create mode 100644 examples/xx-denoise/fs_denoise_gbuffer.sc create mode 100644 examples/xx-denoise/fs_denoise_spatial_3x3.sc create mode 100644 examples/xx-denoise/fs_denoise_spatial_5x5.sc create mode 100644 examples/xx-denoise/fs_denoise_spatial_implementation.sh create mode 100644 examples/xx-denoise/fs_denoise_temporal.sc create mode 100644 examples/xx-denoise/fs_denoise_txaa.sc create mode 100644 examples/xx-denoise/makefile create mode 100644 examples/xx-denoise/normal_encoding.sh create mode 100644 examples/xx-denoise/parameters.sh create mode 100644 examples/xx-denoise/shared_functions.sh create mode 100644 examples/xx-denoise/varying.def.sc create mode 100644 examples/xx-denoise/vs_denoise_gbuffer.sc create mode 100644 examples/xx-denoise/vs_denoise_screenquad.sc diff --git a/examples/xx-denoise/denoise.cpp b/examples/xx-denoise/denoise.cpp new file mode 100644 index 000000000..e7abb1ee7 --- /dev/null +++ b/examples/xx-denoise/denoise.cpp @@ -0,0 +1,1075 @@ +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +/* +* Implement SVGF style denoising as bgfx example. Goal is to explore various +* options and parameters, not produce an optimized, efficient denoiser. +* +* Starts with deferred rendering scene with very basic lighting. Lighting is +* masked out with a noise pattern to provide something to denoise. There are +* two options for the noise pattern. One is a fixed 2x2 dither pattern to +* stand-in for lighting at quarter resolution. The other is the common +* shadertoy random pattern as a stand-in for some fancier lighting without +* enough samples per pixel, like ray tracing. +* +* First a temporal denoising filter is applied. The temporal filter is only +* using normals to reject previous samples. The SVGF paper also describes using +* depth comparison to reject samples but that is not implemented here. +* +* Followed by some number of spatial filters. These are implemented like in the +* SVGF paper. As an alternative to the 5x5 Edge-Avoiding A-Trous filter, can +* select a 3x3 filter instead. The 3x3 filter takes fewer samples and covers a +* smaller area, but takes less time to compute. From a loosely eyeballed +* comparison, N 5x5 passes looks similar to N+1 3x3 passes. The wider spatial +* filters take a fair chunk of time to compute. I wonder if it would be a good +* idea to interleave the input texture before computing, after the first pass +* which skips zero pixels. +* +* I have not implemetened the variance guided part. +* +* There's also an optional TXAA pass to be applied after. I am not happy with +* its implementation yet, so it defaults to off here. +*/ + +/* +* References: +* Spatiotemporal Variance-Guided Filtering: Real-Time Reconstruction for +* Path-Traced Global Illumination. by Christoph Schied and more. +* - SVGF denoising algorithm +* +* Streaming G-Buffer Compression for Multi-Sample Anti-Aliasing. +* by E. Kerzner and M. Salvi. +* - details about history comparison for temporal denoising filter +* +* Edge-Avoiding À-Trous Wavelet Transform for Fast Global Illumination +* Filtering. by Holger Dammertz and more. +* - details about a-trous algorithm for spatial denoising filter +*/ + + +#include +#include +#include +#include +#include +#include + + +namespace { + +#define DENOISE_MAX_PASSES 6 + +// Gbuffer has multiple render targets +#define GBUFFER_RT_COLOR 0 +#define GBUFFER_RT_NORMAL 1 +#define GBUFFER_RT_VELOCITY 2 +#define GBUFFER_RT_DEPTH 3 +#define GBUFFER_RENDER_TARGETS 4 + +#define MODEL_COUNT 100 + +static const char * s_meshPaths[] = +{ + "meshes/column.bin", + "meshes/tree.bin", + "meshes/hollowcube.bin", + "meshes/bunny.bin" +}; + +static const float s_meshScale[] = +{ + 0.05f, + 0.15f, + 0.25f, + 0.25f +}; + +// Vertex decl for our screen space quad (used in deferred rendering) +struct PosTexCoord0Vertex +{ + float m_x; + float m_y; + float m_z; + float m_u; + float m_v; + + static void init() + { + ms_layout + .begin() + .add(bgfx::Attrib::Position, 3, bgfx::AttribType::Float) + .add(bgfx::Attrib::TexCoord0, 2, bgfx::AttribType::Float) + .end(); + } + + static bgfx::VertexLayout ms_layout; +}; + +bgfx::VertexLayout PosTexCoord0Vertex::ms_layout; + +struct Uniforms +{ + enum { NumVec4 = 13 }; + + void init() { + u_params = bgfx::createUniform("u_params", bgfx::UniformType::Vec4, NumVec4); + }; + + void submit() const { + bgfx::setUniform(u_params, m_params, NumVec4); + } + + void destroy() { + bgfx::destroy(u_params); + } + + union + { + struct + { + /* 0 */ struct { float m_cameraJitterCurr[2]; float m_cameraJitterPrev[2]; }; + /* 1 */ struct { float m_feedbackMin; float m_feedbackMax; float m_unused1[2]; }; + /* 2 */ struct { float m_unused2; float m_applyMitchellFilter; float m_options[2]; }; + /* 3-6 */ struct { float m_worldToViewPrev[16]; }; + /* 7-10 */ struct { float m_viewToProjPrev[16]; }; + /* 11 */ struct { float m_frameOffsetForNoise; float m_noiseType; float m_unused11[2]; }; + /* 12 */ struct { float m_denoiseStep; float m_sigmaDepth; float m_sigmaNormal; float m_unused12; }; + }; + + float m_params[NumVec4 * 4]; + }; + + bgfx::UniformHandle u_params; +}; + +struct RenderTarget +{ + void init(uint32_t _width, uint32_t _height, bgfx::TextureFormat::Enum _format, uint64_t _flags) + { + m_texture = bgfx::createTexture2D(uint16_t(_width), uint16_t(_height), false, 1, _format, _flags); + const bool destroyTextures = true; + m_buffer = bgfx::createFrameBuffer(1, &m_texture, destroyTextures); + } + + void destroy() + { + // also responsible for destroying texture + bgfx::destroy(m_buffer); + } + + bgfx::TextureHandle m_texture; + bgfx::FrameBufferHandle m_buffer; +}; + +void screenSpaceQuad(float _textureWidth, float _textureHeight, float _texelHalf, bool _originBottomLeft, float _width = 1.0f, float _height = 1.0f) +{ + if (3 == bgfx::getAvailTransientVertexBuffer(3, PosTexCoord0Vertex::ms_layout)) + { + bgfx::TransientVertexBuffer vb; + bgfx::allocTransientVertexBuffer(&vb, 3, PosTexCoord0Vertex::ms_layout); + PosTexCoord0Vertex* vertex = (PosTexCoord0Vertex*)vb.data; + + const float minx = -_width; + const float maxx = _width; + const float miny = 0.0f; + const float maxy = _height * 2.0f; + + const float texelHalfW = _texelHalf / _textureWidth; + const float texelHalfH = _texelHalf / _textureHeight; + const float minu = -1.0f + texelHalfW; + const float maxu = 1.0f + texelHalfW; + + const float zz = 0.0f; + + float minv = texelHalfH; + float maxv = 2.0f + texelHalfH; + + if (_originBottomLeft) + { + float temp = minv; + minv = maxv; + maxv = temp; + + minv -= 1.0f; + maxv -= 1.0f; + } + + vertex[0].m_x = minx; + vertex[0].m_y = miny; + vertex[0].m_z = zz; + vertex[0].m_u = minu; + vertex[0].m_v = minv; + + vertex[1].m_x = maxx; + vertex[1].m_y = miny; + vertex[1].m_z = zz; + vertex[1].m_u = maxu; + vertex[1].m_v = minv; + + vertex[2].m_x = maxx; + vertex[2].m_y = maxy; + vertex[2].m_z = zz; + vertex[2].m_u = maxu; + vertex[2].m_v = maxv; + + bgfx::setVertexBuffer(0, &vb); + } +} + +void vec2Set(float* _v, float _x, float _y) +{ + _v[0] = _x; + _v[1] = _y; +} + +void vec4Set(float* _v, float _x, float _y, float _z, float _w) +{ + _v[0] = _x; + _v[1] = _y; + _v[2] = _z; + _v[3] = _w; +} + +void mat4Set(float * _m, const float * _src) +{ + const uint32_t MAT4_FLOATS = 16; + for (uint32_t ii = 0; ii < MAT4_FLOATS; ++ii) { + _m[ii] = _src[ii]; + } +} + +class ExampleDenoise : public entry::AppI +{ +public: + ExampleDenoise(const char* _name, const char* _description) + : entry::AppI(_name, _description) + , m_currFrame(UINT32_MAX) + , m_texelHalf(0.0f) + { + } + + void init(int32_t _argc, const char* const* _argv, uint32_t _width, uint32_t _height) override + { + Args args(_argc, _argv); + + m_width = _width; + m_height = _height; + m_debug = BGFX_DEBUG_NONE; + m_reset = BGFX_RESET_VSYNC; + + bgfx::Init init; + init.type = args.m_type; + + init.vendorId = args.m_pciId; + init.resolution.width = m_width; + init.resolution.height = m_height; + init.resolution.reset = m_reset; + bgfx::init(init); + + // Enable debug text. + bgfx::setDebug(m_debug); + + // Create uniforms + m_uniforms.init(); + + // Create texture sampler uniforms (used when we bind textures) + s_albedo = bgfx::createUniform("s_albedo", bgfx::UniformType::Sampler); // Model's source albedo + s_color = bgfx::createUniform("s_color", bgfx::UniformType::Sampler); // Color (albedo) gbuffer, default color input + s_normal = bgfx::createUniform("s_normal", bgfx::UniformType::Sampler); // Normal gbuffer, Model's source normal + s_velocity = bgfx::createUniform("s_velocity", bgfx::UniformType::Sampler); // Velocity gbuffer + s_depth = bgfx::createUniform("s_depth", bgfx::UniformType::Sampler); // Depth gbuffer + s_previousColor = bgfx::createUniform("s_previousColor", bgfx::UniformType::Sampler); // Previous frame's result + s_previousNormal = bgfx::createUniform("s_previousNormal", bgfx::UniformType::Sampler); // Previous frame's gbuffer normal + + // Create program from shaders. + m_gbufferProgram = loadProgram("vs_denoise_gbuffer", "fs_denoise_gbuffer"); // Fill gbuffer + m_combineProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_deferred_combine"); // Compute lighting from gbuffer + m_copyProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_copy"); + m_denoiseTemporalProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_temporal"); + m_denoiseSpatialProgram3x3 = loadProgram("vs_denoise_screenquad", "fs_denoise_spatial_3x3"); + m_denoiseSpatialProgram5x5 = loadProgram("vs_denoise_screenquad", "fs_denoise_spatial_5x5"); + m_denoiseApplyLighting = loadProgram("vs_denoise_screenquad", "fs_denoise_apply_lighting"); + m_txaaProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_txaa"); + + // Load some meshes + for (uint32_t ii = 0; ii < BX_COUNTOF(s_meshPaths); ++ii) + { + m_meshes[ii] = meshLoad(s_meshPaths[ii]); + } + + // Randomly create some models + bx::RngMwc mwc; + for (uint32_t ii = 0; ii < BX_COUNTOF(m_models); ++ii) + { + Model& model = m_models[ii]; + + model.mesh = mwc.gen() % BX_COUNTOF(s_meshPaths); + model.position[0] = (((mwc.gen() % 256)) - 128.0f) / 20.0f; + model.position[1] = 0; + model.position[2] = (((mwc.gen() % 256)) - 128.0f) / 20.0f; + } + + // Load ground, just use the cube + m_ground = meshLoad("meshes/cube.bin"); + + m_groundTexture = loadTexture("textures/fieldstone-rgba.dds"); + m_normalTexture = loadTexture("textures/fieldstone-n.dds"); + + m_recreateFrameBuffers = false; + createFramebuffers(); + + // Vertex decl + PosTexCoord0Vertex::init(); + + // Init camera + cameraCreate(); + cameraSetPosition({ 0.0f, 1.5f, 0.0f }); + cameraSetVerticalAngle(-0.3f); + m_fovY = 60.0f; + + // Init "prev" matrices, will be same for first frame + cameraGetViewMtx(m_view); + bx::mtxProj(m_proj, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, bgfx::getCaps()->homogeneousDepth); + mat4Set(m_worldToViewPrev, m_view); + mat4Set(m_viewToProjPrev, m_proj); + + // Track whether previous results are valid + m_havePrevious = false; + + // Get renderer capabilities info. + const bgfx::RendererType::Enum renderer = bgfx::getRendererType(); + m_texelHalf = bgfx::RendererType::Direct3D9 == renderer ? 0.5f : 0.0f; + + imguiCreate(); + } + + int32_t shutdown() override + { + for (uint32_t ii = 0; ii < BX_COUNTOF(s_meshPaths); ++ii) + { + meshUnload(m_meshes[ii]); + } + meshUnload(m_ground); + + bgfx::destroy(m_normalTexture); + bgfx::destroy(m_groundTexture); + + bgfx::destroy(m_gbufferProgram); + bgfx::destroy(m_combineProgram); + bgfx::destroy(m_copyProgram); + bgfx::destroy(m_denoiseTemporalProgram); + bgfx::destroy(m_denoiseSpatialProgram3x3); + bgfx::destroy(m_denoiseSpatialProgram5x5); + bgfx::destroy(m_denoiseApplyLighting); + bgfx::destroy(m_txaaProgram); + + m_uniforms.destroy(); + + bgfx::destroy(s_albedo); + bgfx::destroy(s_color); + bgfx::destroy(s_normal); + bgfx::destroy(s_velocity); + bgfx::destroy(s_depth); + bgfx::destroy(s_previousColor); + bgfx::destroy(s_previousNormal); + + destroyFramebuffers(); + + cameraDestroy(); + + imguiDestroy(); + + bgfx::shutdown(); + + return 0; + } + + bool update() override + { + if (!entry::processEvents(m_width, m_height, m_debug, m_reset, &m_mouseState)) + { + // skip processing when minimized, otherwise crashing + if (0 == m_width || 0 == m_height) + { + return true; + } + + // Update frame timer + int64_t now = bx::getHPCounter(); + static int64_t last = now; + const int64_t frameTime = now - last; + last = now; + const double freq = double(bx::getHPFrequency()); + const float deltaTime = float(frameTime / freq); + const bgfx::Caps* caps = bgfx::getCaps(); + + if (m_size[0] != (int32_t)m_width + || m_size[1] != (int32_t)m_height + || m_recreateFrameBuffers) + { + destroyFramebuffers(); + createFramebuffers(); + m_recreateFrameBuffers = false; + } + + // Update camera + cameraUpdate(deltaTime*0.15f, m_mouseState); + + // Set up matrices for gbuffer + cameraGetViewMtx(m_view); + + updateUniforms(); + + bx::mtxProj(m_proj, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, caps->homogeneousDepth); + bx::mtxProj(m_proj2, m_fovY, float(m_size[0]) / float(m_size[1]), 0.01f, 100.0f, false); + + if (m_enableTxaa) + { + m_proj[2*4+0] += m_jitter[0] * (2.0f / m_size[0]); + m_proj[2*4+1] -= m_jitter[1] * (2.0f / m_size[1]); + } + + bgfx::ViewId view = 0; + + // Draw everything into gbuffer + { + bgfx::setViewName(view, "gbuffer"); + bgfx::setViewClear(view + , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH + , 0 + , 1.0f + , 0 + ); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_size[0]), uint16_t(m_size[1])); + bgfx::setViewTransform(view, m_view, m_proj); + // Make sure when we draw it goes into gbuffer and not backbuffer + bgfx::setViewFrameBuffer(view, m_gbuffer); + + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_WRITE_Z + | BGFX_STATE_DEPTH_TEST_LESS + ); + + drawAllModels(view, m_gbufferProgram, m_uniforms); + ++view; + } + + float orthoProj[16]; + bx::mtxOrtho(orthoProj, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f, caps->homogeneousDepth); + + // Shade gbuffer + { + bgfx::setViewName(view, "combine"); + + // for some reason, previous draws texture lingering in transform stack + // need to clear out, otherwise this copy is garbled. this used to work + // and broke after updating, but i last updated like 2 years ago. + float identity[16]; + bx::mtxIdentity(identity); + bgfx::setTransform(identity); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_currentColor.m_buffer); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_DEPTH_TEST_ALWAYS + ); + bgfx::setTexture(0, s_color, m_gbufferTex[GBUFFER_RT_COLOR]); + bgfx::setTexture(1, s_normal, m_gbufferTex[GBUFFER_RT_NORMAL]); + m_uniforms.submit(); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_combineProgram); + ++view; + } + + // update last texture written, to chain passes together + bgfx::TextureHandle lastTex = m_currentColor.m_texture; + + // denoise temporal pass + if (m_useTemporalPass && m_havePrevious) + { + bgfx::setViewName(view, "denoise temporal"); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_temporaryColor.m_buffer); + bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_DEPTH_TEST_ALWAYS); + + // want color, prevColor + // normal, prevNormal + // depth, prevDepth to reject previous samples from accumulating - skipping depth for now + + bgfx::setTexture(0, s_color, lastTex); + bgfx::setTexture(1, s_normal, m_gbufferTex[GBUFFER_RT_NORMAL]); + bgfx::setTexture(2, s_velocity, m_gbufferTex[GBUFFER_RT_VELOCITY]); + bgfx::setTexture(3, s_previousColor, m_previousDenoise.m_texture); + bgfx::setTexture(4, s_previousNormal, m_previousNormal.m_texture); + + m_uniforms.submit(); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_denoiseTemporalProgram); + ++view; + + lastTex = m_temporaryColor.m_texture; + } + + // denoise spatial passes + if (0 < m_denoisePasses) + { + // variable number of passes for denoise, alternate between two textures/buffers + bgfx::FrameBufferHandle destBuffer[DENOISE_MAX_PASSES] = { + m_previousDenoise.m_buffer, + m_currentColor.m_buffer, + m_temporaryColor.m_buffer, + m_currentColor.m_buffer, + m_temporaryColor.m_buffer, + m_currentColor.m_buffer + }; + + uint32_t denoisePasses = bx::min(DENOISE_MAX_PASSES, m_denoisePasses); + for (uint32_t i = 0; i < denoisePasses; ++i) + { + const char buffer[] = { 'd', 'e', 'n', 'o', 'i', 's', 'e', ' ', char('0'+i), 0 }; + bgfx::setViewName(view, buffer); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, destBuffer[i]); + bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_DEPTH_TEST_ALWAYS); + bgfx::setTexture(0, s_color, lastTex); + bgfx::setTexture(1, s_normal, m_gbufferTex[GBUFFER_RT_NORMAL]); + bgfx::setTexture(2, s_depth, m_gbufferTex[GBUFFER_RT_DEPTH]); + + // need to update some denoise uniforms per draw + float denoiseStepScale = bx::pow(2.0f, float(i)); + m_uniforms.m_denoiseStep = denoiseStepScale; + + m_uniforms.submit(); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::ProgramHandle spatialProgram = (0 == m_spatialSampleType) + ? m_denoiseSpatialProgram3x3 + : m_denoiseSpatialProgram5x5; + bgfx::submit(view, spatialProgram); + ++view; + + if (m_previousDenoise.m_buffer.idx == destBuffer[i].idx) + { + lastTex = m_previousDenoise.m_texture; + } + else if (m_temporaryColor.m_buffer.idx == destBuffer[i].idx) + { + lastTex = m_temporaryColor.m_texture; + } + else + { + lastTex = m_currentColor.m_texture; + } + } + } + else + { + // need color result for temporal denoise if not supplied by spatial pass + // (per SVGF paper, reuse previous frame's first spatial pass output as previous color + bgfx::setViewName(view, "copy color for temporal denoise"); + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_previousDenoise.m_buffer); + bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_DEPTH_TEST_ALWAYS); + bgfx::setTexture(0, s_color, lastTex); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_copyProgram); + ++view; + } + + // apply lighting + { + bgfx::setViewName(view, "apply lighting"); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + + bgfx::FrameBufferHandle destBuffer = (lastTex.idx == m_currentColor.m_texture.idx) + ? m_temporaryColor.m_buffer + : m_currentColor.m_buffer; + bgfx::setViewFrameBuffer(view, destBuffer); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_DEPTH_TEST_ALWAYS + ); + bgfx::setTexture(0, s_color, lastTex); + bgfx::setTexture(1, s_albedo, m_gbufferTex[GBUFFER_RT_COLOR]); + m_uniforms.submit(); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_denoiseApplyLighting); + ++view; + lastTex = (m_temporaryColor.m_buffer.idx == destBuffer.idx) + ? m_temporaryColor.m_texture + : m_currentColor.m_texture; + } + + if (m_enableTxaa) + { + // Draw txaa to txaa buffer + { + bgfx::setViewName(view, "temporal aa"); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_txaaColor.m_buffer); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_DEPTH_TEST_ALWAYS + ); + bgfx::setTexture(0, s_color, lastTex); + bgfx::setTexture(1, s_previousColor, m_previousColor.m_texture); + bgfx::setTexture(2, s_velocity, m_gbufferTex[GBUFFER_RT_VELOCITY]); + bgfx::setTexture(3, s_depth, m_gbufferTex[GBUFFER_RT_DEPTH]); + m_uniforms.submit(); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_txaaProgram); + ++view; + } + + // Copy txaa result to previous + { + bgfx::setViewName(view, "copy2previous"); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_previousColor.m_buffer); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_DEPTH_TEST_ALWAYS + ); + bgfx::setTexture(0, s_color, m_txaaColor.m_texture); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_copyProgram); + ++view; + } + + // Copy txaa result to swap chain + { + bgfx::setViewName(view, "display"); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + | BGFX_STATE_DEPTH_TEST_ALWAYS + ); + bgfx::setTexture(0, s_color, m_txaaColor.m_texture); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_copyProgram); + ++view; + } + } + else + { + // Copy color result to swap chain + { + bgfx::setViewName(view, "display"); + bgfx::setViewClear(view + , BGFX_CLEAR_NONE + , 0 + , 1.0f + , 0 + ); + + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, BGFX_INVALID_HANDLE); + bgfx::setState(0 + | BGFX_STATE_WRITE_RGB + | BGFX_STATE_WRITE_A + ); + bgfx::setTexture(0, s_color, lastTex); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_copyProgram); + ++view; + } + } + + // copy the normal buffer for next time + { + bgfx::setViewName(view, "copy normals"); + bgfx::setViewRect(view, 0, 0, uint16_t(m_width), uint16_t(m_height)); + bgfx::setViewTransform(view, NULL, orthoProj); + bgfx::setViewFrameBuffer(view, m_previousNormal.m_buffer); + bgfx::setState(BGFX_STATE_WRITE_RGB | BGFX_STATE_WRITE_A | BGFX_STATE_DEPTH_TEST_ALWAYS); + bgfx::setTexture(0, s_color, m_gbufferTex[GBUFFER_RT_NORMAL]); + screenSpaceQuad(float(m_width), float(m_height), m_texelHalf, caps->originBottomLeft); + bgfx::submit(view, m_copyProgram); + ++view; + + // update previous status + m_havePrevious = true; + } + + // Copy matrices for next time + mat4Set(m_worldToViewPrev, m_view); + mat4Set(m_viewToProjPrev, m_proj); + + // Draw UI + imguiBeginFrame(m_mouseState.m_mx + , m_mouseState.m_my + , (m_mouseState.m_buttons[entry::MouseButton::Left] ? IMGUI_MBUT_LEFT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Right] ? IMGUI_MBUT_RIGHT : 0) + | (m_mouseState.m_buttons[entry::MouseButton::Middle] ? IMGUI_MBUT_MIDDLE : 0) + , m_mouseState.m_mz + , uint16_t(m_width) + , uint16_t(m_height) + ); + + showExampleDialog(this); + + ImGui::SetNextWindowPos( + ImVec2(m_width - m_width / 4.0f - 10.0f, 10.0f) + , ImGuiCond_FirstUseEver + ); + ImGui::SetNextWindowSize( + ImVec2(m_width / 4.0f, m_height / 1.24f) + , ImGuiCond_FirstUseEver + ); + ImGui::Begin("Settings" + , NULL + , 0 + ); + + ImGui::PushItemWidth(ImGui::GetWindowWidth() * 0.5f); + + { + ImGui::TextWrapped( + "In this demo, noise is added to results of deferred lighting. Then denoise is applied " + "before multiplying the lit result with gbuffer albedo. Optionally, temporal antialiasing " + "can be applied after that. (off by default, implementation blurry)"); + ImGui::Separator(); + + ImGui::Text("noise controls:"); + ImGui::Combo("pattern", &m_noiseType, "none\0dither\0random\0\0"); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("none"); + ImGui::BulletText("compare denoised results to this"); + ImGui::BulletText("brighter than noisy images, not losing any pixel's energy"); + ImGui::Text("dither"); + ImGui::BulletText("reject 3 out of 4 pixels in 2x2 pattern"); + ImGui::BulletText("could represent lower resolution signal"); + ImGui::Text("random"); + ImGui::BulletText("reject about half pixels, using common shader random"); + ImGui::BulletText("could represent monte carlo something or other"); + ImGui::EndTooltip(); + } + + ImGui::Checkbox("dynamic noise", &m_dynamicNoise); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("update noise pattern each frame"); + ImGui::Separator(); + } + + { + ImGui::Text("temporal denoise pass controls:"); + ImGui::Checkbox("use temporal pass", &m_useTemporalPass); + ImGui::Separator(); + } + + { + ImGui::Text("spatial denoise pass controls:"); + ImGui::SliderInt("spatial passes", &m_denoisePasses, 0, DENOISE_MAX_PASSES); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("set passes to 0 to turn off spatial denoise"); + + ImGui::Combo("spatial sample extent", &m_spatialSampleType, "three\0five\0\0"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("select 3x3 or 5x5 filter kernal"); + + ImGui::SliderFloat("sigma z", &m_sigmaDepth, 0.0f, 0.1f, "%.5f"); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("lower sigma z, pickier blending across depth edges"); + + ImGui::SliderFloat("sigma n", &m_sigmaNormal, 1.0f, 256.0f); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("higher sigma n, pickier blending across normal edges"); + ImGui::Separator(); + } + + if (ImGui::CollapsingHeader("TXAA options")) + { + ImGui::Checkbox("use TXAA", &m_enableTxaa); + ImGui::Checkbox("apply extra blur to current color", &m_applyMitchellFilter); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("reduces flicker/crawl on thin features, maybe too much!"); + + ImGui::SliderFloat("feedback min", &m_feedbackMin, 0.0f, 1.0f); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("minimum amount of previous frame to blend in"); + + ImGui::SliderFloat("feedback max", &m_feedbackMax, 0.0f, 1.0f); + if (ImGui::IsItemHovered()) + ImGui::SetTooltip("maximum amount of previous frame to blend in"); + + ImGui::Checkbox("debug TXAA with slow frame rate", &m_useTxaaSlow); + if (ImGui::IsItemHovered()) + { + ImGui::BeginTooltip(); + ImGui::Text("sleep 100ms per frame to highlight temporal artifacts"); + ImGui::Text("high framerate compensates for flickering, masking issues"); + ImGui::EndTooltip(); + } + ImGui::Separator(); + } + + ImGui::End(); + + imguiEndFrame(); + + // Advance to next frame. Rendering thread will be kicked to + // process submitted rendering primitives. + m_currFrame = bgfx::frame(); + + // add artificial wait to emphasize txaa behavior + if (m_useTxaaSlow) + { + bx::sleep(100); + } + + return true; + } + + return false; + } + + void drawAllModels(bgfx::ViewId _pass, bgfx::ProgramHandle _program, const Uniforms & _uniforms) + { + for (uint32_t ii = 0; ii < BX_COUNTOF(m_models); ++ii) + { + const Model& model = m_models[ii]; + + // Set up transform matrix for each model + const float scale = s_meshScale[model.mesh]; + float mtx[16]; + bx::mtxSRT(mtx + , scale + , scale + , scale + , 0.0f + , 0.0f + , 0.0f + , model.position[0] + , model.position[1] + , model.position[2] + ); + + // Submit mesh to gbuffer + bgfx::setTexture(0, s_albedo, m_groundTexture); + bgfx::setTexture(1, s_normal, m_normalTexture); + _uniforms.submit(); + + meshSubmit(m_meshes[model.mesh], _pass, _program, mtx); + } + + // Draw ground + float mtxScale[16]; + const float scale = 10.0f; + bx::mtxScale(mtxScale, scale, scale, scale); + + float mtxTranslate[16]; + bx::mtxTranslate(mtxTranslate + , 0.0f + , -10.0f + , 0.0f + ); + + float mtx[16]; + bx::mtxMul(mtx, mtxScale, mtxTranslate); + bgfx::setTexture(0, s_albedo, m_groundTexture); + bgfx::setTexture(1, s_normal, m_normalTexture); + _uniforms.submit(); + + meshSubmit(m_ground, _pass, _program, mtx); + } + + void createFramebuffers() + { + m_size[0] = m_width; + m_size[1] = m_height; + + const uint64_t bilinearFlags = 0 + | BGFX_TEXTURE_RT + | BGFX_SAMPLER_U_CLAMP + | BGFX_SAMPLER_V_CLAMP + ; + + const uint64_t pointSampleFlags = bilinearFlags + | BGFX_SAMPLER_MIN_POINT + | BGFX_SAMPLER_MAG_POINT + | BGFX_SAMPLER_MIP_POINT + ; + + m_gbufferTex[GBUFFER_RT_COLOR] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::BGRA8, pointSampleFlags); + m_gbufferTex[GBUFFER_RT_NORMAL] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::BGRA8, pointSampleFlags); + m_gbufferTex[GBUFFER_RT_VELOCITY] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::RG16F, pointSampleFlags); + m_gbufferTex[GBUFFER_RT_DEPTH] = bgfx::createTexture2D(uint16_t(m_size[0]), uint16_t(m_size[1]), false, 1, bgfx::TextureFormat::D24, pointSampleFlags); + m_gbuffer = bgfx::createFrameBuffer(BX_COUNTOF(m_gbufferTex), m_gbufferTex, true); + + m_currentColor.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, bilinearFlags); + m_previousColor.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, bilinearFlags); + m_txaaColor.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, bilinearFlags); + m_temporaryColor.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, bilinearFlags); + m_previousNormal.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, pointSampleFlags); + m_previousDenoise.init(m_size[0], m_size[1], bgfx::TextureFormat::RG11B10F, bilinearFlags); + } + + // all buffers set to destroy their textures + void destroyFramebuffers() + { + bgfx::destroy(m_gbuffer); + + m_currentColor.destroy(); + m_previousColor.destroy(); + m_txaaColor.destroy(); + m_temporaryColor.destroy(); + m_previousNormal.destroy(); + m_previousDenoise.destroy(); + } + + void updateUniforms() + { + { + uint32_t idx = m_currFrame % 8; + const float offsets[] = { + (1.0f/2.0f), (1.0f/3.0f), + (1.0f/4.0f), (2.0f/3.0f), + (3.0f/4.0f), (1.0f/9.0f), + (1.0f/8.0f), (4.0f/9.0f), + (5.0f/8.0f), (7.0f/9.0f), + (3.0f/8.0f), (2.0f/9.0f), + (7.0f/8.0f), (5.0f/9.0f), + (1.0f/16.0f), (8.0f/9.0f) + }; + + // Strange constant for jitterX is because 8 values from halton2 + // sequence above do not average out to 0.5, 1/16 skews it to the + // left. Subtracting a smaller value to center the range of jitter + // around 0. Not necessary for jitterY. Not confident this makes sense... + const float jitterX = 1.0f * (offsets[2*idx] - (7.125f/16.0f)); + const float jitterY = 1.0f * (offsets[2*idx+1] - 0.5f); + + vec2Set(m_uniforms.m_cameraJitterCurr, jitterX, jitterY); + vec2Set(m_uniforms.m_cameraJitterPrev, m_jitter[0], m_jitter[1]); + + m_jitter[0] = jitterX; + m_jitter[1] = jitterY; + } + + m_uniforms.m_feedbackMin = m_feedbackMin; + m_uniforms.m_feedbackMax = m_feedbackMax; + m_uniforms.m_applyMitchellFilter = m_applyMitchellFilter ? 1.0f : 0.0f; + + mat4Set(m_uniforms.m_worldToViewPrev, m_worldToViewPrev); + mat4Set(m_uniforms.m_viewToProjPrev, m_viewToProjPrev); + + m_uniforms.m_frameOffsetForNoise = m_dynamicNoise + ? float(m_currFrame % 8) + : 0.0f; + m_uniforms.m_noiseType = float(m_noiseType); + m_uniforms.m_sigmaDepth = m_sigmaDepth; + m_uniforms.m_sigmaNormal = m_sigmaNormal; + } + + + uint32_t m_width; + uint32_t m_height; + uint32_t m_debug; + uint32_t m_reset; + + entry::MouseState m_mouseState; + + // Resource handles + bgfx::ProgramHandle m_gbufferProgram; + bgfx::ProgramHandle m_combineProgram; + bgfx::ProgramHandle m_copyProgram; + bgfx::ProgramHandle m_denoiseTemporalProgram; + bgfx::ProgramHandle m_denoiseSpatialProgram3x3; + bgfx::ProgramHandle m_denoiseSpatialProgram5x5; + bgfx::ProgramHandle m_denoiseApplyLighting; + bgfx::ProgramHandle m_txaaProgram; + + // Shader uniforms + Uniforms m_uniforms; + + // Uniforms to indentify texture samplers + bgfx::UniformHandle s_albedo; + bgfx::UniformHandle s_color; + bgfx::UniformHandle s_normal; + bgfx::UniformHandle s_velocity; + bgfx::UniformHandle s_depth; + bgfx::UniformHandle s_previousColor; + bgfx::UniformHandle s_previousNormal; + + bgfx::FrameBufferHandle m_gbuffer; + bgfx::TextureHandle m_gbufferTex[GBUFFER_RENDER_TARGETS]; + + RenderTarget m_currentColor; + RenderTarget m_previousColor; + RenderTarget m_txaaColor; + RenderTarget m_temporaryColor; // need another buffer to ping-pong results + RenderTarget m_previousNormal; + RenderTarget m_previousDenoise; // color output by first spatial denoise pass, input to next frame as previous color + + struct Model + { + uint32_t mesh; // Index of mesh in m_meshes + float position[3]; + }; + + Model m_models[MODEL_COUNT]; + Mesh* m_meshes[BX_COUNTOF(s_meshPaths)]; + Mesh* m_ground; + bgfx::TextureHandle m_groundTexture; + bgfx::TextureHandle m_normalTexture; + + uint32_t m_currFrame; + float m_texelHalf = 0.0f; + float m_fovY = 60.0f; + bool m_recreateFrameBuffers = false; + bool m_havePrevious = false; + + float m_view[16]; + float m_proj[16]; + float m_proj2[16]; + float m_viewToProjPrev[16]; + float m_worldToViewPrev[16]; + float m_jitter[2]; + int32_t m_size[2]; + + // UI parameters + int32_t m_noiseType = 2; + bool m_dynamicNoise = true; + bool m_useTemporalPass = true; + int32_t m_spatialSampleType = 1; + int32_t m_denoisePasses = 5; + float m_sigmaDepth = 0.05f; + float m_sigmaNormal = 128.0f; + bool m_enableTxaa = false; + float m_feedbackMin = 0.8f; + float m_feedbackMax = 0.95f; + bool m_applyMitchellFilter = true; + bool m_useTxaaSlow = false; +}; + +} // namespace + +ENTRY_IMPLEMENT_MAIN(ExampleDenoise, "xx-denoise", "Denoise."); diff --git a/examples/xx-denoise/fs_denoise_apply_lighting.sc b/examples/xx-denoise/fs_denoise_apply_lighting.sc new file mode 100644 index 000000000..3b0b4427a --- /dev/null +++ b/examples/xx-denoise/fs_denoise_apply_lighting.sc @@ -0,0 +1,25 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" + +SAMPLER2D(s_color, 0); +SAMPLER2D(s_albedo, 1); + +void main() +{ + vec2 texCoord = v_texcoord0; + vec3 lightColor = texture2D(s_color, texCoord).xyz; + vec3 albedo = texture2D(s_albedo, texCoord).xyz; + albedo = toLinear(albedo); + + vec3 color = lightColor * albedo; + color = toGamma(color); + + gl_FragColor = vec4(color, 1.0); +} diff --git a/examples/xx-denoise/fs_denoise_copy.sc b/examples/xx-denoise/fs_denoise_copy.sc new file mode 100644 index 000000000..5cba19fea --- /dev/null +++ b/examples/xx-denoise/fs_denoise_copy.sc @@ -0,0 +1,18 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" + +SAMPLER2D(s_color, 0); + +void main() +{ + vec2 texCoord = v_texcoord0; + vec4 color = texture2D(s_color, texCoord); + gl_FragColor = color; +} diff --git a/examples/xx-denoise/fs_denoise_deferred_combine.sc b/examples/xx-denoise/fs_denoise_deferred_combine.sc new file mode 100644 index 000000000..51a39978d --- /dev/null +++ b/examples/xx-denoise/fs_denoise_deferred_combine.sc @@ -0,0 +1,61 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" +#include "normal_encoding.sh" + +SAMPLER2D(s_color, 0); +SAMPLER2D(s_normal, 1); + +float ShadertoyNoise (vec2 uv) { + return fract(sin(dot(uv.xy, vec2(12.9898,78.233))) * 43758.5453123); +} + +int ModHelper (float a, float b) +{ + return int( a - (b*floor(a/b))); +} + +void main() +{ + vec2 texCoord = v_texcoord0; + + // mess with result so there's something to denosie + float sn = 1.0; + if (1.5 < u_noiseType) + { + sn = ShadertoyNoise(gl_FragCoord.xy + vec2(314.0, 159.0)*u_frameIdx); + sn = (sn < 0.5) ? 0.0 : 1.0; + } + else if (0.5 < u_noiseType) + { + // having trouble compiling for gles when using % or mod :( + int modCoordX = ModHelper(gl_FragCoord.x, 2.0); + int modCoordY = ModHelper(gl_FragCoord.y, 2.0); + int frameSelect = modCoordY * 2 + modCoordX; + int frameMod4 = ModHelper(u_frameIdx, 4.0); + sn = (frameSelect == frameMod4) ? 1.0 : 0.0; + } + + vec4 normalRoughness = texture2D(s_normal, texCoord).xyzw; + vec3 normal = NormalDecode(normalRoughness.xyz); + float roughness = 0.5; + + // need to get a valid view vector for any microfacet stuff :( + float gloss = 1.0-roughness; + float specPower = 1022.0 * gloss + 2.0; + + vec3 light = normalize(vec3(-0.2, 1.0, -0.2)); + float NdotL = saturate(dot(normal, light)); + float diff = NdotL*0.99 + 0.01; + float spec = 5.0 * pow(NdotL, specPower); + + float lightAmt = (diff + spec) * sn; + + gl_FragColor = vec4(vec3_splat(lightAmt), 1.0); +} diff --git a/examples/xx-denoise/fs_denoise_gbuffer.sc b/examples/xx-denoise/fs_denoise_gbuffer.sc new file mode 100644 index 000000000..8c86314aa --- /dev/null +++ b/examples/xx-denoise/fs_denoise_gbuffer.sc @@ -0,0 +1,69 @@ +$input v_normal, v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3 + +#include "../common/common.sh" +#include "parameters.sh" +#include "normal_encoding.sh" + +SAMPLER2D(s_albedo, 0); +SAMPLER2D(s_normal, 1); + +// http://www.thetenthplanet.de/archives/1180 +// "followup: normal mapping without precomputed tangents" +mat3 cotangentFrame(vec3 N, vec3 p, vec2 uv) +{ + // get edge vectors of the pixel triangle + vec3 dp1 = dFdx(p); + vec3 dp2 = dFdy(p); + vec2 duv1 = dFdx(uv); + vec2 duv2 = dFdy(uv); + + // solve the linear system + vec3 dp2perp = cross(dp2, N); + vec3 dp1perp = cross(N, dp1); + vec3 T = dp2perp * duv1.x + dp1perp * duv2.x; + vec3 B = dp2perp * duv1.y + dp1perp * duv2.y; + + // construct a scale-invariant frame + float invMax = inversesqrt(max(dot(T,T), dot(B,B))); + return mat3(T*invMax, B*invMax, N); +} + +void main() +{ + vec3 albedo = toLinear(texture2D(s_albedo, v_texcoord0).xyz); + + // get vertex normal + vec3 normal = normalize(v_normal); + + // get normal map normal, unpack, and calculate z + vec3 normalMap; + normalMap.xy = texture2D(s_normal, v_texcoord0).xy; + normalMap.xy = normalMap.xy * 2.0 - 1.0; + normalMap.z = sqrt(1.0 - dot(normalMap.xy, normalMap.xy)); + + // swap x and y, because the brick texture looks flipped, don't copy this... + normalMap.xy = normalMap.yx; + + // perturb geometry normal by normal map + vec3 pos = v_texcoord2.xyz; // contains world space pos + mat3 TBN = cotangentFrame(normal, pos, v_texcoord0); + vec3 bumpedNormal = normalize(instMul(TBN, normalMap)); + + // need some proxy for roughness value w/o roughness texture + // assume horizontal (blue) normal map is smooth, and then + // modulate with albedo for some higher frequency detail + float roughness = normalMap.z * mix(0.9, 1.0, albedo.y); + roughness = roughness * 0.6 + 0.2; + + // Calculate velocity as delta position from previous frame to this + vec2 previousNDC = v_texcoord1.xy * (1.0/v_texcoord1.w); + previousNDC.y *= -1.0; + previousNDC = previousNDC * 0.5 + 0.5; + vec2 velocity = gl_FragCoord.xy*u_viewTexel.xy - previousNDC; + + vec3 bufferNormal = NormalEncode(bumpedNormal); + + gl_FragData[0] = vec4(toGamma(albedo), 1.0); + gl_FragData[1] = vec4(bufferNormal, roughness); // Todo, better packing + gl_FragData[2] = vec4(velocity, 0.0, 0.0); +} diff --git a/examples/xx-denoise/fs_denoise_spatial_3x3.sc b/examples/xx-denoise/fs_denoise_spatial_3x3.sc new file mode 100644 index 000000000..1cf0075fe --- /dev/null +++ b/examples/xx-denoise/fs_denoise_spatial_3x3.sc @@ -0,0 +1,12 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +// don't use 5x5 sample pattern for spatial denoise, use 3x3 instead +#define USE_SPATIAL_5X5 0 + +// includes main function to implement spatial pattern +#include "fs_denoise_spatial_implementation.sh" diff --git a/examples/xx-denoise/fs_denoise_spatial_5x5.sc b/examples/xx-denoise/fs_denoise_spatial_5x5.sc new file mode 100644 index 000000000..f9d36eb0a --- /dev/null +++ b/examples/xx-denoise/fs_denoise_spatial_5x5.sc @@ -0,0 +1,12 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +// use 5x5 sample pattern for spatial denoise +#define USE_SPATIAL_5X5 1 + +// includes main function to implement spatial pattern +#include "fs_denoise_spatial_implementation.sh" diff --git a/examples/xx-denoise/fs_denoise_spatial_implementation.sh b/examples/xx-denoise/fs_denoise_spatial_implementation.sh new file mode 100644 index 000000000..8cfde98d9 --- /dev/null +++ b/examples/xx-denoise/fs_denoise_spatial_implementation.sh @@ -0,0 +1,97 @@ +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#ifndef FS_DENOISE_SPATIAL_IMPLEMENTATION_SH +#define FS_DENOISE_SPATIAL_IMPLEMENTATION_SH + +#include "../common/common.sh" +#include "parameters.sh" +#include "normal_encoding.sh" + +SAMPLER2D(s_color, 0); // input color, signal to be denoised +SAMPLER2D(s_normal, 1); // scene's gbuffer normal, used for edge stopping function +SAMPLER2D(s_depth, 2); // scene's depth, used for edge stopping function + +void main() +{ + vec2 texCoord = v_texcoord0; + + // read center pixel + vec4 color = texture2D(s_color, texCoord); + vec3 normal = NormalDecode(texture2D(s_normal, texCoord).xyz); // * 2.0 - 1.0; + float depth = texture2D(s_depth, texCoord).x; + // want depth gradient for edge stopping function + float depthGradient = abs(dFdx(depth)) + abs(dFdy(depth)); + + float du = u_texCoordStep * u_viewTexel.x; + float dv = u_texCoordStep * u_viewTexel.y; + +#if USE_SPATIAL_5X5 + float gaussianWeights[5]; + gaussianWeights[0] = 1.0/16.0; + gaussianWeights[1] = 4.0/16.0; + gaussianWeights[2] = 6.0/16.0; + gaussianWeights[3] = 4.0/16.0; + gaussianWeights[4] = 1.0/16.0; + float initialWeight = (gaussianWeights[2]*gaussianWeights[2]); + int centerIdx = 2; + + vec4 accumulateColor = color * initialWeight; + float accumulateWeight = initialWeight; + + for (int yy = 0; yy < 5; ++yy) + { + for (int xx = 0; xx < 5; ++xx) + { +#else + float gaussianWeights[3]; + gaussianWeights[0] = 1.0/4.0; + gaussianWeights[1] = 2.0/4.0; + gaussianWeights[2] = 1.0/4.0; + float initialWeight = (gaussianWeights[1]*gaussianWeights[1]); + int centerIdx = 1; + + vec4 accumulateColor = color * initialWeight; + float accumulateWeight = initialWeight; + + for (int yy = 0; yy < 3; ++yy) + { + for (int xx = 0; xx < 3; ++xx) + { +#endif // USE_SPATIAL_5X5 + if ((centerIdx == xx) && (centerIdx == yy)) { + continue; + } + + float xOffset = float(xx) - float(centerIdx); + float yOffset = float(yy) - float(centerIdx); + vec2 sampleTexCoord = texCoord; + sampleTexCoord.x += xOffset * du; + sampleTexCoord.y += yOffset * dv; + + vec4 sampleColor = texture2D(s_color, sampleTexCoord); + vec3 sampleNormal = NormalDecode(texture2D(s_normal, sampleTexCoord).xyz); + float normalWeight = pow(saturate(dot(normal, sampleNormal)), u_sigmaNormal); + + float sampleDepth = texture2D(s_depth, sampleTexCoord).x; + float depthDelta = depth - sampleDepth; + float depthWeight = exp(-abs(depthDelta) / max(1e-5, u_sigmaDepth*u_sigmaDepth)); + + float weight = depthWeight * normalWeight; + + // apply gaussian + weight *= (gaussianWeights[xx]*gaussianWeights[yy]); + + accumulateColor += sampleColor * weight; + accumulateWeight += weight; + } + } + + accumulateColor /= max(accumulateWeight, 1e-5); + + gl_FragColor = accumulateColor; +} + +#endif // FS_DENOISE_SPATIAL_IMPLEMENTATION_SH diff --git a/examples/xx-denoise/fs_denoise_temporal.sc b/examples/xx-denoise/fs_denoise_temporal.sc new file mode 100644 index 000000000..dcd513bdf --- /dev/null +++ b/examples/xx-denoise/fs_denoise_temporal.sc @@ -0,0 +1,96 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" +#include "normal_encoding.sh" +#include "shared_functions.sh" + +SAMPLER2D(s_color, 0); +SAMPLER2D(s_normal, 1); +SAMPLER2D(s_velocity, 2); +SAMPLER2D(s_previousColor, 3); // previous color +SAMPLER2D(s_previousNormal, 4); // previous normal + +#define COS_PI_OVER_4 0.70710678118 + +void main() +{ + vec2 texCoord = v_texcoord0; + + // read center pixel + vec4 color = texture2D(s_color, texCoord); + vec3 normal = NormalDecode(texture2D(s_normal, texCoord).xyz); + + // offset to last pixel + vec2 velocity = texture2D(s_velocity, texCoord).xy; + vec2 texCoordPrev = GetTexCoordPreviousNoJitter(texCoord, velocity); + + // SVGF approach suggests sampling and test/rejecting 4 contributing + // samples individually and then doing custom bilinear filter of result + + // multiply texCoordPrev by dimensions to get nearest pixels, produces (X.5, Y.5) coordinate + // under no motion, so subtract half here to get correct weights for bilinear filter. + // not thrilled by this, feels like something is wrong. + vec2 screenPixelPrev = texCoordPrev * u_viewRect.zw - vec2_splat(0.5); + vec2 screenPixelMin = floor(screenPixelPrev); + vec2 screenPixelMix = fract(screenPixelPrev); + + float x0 = 1.0 - screenPixelMix.x; + float x1 = screenPixelMix.x; + float y0 = 1.0 - screenPixelMix.y; + float y1 = screenPixelMix.y; + + float coordWeights[4]; + coordWeights[0] = x0*y0; + coordWeights[1] = x1*y0; + coordWeights[2] = x0*y1; + coordWeights[3] = x1*y1; + + // adding a half texel here to correct the modification above, in addition to pixel offset + // to grab adjacent pixels for bilinear filter. not thrilled by this, feels like something is wrong. + vec2 coords[4]; + coords[0] = (screenPixelMin + vec2(0.5, 0.5)) * u_viewTexel.xy; + coords[1] = (screenPixelMin + vec2(1.5, 0.5)) * u_viewTexel.xy; + coords[2] = (screenPixelMin + vec2(0.5, 1.5)) * u_viewTexel.xy; + coords[3] = (screenPixelMin + vec2(1.5, 1.5)) * u_viewTexel.xy; + + // SVGF paper mentions comparing depths and normals to establish + // whether samples are similar enough to contribute, but does not + // describe how. References the following paper, which uses threshold + // of cos(PI/4) to accept/reject. + // https://software.intel.com/content/www/us/en/develop/articles/streaming-g-buffer-compression-for-multi-sample-anti-aliasing.html + // this paper also discusses using depth derivatives to estimate overlapping depth range + + vec4 accumulatedColor = vec4_splat(0.0); + float accumulatedWeight = 0.0; + for (int i = 0; i < 4; ++i) + { + vec3 sampleNormal = NormalDecode(texture2D(s_previousNormal, coords[i]).xyz); + float normalSimilarity = dot(normal, sampleNormal); + float weight = (normalSimilarity < COS_PI_OVER_4) ? 0.0 : 1.0; + + vec4 sampleColor = texture2D(s_previousColor, coords[i]); + + weight *= coordWeights[i]; + accumulatedColor += sampleColor * weight; + accumulatedWeight += weight; + } + + if (0.0 < accumulatedWeight) + { + accumulatedColor *= (1.0 / accumulatedWeight); + color = mix(color, accumulatedColor, 0.8); + } + else + { + // debug colorize + //color.xyz *= vec3(0.5, 0.01, 0.65); + } + + gl_FragColor = color; +} diff --git a/examples/xx-denoise/fs_denoise_txaa.sc b/examples/xx-denoise/fs_denoise_txaa.sc new file mode 100644 index 000000000..e7fbb8e70 --- /dev/null +++ b/examples/xx-denoise/fs_denoise_txaa.sc @@ -0,0 +1,208 @@ +$input v_texcoord0 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" +#include "shared_functions.sh" + +#define APPLY_TXAA_IN_LINEAR 0 +#define DEBUG_HALF_SCREEN 0 + +SAMPLER2D(s_color, 0); // this frame's shaded color +SAMPLER2D(s_previousColor, 1); // previous frame's shaded color +SAMPLER2D(s_velocity, 2); // screenspace delta from previous to current frame +SAMPLER2D(s_depth, 3); // depth buffer + +vec3 FindNearestDepth(sampler2D _depthSampler, vec2 _texCoord) { + vec2 du = vec2(u_viewTexel.x, 0.0); + vec2 dv = vec2(u_viewTexel.y, 0.0); + + vec2 coord = _texCoord - du - dv; + vec3 tcd0 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord - dv; + vec3 tcd1 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord + du - dv; + vec3 tcd2 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord + du; + vec3 tcd3 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord; + vec3 tcd4 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord - du; + vec3 tcd5 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord - du + dv; + vec3 tcd6 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord + dv; + vec3 tcd7 = vec3(coord, texture2D(_depthSampler, coord).x); + coord = _texCoord + du + dv; + vec3 tcd8 = vec3(coord, texture2D(_depthSampler, coord).x); + + vec3 minTcd = tcd0; + if (tcd1.z < minTcd.z) minTcd = tcd1; + if (tcd2.z < minTcd.z) minTcd = tcd2; + if (tcd3.z < minTcd.z) minTcd = tcd3; + if (tcd4.z < minTcd.z) minTcd = tcd4; + if (tcd5.z < minTcd.z) minTcd = tcd5; + if (tcd6.z < minTcd.z) minTcd = tcd6; + if (tcd7.z < minTcd.z) minTcd = tcd7; + if (tcd8.z < minTcd.z) minTcd = tcd8; + + return minTcd; +} + +float Mitchell (float _b, float _c, float _x) { + + float v = 0.0; + float x = abs(_x); + float x2 = x*x; + float x3 = x2*x; + + if (x < 1.0) { + v = (12.0-9.0*_b-6.0*_c)*x3 + (-18.0+12.0*_b+6.0*_c)*x2 + (6.0-2.0*_b); + } + else if (x < 2.0) { + v = (-_b-6.0*_c)*x3 + (6.0*_b+30.0*_c)*x2 + (-12.0*_b-48.0*_c)*x + (8.0*_b+24.0*_c); + } + + return v*(1.0/6.0); +} + + +void main() +{ + vec2 texCoord = v_texcoord0; + vec3 colorCurr = texture2D(s_color, texCoord).xyz; + +#if DEBUG_HALF_SCREEN + if (texCoord.x > 0.5) { +#endif + + vec3 nearestCoordAndDepth = FindNearestDepth(s_depth, texCoord); + + vec2 velocity = texture2D(s_velocity, nearestCoordAndDepth.xy).xy; + vec2 texCoordPrev = GetTexCoordPrevious(texCoord, velocity); + + vec3 colorPrev = texture2D(s_previousColor, texCoordPrev).xyz; + + // Sample local neighborhood for variance clipping + vec2 du = vec2(u_viewTexel.x, 0.0); + vec2 dv = vec2(0.0, u_viewTexel.y); + + vec3 colorUL = texture2D(s_color, texCoord - du - dv).xyz; + vec3 colorUp = texture2D(s_color, texCoord - dv).xyz; + vec3 colorUR = texture2D(s_color, texCoord + du - dv).xyz; + vec3 colorRi = texture2D(s_color, texCoord + du ).xyz; + vec3 colorLe = texture2D(s_color, texCoord - du ).xyz; + vec3 colorDL = texture2D(s_color, texCoord - du + dv).xyz; + vec3 colorDo = texture2D(s_color, texCoord + dv).xyz; + vec3 colorDR = texture2D(s_color, texCoord + du + dv).xyz; + + // in an ideal world, lighting and such is in linear space, + // would possibly want to convert to gamma and apply txaa + // there. but this sample isn't storing intermediate results + // in linear space (or doing any reasonable lighting) so + // would possibly want to do the opposite. +#if APPLY_TXAA_IN_LINEAR + colorCurr = toLinear(colorCurr); + colorPrev = toLinear(colorPrev); + colorUL = toLinear(colorUL); + colorUp = toLinear(colorUp); + colorUR = toLinear(colorUR); + colorLe = toLinear(colorLe); + colorRi = toLinear(colorRi); + colorDL = toLinear(colorDL); + colorDo = toLinear(colorDo); + colorDR = toLinear(colorDR); +#endif + + // Compute variance box on color neighborhood, clip to box + float outVal = 0.0; + { + vec3 m1 = vec3_splat(0.0); + vec3 m2 = vec3_splat(0.0); + m1 += colorUL; m2 += colorUL*colorUL; + m1 += colorUp; m2 += colorUp*colorUp; + m1 += colorUR; m2 += colorUR*colorUR; + m1 += colorLe; m2 += colorLe*colorLe; + m1 += colorCurr; m2 += colorCurr*colorCurr; + m1 += colorRi; m2 += colorRi*colorRi; + m1 += colorDL; m2 += colorDL*colorDL; + m1 += colorDo; m2 += colorDo*colorDo; + m1 += colorDR; m2 += colorDR*colorDR; + m1 *= (1.0/9.0); + m2 *= (1.0/9.0); + + vec3 var = max(vec3_splat(0.0), m2 - m1*m1); + vec3 sigma = sqrt(var); + outVal = max(sigma.x, max(sigma.y, sigma.z)); + sigma *= 1.4; + vec3 colorMin = m1 - sigma; + vec3 colorMax = m1 + sigma; + + vec3 displacement = colorPrev - m1; + vec3 units = abs(displacement / sigma); + float maxUnit = max(max(units.x, units.y), max(units.z, 1.0)); + + colorPrev = m1 + displacement * (1.0/maxUnit); + } + + float lumaCurr = dot(colorCurr, vec3(0.3, 0.6, 0.1)); + float lumaPrev = dot(colorPrev, vec3(0.3, 0.6, 0.1)); + + // adjust feedback/blend amount depending on color difference + float r = abs(lumaCurr-lumaPrev) / max(max(lumaCurr, lumaPrev), 0.2); + r = 1.0-r; + r = r*r; + float feedback = mix(u_feedbackMin, u_feedbackMax, r); + + vec3 colorOut = mix(colorCurr, colorPrev, feedback); + + // optionally blur current color, since we've already taken + // the samples to build the variance window. could use more + // blur when feedback is lower, to replace temporal accumulation + // with spatial accumulation. or could use filter to sharpen. + if (u_applyMitchellFilter > 0.0) + { + // adjust filter coefficients depending on color difference + float b = mix(3.0/2.0, 1.0/3.0, r); + float c = mix(-1.0/4.0, 1.0/3.0, r); + + float m0 = Mitchell(b, c, 0.0); + float m1 = Mitchell(b, c, 1.0); + float m2 = Mitchell(b, c, sqrt(2.0)); + + vec3 colorFilter = m0 * colorCurr; + colorFilter += m1 * colorLe; + colorFilter += m1 * colorRi; + colorFilter += m1 * colorUp; + colorFilter += m1 * colorDo; + colorFilter += m2 * colorUL; + colorFilter += m2 * colorUR; + colorFilter += m2 * colorDL; + colorFilter += m2 * colorDR; + colorFilter *= 1.0/(m0 + 4.0*m1 + 4.0*m2); + + colorOut = mix(colorFilter, colorPrev, feedback); + } + + // in an ideal world, lighting and such is in linear space, + // would possibly want to convert to gamma and apply txaa + // there. but this sample isn't storing intermediate results + // in linear space (or doing any reasonable lighting) so + // would possibly want to do the opposite. +#if APPLY_TXAA_IN_LINEAR + colorCurr = toGamma(colorOut); +#else + colorCurr = colorOut; +#endif + + +#if DEBUG_HALF_SCREEN + } +#endif + + gl_FragColor = vec4(colorCurr, 1.0); +} diff --git a/examples/xx-denoise/makefile b/examples/xx-denoise/makefile new file mode 100644 index 000000000..ef9203197 --- /dev/null +++ b/examples/xx-denoise/makefile @@ -0,0 +1,10 @@ +# +# Copyright 2011-2019 Branimir Karadzic. All rights reserved. +# License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +# + +BGFX_DIR=../.. +RUNTIME_DIR=$(BGFX_DIR)/examples/runtime +BUILD_DIR=../../.build + +include $(BGFX_DIR)/scripts/shader.mk diff --git a/examples/xx-denoise/normal_encoding.sh b/examples/xx-denoise/normal_encoding.sh new file mode 100644 index 000000000..98ff30f8b --- /dev/null +++ b/examples/xx-denoise/normal_encoding.sh @@ -0,0 +1,92 @@ +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#ifndef NORMAL_ENCODING_SH +#define NORMAL_ENCODING_SH + +#define NE_USE_OCTAHEDRAL_REPRESENTATION 0 + +// From "A Survey of Efficient Representations for Independent Unit Vectors" +// http://jcgt.org/published/0003/02/01/paper.pdf + +// Convert an oct24 (2x12bit normal) to an rgb8 value for storing in texture +vec3 snorm12x2_to_unorm8x3 (vec2 f) { + + f = clamp(f, -1.0, 1.0);//min(max(f, vec2(-1.0)), vec2(1.0)); + vec2 u = floor(f * 2047.0 + 2047.5); + float t = floor(u.y / 256.0); + + // "This code assumes that rounding will occur during storage." + // -- Not certain but this appears to mainly apply to the x channel. + // From paper: x = u.x / 16.0 - 0.5 + // Instead round by +0.5 and floor. + return vec3(floor(u.x / 16.0), fract(u.x / 16.0) * 256.0 + t, u.y - t * 256.0) / 255.0; +} + +// Unpack oct24 (2x12bit normal) from an rgb8 value stored in texture (normal spec) +vec2 unorm8x3_to_snorm12x2 (vec3 u) { + + u *= 255.0; + u.y *= (1.0 / 16.0); + vec2 s = vec2(u.x * 16.0 + floor(u.y), fract(u.y) * (16.0 * 256.0) + u.z); + + s = s * (1.0 / 2047.0) - 1.0; + return min(max(s, -1.0), 1.0); +} + +// Built in sign test could return 0, don't want that +vec2 signNotZero (vec2 v) { + return vec2((v.x >= 0.0) ? 1.0 : -1.0, (v.y >= 0.0) ? 1.0 : -1.0); +} + +// Assume normalized input. Output is (-1, 1) for each component +vec2 float32x3_to_oct(vec3 v) { + + // Project the sphere onto the octahedron, and then onto the xy plane + vec2 p = v.xy * (1.0 / (abs(v.x) + abs(v.y) + abs(v.z))); + + // Reflect the folds of the lower hemisphere over the diagonals + return (v.z <= 0.0) ? ((1.0 - abs(p.yx)) * signNotZero(p)) : p; +} + +// Get a float3 normal from an oct representation +vec3 oct_to_float32x3 (vec2 e) { + vec3 v = vec3(e.xy, 1.0 - abs(e.x) - abs(e.y)); + if (v.z < 0.0) { + v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy); + } + + return normalize(v); +} + +vec3 SignedNormalEncodeToOct (vec3 normal) { + + return snorm12x2_to_unorm8x3(float32x3_to_oct(normal)); +} + +vec3 SignedNormalDecodeFromOct (vec3 normal) { + + return oct_to_float32x3(unorm8x3_to_snorm12x2(normal)); +} + +vec3 NormalEncode (vec3 normal) +{ +#if NE_USE_OCTAHEDRAL_REPRESENTATION + return SignedNormalEncodeToOct(normal); +#else + return normal * 0.5 + 0.5; +#endif +} + +vec3 NormalDecode (vec3 normal) +{ +#if NE_USE_OCTAHEDRAL_REPRESENTATION + return SignedNormalDecodeFromOct(normal); +#else + return normal * 2.0 - 1.0; +#endif +} + +#endif // NORMAL_ENCODING_SH diff --git a/examples/xx-denoise/parameters.sh b/examples/xx-denoise/parameters.sh new file mode 100644 index 000000000..be248e744 --- /dev/null +++ b/examples/xx-denoise/parameters.sh @@ -0,0 +1,32 @@ +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#ifndef PARAMETERS_SH +#define PARAMETERS_SH + +uniform vec4 u_params[13]; + +#define u_jitterCurr (u_params[0].xy) +#define u_jitterPrev (u_params[0].zw) +#define u_feedbackMin (u_params[1].x) +#define u_feedbackMax (u_params[1].y) +#define u_applyMitchellFilter (u_params[2].y) + +#define u_worldToViewPrev0 (u_params[3]) +#define u_worldToViewPrev1 (u_params[4]) +#define u_worldToViewPrev2 (u_params[5]) +#define u_worldToViewPrev3 (u_params[6]) +#define u_viewToProjPrev0 (u_params[7]) +#define u_viewToProjPrev1 (u_params[8]) +#define u_viewToProjPrev2 (u_params[9]) +#define u_viewToProjPrev3 (u_params[10]) + +#define u_frameIdx (u_params[11].x) +#define u_noiseType (u_params[11].y) // 0=none, 1=dither, 2=random +#define u_texCoordStep (u_params[12].x) +#define u_sigmaDepth (u_params[12].y) +#define u_sigmaNormal (u_params[12].z) + +#endif // PARAMETERS_SH diff --git a/examples/xx-denoise/shared_functions.sh b/examples/xx-denoise/shared_functions.sh new file mode 100644 index 000000000..5fd817856 --- /dev/null +++ b/examples/xx-denoise/shared_functions.sh @@ -0,0 +1,25 @@ +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#ifndef SHARED_FUNCTIONS_SH +#define SHARED_FUNCTIONS_SH + +vec2 GetTexCoordPreviousNoJitter(vec2 texCoord, vec2 velocity) +{ + vec2 texCoordPrev = texCoord - velocity; + return texCoordPrev; +} + +vec2 GetTexCoordPrevious(vec2 texCoord, vec2 velocity) +{ + vec2 texCoordPrev = texCoord - velocity; + + vec2 jitterDelta = (u_jitterCurr-u_jitterPrev); + texCoordPrev += jitterDelta * u_viewTexel.xy; + + return texCoordPrev; +} + +#endif // SHARED_FUNCTIONS_SH diff --git a/examples/xx-denoise/varying.def.sc b/examples/xx-denoise/varying.def.sc new file mode 100644 index 000000000..4c827e4af --- /dev/null +++ b/examples/xx-denoise/varying.def.sc @@ -0,0 +1,9 @@ +vec4 a_position : POSITION; +vec2 a_texcoord0 : TEXCOORD0; +vec3 a_normal : NORMAL; + +vec2 v_texcoord0 : TEXCOORD0; +vec4 v_texcoord1 : TEXCOORD1; +vec4 v_texcoord2 : TEXCOORD2; +vec4 v_texcoord3 : TEXCOORD3; +vec3 v_normal : NORMAL = vec3(0.0, 0.0, 1.0); diff --git a/examples/xx-denoise/vs_denoise_gbuffer.sc b/examples/xx-denoise/vs_denoise_gbuffer.sc new file mode 100644 index 000000000..f7bd25427 --- /dev/null +++ b/examples/xx-denoise/vs_denoise_gbuffer.sc @@ -0,0 +1,54 @@ +$input a_position, a_normal, a_texcoord0 +$output v_normal, v_texcoord0, v_texcoord1, v_texcoord2, v_texcoord3 + +/* +* Copyright 2021 elven cache. All rights reserved. +* License: https://github.com/bkaradzic/bgfx#license-bsd-2-clause +*/ + +#include "../common/common.sh" +#include "parameters.sh" + +void main() +{ + // Calculate vertex position + vec3 pos = a_position.xyz; + gl_Position = mul(u_modelViewProj, vec4(pos, 1.0)); + + // Calculate previous frame's position + mat4 worldToViewPrev = mat4( + u_worldToViewPrev0, + u_worldToViewPrev1, + u_worldToViewPrev2, + u_worldToViewPrev3 + ); + mat4 viewToProjPrev = mat4( + u_viewToProjPrev0, + u_viewToProjPrev1, + u_viewToProjPrev2, + u_viewToProjPrev3 + ); + + vec3 wsPos = mul(u_model[0], vec4(pos, 1.0)).xyz; + vec3 vspPos = instMul(worldToViewPrev, vec4(wsPos, 1.0)).xyz; + vec4 pspPos = instMul(viewToProjPrev, vec4(vspPos, 1.0)); + + // Calculate normal, unpack + vec3 osNormal = a_normal.xyz * 2.0 - 1.0; + + // Transform normal into world space + vec3 wsNormal = mul(u_model[0], vec4(osNormal, 0.0)).xyz; + + v_normal.xyz = normalize(wsNormal); + v_texcoord0 = a_texcoord0; + + // Store previous frame projection space position in extra texCoord attribute + v_texcoord1 = pspPos; + + // Store world space view vector in extra texCoord attribute + vec3 wsCamPos = mul(u_invView, vec4(0.0, 0.0, 0.0, 1.0)).xyz; + vec3 view = normalize(wsCamPos - wsPos); + + v_texcoord2 = vec4(wsPos, 1.0); + v_texcoord3 = vec4(wsCamPos, 1.0); +} diff --git a/examples/xx-denoise/vs_denoise_screenquad.sc b/examples/xx-denoise/vs_denoise_screenquad.sc new file mode 100644 index 000000000..5b137dc5c --- /dev/null +++ b/examples/xx-denoise/vs_denoise_screenquad.sc @@ -0,0 +1,10 @@ +$input a_position, a_texcoord0 +$output v_texcoord0 + +#include "../common/common.sh" + +void main() +{ + gl_Position = mul(u_modelViewProj, vec4(a_position.xyz, 1.0)); + v_texcoord0 = a_texcoord0; +} From 4760628bb137736507f4a805ec44735ac42bd84d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Branimir=20Karad=C5=BEi=C4=87?= Date: Sat, 2 Jan 2021 10:46:43 -0800 Subject: [PATCH 2/2] 43-denoise: Added shaders. --- examples/{xx-denoise => 43-denoise}/denoise.cpp | 14 +++++++------- .../fs_denoise_apply_lighting.sc | 0 .../fs_denoise_copy.sc | 0 .../fs_denoise_deferred_combine.sc | 0 .../fs_denoise_gbuffer.sc | 0 .../fs_denoise_spatial_3x3.sc | 0 .../fs_denoise_spatial_5x5.sc | 0 .../fs_denoise_spatial_implementation.sh | 0 .../fs_denoise_temporal.sc | 0 .../fs_denoise_txaa.sc | 0 examples/{xx-denoise => 43-denoise}/makefile | 0 .../normal_encoding.sh | 0 .../{xx-denoise => 43-denoise}/parameters.sh | 0 .../shared_functions.sh | 0 .../{xx-denoise => 43-denoise}/varying.def.sc | 0 .../vs_denoise_gbuffer.sc | 0 .../vs_denoise_screenquad.sc | 0 .../shaders/dx11/fs_denoise_apply_lighting.bin | Bin 0 -> 688 bytes .../runtime/shaders/dx11/fs_denoise_copy.bin | Bin 0 -> 358 bytes .../dx11/fs_denoise_deferred_combine.bin | Bin 0 -> 1409 bytes .../runtime/shaders/dx11/fs_denoise_gbuffer.bin | Bin 0 -> 2158 bytes .../shaders/dx11/fs_denoise_spatial_3x3.bin | Bin 0 -> 2193 bytes .../shaders/dx11/fs_denoise_spatial_5x5.bin | Bin 0 -> 2241 bytes .../shaders/dx11/fs_denoise_temporal.bin | Bin 0 -> 2755 bytes .../runtime/shaders/dx11/fs_denoise_txaa.bin | Bin 0 -> 5225 bytes .../runtime/shaders/dx11/vs_denoise_gbuffer.bin | Bin 0 -> 1539 bytes .../shaders/dx11/vs_denoise_screenquad.bin | Bin 0 -> 530 bytes .../shaders/glsl/fs_denoise_apply_lighting.bin | Bin 0 -> 402 bytes .../runtime/shaders/glsl/fs_denoise_copy.bin | Bin 0 -> 157 bytes .../glsl/fs_denoise_deferred_combine.bin | Bin 0 -> 1548 bytes .../runtime/shaders/glsl/fs_denoise_gbuffer.bin | Bin 0 -> 2191 bytes .../shaders/glsl/fs_denoise_spatial_3x3.bin | Bin 0 -> 2068 bytes .../shaders/glsl/fs_denoise_spatial_5x5.bin | Bin 0 -> 2136 bytes .../shaders/glsl/fs_denoise_temporal.bin | Bin 0 -> 3723 bytes .../runtime/shaders/glsl/fs_denoise_txaa.bin | Bin 0 -> 6595 bytes .../runtime/shaders/glsl/vs_denoise_gbuffer.bin | Bin 0 -> 1540 bytes .../shaders/glsl/vs_denoise_screenquad.bin | Bin 0 -> 317 bytes .../shaders/metal/fs_denoise_apply_lighting.bin | Bin 0 -> 862 bytes .../runtime/shaders/metal/fs_denoise_copy.bin | Bin 0 -> 566 bytes .../metal/fs_denoise_deferred_combine.bin | Bin 0 -> 1694 bytes .../shaders/metal/fs_denoise_gbuffer.bin | Bin 0 -> 2299 bytes .../shaders/metal/fs_denoise_spatial_3x3.bin | Bin 0 -> 3630 bytes .../shaders/metal/fs_denoise_spatial_5x5.bin | Bin 0 -> 3676 bytes .../shaders/metal/fs_denoise_temporal.bin | Bin 0 -> 3812 bytes .../runtime/shaders/metal/fs_denoise_txaa.bin | Bin 0 -> 8341 bytes .../shaders/metal/vs_denoise_gbuffer.bin | Bin 0 -> 1720 bytes .../shaders/metal/vs_denoise_screenquad.bin | Bin 0 -> 699 bytes .../shaders/spirv/fs_denoise_apply_lighting.bin | Bin 0 -> 1533 bytes .../runtime/shaders/spirv/fs_denoise_copy.bin | Bin 0 -> 864 bytes .../spirv/fs_denoise_deferred_combine.bin | Bin 0 -> 2986 bytes .../shaders/spirv/fs_denoise_gbuffer.bin | Bin 0 -> 3900 bytes .../shaders/spirv/fs_denoise_spatial_3x3.bin | Bin 0 -> 4308 bytes .../shaders/spirv/fs_denoise_spatial_5x5.bin | Bin 0 -> 4420 bytes .../shaders/spirv/fs_denoise_temporal.bin | Bin 0 -> 4738 bytes .../runtime/shaders/spirv/fs_denoise_txaa.bin | Bin 0 -> 11172 bytes .../shaders/spirv/vs_denoise_gbuffer.bin | Bin 0 -> 3399 bytes .../shaders/spirv/vs_denoise_screenquad.bin | Bin 0 -> 1078 bytes scripts/genie.lua | 1 + 58 files changed, 8 insertions(+), 7 deletions(-) rename examples/{xx-denoise => 43-denoise}/denoise.cpp (99%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_apply_lighting.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_copy.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_deferred_combine.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_gbuffer.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_spatial_3x3.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_spatial_5x5.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_spatial_implementation.sh (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_temporal.sc (100%) rename examples/{xx-denoise => 43-denoise}/fs_denoise_txaa.sc (100%) rename examples/{xx-denoise => 43-denoise}/makefile (100%) rename examples/{xx-denoise => 43-denoise}/normal_encoding.sh (100%) rename examples/{xx-denoise => 43-denoise}/parameters.sh (100%) rename examples/{xx-denoise => 43-denoise}/shared_functions.sh (100%) rename examples/{xx-denoise => 43-denoise}/varying.def.sc (100%) rename examples/{xx-denoise => 43-denoise}/vs_denoise_gbuffer.sc (100%) rename examples/{xx-denoise => 43-denoise}/vs_denoise_screenquad.sc (100%) create mode 100644 examples/runtime/shaders/dx11/fs_denoise_apply_lighting.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_copy.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_deferred_combine.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_spatial_3x3.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_spatial_5x5.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_temporal.bin create mode 100644 examples/runtime/shaders/dx11/fs_denoise_txaa.bin create mode 100644 examples/runtime/shaders/dx11/vs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/dx11/vs_denoise_screenquad.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_apply_lighting.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_copy.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_deferred_combine.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_spatial_3x3.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_spatial_5x5.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_temporal.bin create mode 100644 examples/runtime/shaders/glsl/fs_denoise_txaa.bin create mode 100644 examples/runtime/shaders/glsl/vs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/glsl/vs_denoise_screenquad.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_apply_lighting.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_copy.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_deferred_combine.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_spatial_3x3.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_spatial_5x5.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_temporal.bin create mode 100644 examples/runtime/shaders/metal/fs_denoise_txaa.bin create mode 100644 examples/runtime/shaders/metal/vs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/metal/vs_denoise_screenquad.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_apply_lighting.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_copy.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_deferred_combine.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_spatial_3x3.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_spatial_5x5.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_temporal.bin create mode 100644 examples/runtime/shaders/spirv/fs_denoise_txaa.bin create mode 100644 examples/runtime/shaders/spirv/vs_denoise_gbuffer.bin create mode 100644 examples/runtime/shaders/spirv/vs_denoise_screenquad.bin diff --git a/examples/xx-denoise/denoise.cpp b/examples/43-denoise/denoise.cpp similarity index 99% rename from examples/xx-denoise/denoise.cpp rename to examples/43-denoise/denoise.cpp index e7abb1ee7..8d0680a82 100644 --- a/examples/xx-denoise/denoise.cpp +++ b/examples/43-denoise/denoise.cpp @@ -286,7 +286,7 @@ public: // Create program from shaders. m_gbufferProgram = loadProgram("vs_denoise_gbuffer", "fs_denoise_gbuffer"); // Fill gbuffer m_combineProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_deferred_combine"); // Compute lighting from gbuffer - m_copyProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_copy"); + m_copyProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_copy"); m_denoiseTemporalProgram = loadProgram("vs_denoise_screenquad", "fs_denoise_temporal"); m_denoiseSpatialProgram3x3 = loadProgram("vs_denoise_screenquad", "fs_denoise_spatial_3x3"); m_denoiseSpatialProgram5x5 = loadProgram("vs_denoise_screenquad", "fs_denoise_spatial_5x5"); @@ -319,7 +319,7 @@ public: m_recreateFrameBuffers = false; createFramebuffers(); - + // Vertex decl PosTexCoord0Vertex::init(); @@ -519,7 +519,7 @@ public: lastTex = m_temporaryColor.m_texture; } - + // denoise spatial passes if (0 < m_denoisePasses) { @@ -532,7 +532,7 @@ public: m_temporaryColor.m_buffer, m_currentColor.m_buffer }; - + uint32_t denoisePasses = bx::min(DENOISE_MAX_PASSES, m_denoisePasses); for (uint32_t i = 0; i < denoisePasses; ++i) { @@ -638,7 +638,7 @@ public: bgfx::submit(view, m_txaaProgram); ++view; } - + // Copy txaa result to previous { bgfx::setViewName(view, "copy2previous"); @@ -715,7 +715,7 @@ public: // update previous status m_havePrevious = true; - } + } // Copy matrices for next time mat4Set(m_worldToViewPrev, m_view); @@ -1072,4 +1072,4 @@ public: } // namespace -ENTRY_IMPLEMENT_MAIN(ExampleDenoise, "xx-denoise", "Denoise."); +ENTRY_IMPLEMENT_MAIN(ExampleDenoise, "43-denoise", "Denoise."); diff --git a/examples/xx-denoise/fs_denoise_apply_lighting.sc b/examples/43-denoise/fs_denoise_apply_lighting.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_apply_lighting.sc rename to examples/43-denoise/fs_denoise_apply_lighting.sc diff --git a/examples/xx-denoise/fs_denoise_copy.sc b/examples/43-denoise/fs_denoise_copy.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_copy.sc rename to examples/43-denoise/fs_denoise_copy.sc diff --git a/examples/xx-denoise/fs_denoise_deferred_combine.sc b/examples/43-denoise/fs_denoise_deferred_combine.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_deferred_combine.sc rename to examples/43-denoise/fs_denoise_deferred_combine.sc diff --git a/examples/xx-denoise/fs_denoise_gbuffer.sc b/examples/43-denoise/fs_denoise_gbuffer.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_gbuffer.sc rename to examples/43-denoise/fs_denoise_gbuffer.sc diff --git a/examples/xx-denoise/fs_denoise_spatial_3x3.sc b/examples/43-denoise/fs_denoise_spatial_3x3.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_spatial_3x3.sc rename to examples/43-denoise/fs_denoise_spatial_3x3.sc diff --git a/examples/xx-denoise/fs_denoise_spatial_5x5.sc b/examples/43-denoise/fs_denoise_spatial_5x5.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_spatial_5x5.sc rename to examples/43-denoise/fs_denoise_spatial_5x5.sc diff --git a/examples/xx-denoise/fs_denoise_spatial_implementation.sh b/examples/43-denoise/fs_denoise_spatial_implementation.sh similarity index 100% rename from examples/xx-denoise/fs_denoise_spatial_implementation.sh rename to examples/43-denoise/fs_denoise_spatial_implementation.sh diff --git a/examples/xx-denoise/fs_denoise_temporal.sc b/examples/43-denoise/fs_denoise_temporal.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_temporal.sc rename to examples/43-denoise/fs_denoise_temporal.sc diff --git a/examples/xx-denoise/fs_denoise_txaa.sc b/examples/43-denoise/fs_denoise_txaa.sc similarity index 100% rename from examples/xx-denoise/fs_denoise_txaa.sc rename to examples/43-denoise/fs_denoise_txaa.sc diff --git a/examples/xx-denoise/makefile b/examples/43-denoise/makefile similarity index 100% rename from examples/xx-denoise/makefile rename to examples/43-denoise/makefile diff --git a/examples/xx-denoise/normal_encoding.sh b/examples/43-denoise/normal_encoding.sh similarity index 100% rename from examples/xx-denoise/normal_encoding.sh rename to examples/43-denoise/normal_encoding.sh diff --git a/examples/xx-denoise/parameters.sh b/examples/43-denoise/parameters.sh similarity index 100% rename from examples/xx-denoise/parameters.sh rename to examples/43-denoise/parameters.sh diff --git a/examples/xx-denoise/shared_functions.sh b/examples/43-denoise/shared_functions.sh similarity index 100% rename from examples/xx-denoise/shared_functions.sh rename to examples/43-denoise/shared_functions.sh diff --git a/examples/xx-denoise/varying.def.sc b/examples/43-denoise/varying.def.sc similarity index 100% rename from examples/xx-denoise/varying.def.sc rename to examples/43-denoise/varying.def.sc diff --git a/examples/xx-denoise/vs_denoise_gbuffer.sc b/examples/43-denoise/vs_denoise_gbuffer.sc similarity index 100% rename from examples/xx-denoise/vs_denoise_gbuffer.sc rename to examples/43-denoise/vs_denoise_gbuffer.sc diff --git a/examples/xx-denoise/vs_denoise_screenquad.sc b/examples/43-denoise/vs_denoise_screenquad.sc similarity index 100% rename from examples/xx-denoise/vs_denoise_screenquad.sc rename to examples/43-denoise/vs_denoise_screenquad.sc diff --git a/examples/runtime/shaders/dx11/fs_denoise_apply_lighting.bin b/examples/runtime/shaders/dx11/fs_denoise_apply_lighting.bin new file mode 100644 index 0000000000000000000000000000000000000000..9b13db3bd1ecb2fa219cac1e790d8b6046a6e82b GIT binary patch literal 688 zcmbVJJx{_=6g@2}XiV&4qJxIj#0YUP4u~kUPzOqAF^vlfqQ+oqU^n=&`3Ia`9Qp^0 zqqF`GXJ-e`eWglROuXsod+*$jbMJeWTbT})^EuEaknA_xK{x1SHGm&d{pL}()j1Bb znx=?91AHAQ`Mbr+<7s>AYsr0DyESur7b{XtdV`zx>7F#O#Q^@#^9Qq81HIkgjbwi0N&qb3o(RT1SZGh#`Q&KRA2Jq pxv7&J&$smd^_F@0$j1-~)!XoHq^8&g`@I=*T0xk>c0PJad;@aHRVM%d literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/fs_denoise_copy.bin b/examples/runtime/shaders/dx11/fs_denoise_copy.bin new file mode 100644 index 0000000000000000000000000000000000000000..40c1b9428145680263ca26b892a0aa2d49ee0cc0 GIT binary patch literal 358 zcmZ<@_TbEyv$J6U0wxCb;`rqJoctmKMg|5T!r+6%T_T*EA3T5l`kc^%ZL+#L!r|

M4nW01RR2vAe7higOu zP!Y&IGC-Wg!N3>=az=sxSZjm?0}Dd|gdG~nz>p-sz-%NS0Jb9)sP2;jM9!6gq5BX6 ZLxXduC<9E)4XVzq4x$ETrX9qI3;@(cFW3M8 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/fs_denoise_deferred_combine.bin b/examples/runtime/shaders/dx11/fs_denoise_deferred_combine.bin new file mode 100644 index 0000000000000000000000000000000000000000..1b2c4923773d648f6d65ba654cc36e1ac246b229 GIT binary patch literal 1409 zcmZ`&OKVe65dL!WFiomS3xZaNm%6eDY8JJXlH9you)U>ukm@2PD2P}aT6`@EL3E|K zaN$Zp&|Ut3xKQaY=&It@AK=1Y;5X+HE@>SyXHI6mne)w@bF1~`iS0~o36wD6k2?MC zPIqf}`Xs;~;=7%P+dEs`2UkKNgpPA8M1gXH<|n@PZXdaZ}b?wTfTGxv950>wbNuvEr|xQE8Waf1`}yaOktH zoTEt6x>PHpx^ytVQP`+e8ceD$SK3b_fbhvM@qQekO(0Tz9oSixxF;P@?Z9MnfKS#v3qP+d|3`P3+;;yTzY^c)$zFCLkT!f)lxNe zQ$S+c!jASH}~Ck~~*Y6nM79wPG44^9lF{#3*q*ZMn)$6fsl#+%a`7 zvWKGKFlVgrlRL;THfww{V;FLe+q^{JeoSvU;cVl!E;%Oyz7c1W;|1#o*8o=9fE(ar zYg%69dxWcYN3ytK5nsMu;t32|)yx4o)4cqpF@)U>f+P3zNh`{>y-q~?yR Z`;3{|t*HJ#&zjFoOy>>qIrSV5@DG2Rbt3=( literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/fs_denoise_gbuffer.bin b/examples/runtime/shaders/dx11/fs_denoise_gbuffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..de874793df51b8fb10753042e0ebe6a5a742a6c4 GIT binary patch literal 2158 zcmah~OKVe65T4vT+lq-n!MG|ex)LlwL<%-HHc4BoO=;2)MW`)FQK?oUK9C}@D?t$i zDe78u5pf~ixDvWfMeqkGt^{`?uKm7q&NP>(IB@RFnddii&b_x(nHu=;_;W)*Q5m|i zc=J;8R<(J%xtfM1gq66yxU{<5Y^;r9=M-1hT341?i7yXdeSh-l zUFOaGtw>{i=O@$A76v=xu07R>S#i$s6=l9CW!SkXMGr#&7Q>GbJ6 z?89)N-qhsYt>lo^(}zTT1S{$Ca8DwSVkNOEwZ*w|Wx6_Do|W11{F(fWREzbAa(TWW zWne$={E2-IOG$Gv2lYWLlvN+ZLfO@<=I1AiRq1ppQ^opg42L;72)vw-&{@DPJsO6_ z<~>Kq7gA=gGHcWiqP=TYzp&8T)5d*cSR&b!wNtm8l5h)qm1!d;>fb_UPwK1=4 zOaVFyecHtylAitqu7~Oeot<@ex_kT#u=LxqA_M(8`hVkLoINlHYy3*O`M5Z>g<-=y zOr4Pd>x*6ncWXziZWEkXI6LBITrfv1>hil>w0(6rUJhDY4EdSEoCR!@M=~}a?*r$c zbrqA=pq&`li`s%XiQVLLJt$7bah6%sqpbIX=fzsV{$uKc9;l~}_Mq6=C-c^{c+-74ohR+YX*hai9koH+#ic&McZ4%A z_yX?;^Y9%ZN59EkALr}&Fo^KUWytf%+VAb6{BI1b7n~#KM;+(KnmR{(w|a9_oQ}gS z5txU$e~b40(UFCg;YyBz?#A3>eSv4eO|l^`1Y=`)fi&AW+wq*8m>a3kZpns+-kO@VB_YOl#<`W5w4X0 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/fs_denoise_spatial_3x3.bin b/examples/runtime/shaders/dx11/fs_denoise_spatial_3x3.bin new file mode 100644 index 0000000000000000000000000000000000000000..99927b4750a0394c22ae8c19d0bb7998feb968a0 GIT binary patch literal 2193 zcmb7FOKTHR6h4`>d6l+9prQrq=tii5p%fJ@ZTiTxanY0}l_)~28fc|8C9&#CLUAX! z)Bm7x<*IHh3W5vW`2z(1K@rz}-^@M9bx_2CGxywczVp2I%=qP{bYpCKO7Ns)V7K;Q zv;MGBe^lSfLK35Nx3;t1T;FbG2VEJ2Ala(jZfrH0IcNhwt9Gx^++N?x#bSW~fIIb_ z`y08Kqx=6e_>mM@sLsv5IsWczXK?Dur_bNtJ|DR8lF_J(V?7Ic0@?=^%Ztk^SRGIb zbP>ddcw+esgBAiVd{G|g!IZDoR!Ze!rC3^)N})PmDy=R^r_(9HFXIGNV)a1!*nVMO znO$8hRA5wIDpV(tBlGzJ!@DVoT@&f^pLv1l5Z>3B)=f9qE6*6pvySRf>64uhv$odT z6KC&<<^Gl{@d?)ra;?Mvp%>b2pe<=&Hjx9CpdVxkB2PbvwCC4GVbnX%>ct9Zo9bbB-u_QuHr0ADsSr#X|;sFjD! zPhx-+Gj6bq^ZMiI3y9y(IB|IPO&rSD3p*dmvXJo{P41EddlJ4+8F|Li(_dWr;-g09 z{RPKfoPlrVN8joLY>+c$%()l0eSq^$GiJY~V;tr?YVMQPm4J+OF(1k_&LxA_T6_Mb zTNkm6L9FVvF6Km?XY!2VSQjX$fjaGj+MPgel#k}XIxx<$5s>!A_D{9urarg4ef;af=@a*;W7y{F1gyprRG(DuPf2LlG6VHT{uodud9Ow$V$hHqc6ILSogEgyKaC zUIf8^!TJaEW)-~nA9xeId+4EOzi;`LLtyZ?rl|Cqvt?J|YX1$q%H2}1#PwUOC)y-Ti7AOE% zt2Lgj=VFeo|Igu4N@Th+IpzKOJ^Ot{8oTe#eR#8bw9IU@#W7#Sc#W}-F;|+IU%>2O zz$~{g_z+Jlp8<^NKnh>f2X+XhezQ?a$qDoYPJu&-<1e>@V}MX6$i#6d1~W z+rvA25=Q1py&;mw<~(D;KISR5$80@08~wPgXWUSKa7@pAKMBvC8vPi!Wja0W4}0n4 z0T6fG9;{)~)G%c6w&m-%aEci*Smyck?eb0V?PVTv@=YG<*t;%&m;8IP&~-VA$mqPk;F#kIVv`?ZYYd11XX?n=%6owGk!DWjP#g0w){r@WT2}%( z*2Vg$(>%8gUTd}Dcj_XRIfzxe*2TP(J;O7K3wmeSv=8cc9=%aH%7I*Q{bPd|+8aAQ z&B{$3mux$~SEp*7-ksf{$Id_QzY)@b& z4}60=cIv+vJ$ueL;FnZ?_Mbl7qdOn>?~z;7X5$%yc=f~i=`-^I;zP($4Q2_9?0VG5roC3>F_!g!n0c00-c? ADF6Tf literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/fs_denoise_temporal.bin b/examples/runtime/shaders/dx11/fs_denoise_temporal.bin new file mode 100644 index 0000000000000000000000000000000000000000..528198e68cf4aec47584c0d405f24a640e865f6e GIT binary patch literal 2755 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 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/dx11/vs_denoise_screenquad.bin b/examples/runtime/shaders/dx11/vs_denoise_screenquad.bin new file mode 100644 index 0000000000000000000000000000000000000000..6c9e1ce72ee58fed1164690c36fc15f12549329c GIT binary patch literal 530 zcmWFw_TXdyf_ynU8%75H()ir`l+>KC%+&ILqWmltpbQ8lZS}|g&EkO_POyv^k7p53X5e@ nP&1+GfI=KFH^KD6%msyq83O|=%#ChPf7pS79_U&DUe?53=O-Cizl8!-Rau+^sA+6)7*A+$H{SkyW6HjSazojs(8E*mF&R~KLC zDrimBl-9+caM63*I!hiyMDRfmsPI{6M5NfrD8B7#eU15jgH=zyg zL6~hT1O@7L&XT*h*hkkfrSgOkkOcg*Zb;`~_bf?}P2WVU;t0YgB`L|!8msC1=|=FV Xl+s7fOTBa^U-(s1^e|n*e>rD=&Qo)j literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/glsl/fs_denoise_copy.bin b/examples/runtime/shaders/glsl/fs_denoise_copy.bin new file mode 100644 index 0000000000000000000000000000000000000000..664d50ec08e53d34beb4914710f3d36ad3a8cc13 GIT binary patch literal 157 zcmZ<@_TbEyv$J6U0!9Y*;`rqJoctmn6NDJ5fVeENs4_DzU7;*B*+`)*z9h9GIX}NB z#lV`YG%qtPzbIFsI5D>%C$-4PMFFbanyV~7GesdcF*8p=LzAnTOFbpDH67C3K3kQ+PqNVS!Kv6TX_szu zVtnv-K(x+^w4y_v$T{BBFFlYHh(NSm6Jt_Dbl-^}pTwDSQg`j`YPSc`Bv5yV>>F^_ zWxA*sJ8W~NO+?G00_G-59?Nv|LunE9nQn+Fg)Ed4;g3j0vG6$!`%!DoM>-Kq1|put zvk^^aA_zmyHMqJk38&FaL=oo=7o*2gY7QI8<}7T^v{dT`y}kg35$Pm_kmNV1A$p}% zc36GnVzLL9k4Wf%y~{c_0$xrtN|9GKv6BYXv6_GtV{ouJCh8~nOk-k!@R1)#FUoiM zKhrJ2%JDHpQke`ms-i&qST(SW7s6ZytsSVvv?b|jiL92UCTVhK5AYYd+QvrP)z)U} z6v06=RKAdi-T`sP&S+J}TY{~X*HY=V!qnn-n*rHmAn` zZsLq2LqRu={^}8&DT2mxhOmna6Hcs|DXp7fLsZ9dg_&n|=rJ_y;@}v!7gb*5L){oF z?#*0%cb( zNUA1dQCqit@l4KrM3D>|o|wgi)z&F0Qj>7w|<FuB2eKE4R^+{xv{Ksm{-YuOV06=OU{7~xSJre0CN$SPg#~nPS4{Z55MXWcazgTc?M(& z;t@o(^BlwUu#0oCCWiIPzLsB>L?jK9J)!pG=Y+`8f*kVroMfltm-T%Bgp}1KI_GnE z2!KjsE7AkB7`W14ekUKvA-fP7J{~DFT%7V4_)tS=hPriZR#vqQCKwwPOw5S+X4^v$ z4}o|9qWL6sOwGJi3ZeROU8Pq{!zi!qX( zfl^HK_Dhto9HX=eu0dF#gcT%E+EkjesxHaY#CfbPD>?bj4+iXq@Udh#ELtl}?qC3~ z58w|06k-S9%kUQf)Os|WImZC2E_uO9o4jwi1Ch3|CAdaP=W4-BRdKZ{OypYCN>_c} zWnp24$nlzT7s@94bDSSzCEGMC300yszr|=iAKh0ShVlT@v?I7!TBzm zFd4k-pS!AiBR>*c2Z1Qls4vSE~l6eY6phpRx0}h&2=;lbuI}AOL?v*_lF*XE2XFwW;AGgv-pzL va?(;s00&6$ttU{}SNYnMHVF6f-?^3(`iBe#d8~TxJ>rG+%YntO$;A2xNj{;x literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/glsl/fs_denoise_spatial_3x3.bin b/examples/runtime/shaders/glsl/fs_denoise_spatial_3x3.bin new file mode 100644 index 0000000000000000000000000000000000000000..4a39f363f8669ab8c85512a7f62218f430253f82 GIT binary patch literal 2068 zcmah~+fLg+5Ot9%+Eo38YM#)VTxvT|S_OEaLVYh%A5gSfoF!Ow?8ukI2JuDx$6?5oh)Q{^v1IAW4+7yRwQDR9(}Yd z{8+~+_v0{5RFo1@9>vKCwXR`3)I9WN1Q62@4 zSgs;x(CXidXcqVGeA$FjvDt<)@xDVvYB;3%4abP;Vzi5c6>LNh0kcOhBY?*+78wZN z&o_A}GWkme>!*x+3ycxhCUm3Z;BytxRcn!_X&@q9lk&5BMcPDHJAU3;<@`%)rG(=b zY6F|hK{MxHs|^oOxf`V%|E9K@6?g}M|YjA3GyClZBX=C~&90((}J&u|Lf%&}n# zE`BL|5{F+PL=;f>0vGB$uYZ7`@oj2Byhy2`2JoKFs>v!v-X zmn)8Ii6s?N8)Mse4-FAN#2XjbD$anJy9p7y4%dN6U#o#z#-89DI}^Hbs;(2fEn0k@ z@eNLg29XT0#8`hAmE6ll*}uC)@-~FBFzeU9((={5BD5f@(*`^kb9r=>jt@_RU0<*JT zUt$*`EAh_d%-Ol+=I4(O(dVm613&MKj~USId~obJOq)Og6nzN zHVfQT{1X1=G3Cwzb;Pw+UCVUvnbat+jhiKj?}o}J;b(VqB(1gUJ)bvD89#5FWa9XR z6kw1UDCYcYDR2NYcP%r=ze!Pjf@M3cP`RRpF*HoGSYV=@L;6n2^`Xw1TYO$##Zrqf!Xt2MRG9m-_a zz*YvYsU4-BUA*Ex`?vd9JG?4-y4_)ya~pda3!@M6id}jbe+YeIx^JhmBuJ|OG=S{_ zAYc{r2t~Ip!c-=bB+eZ$KTM%0xGfX9P;ZwoS8t<{3D0N{G22gW*t9#j|*^yfR0h^Tq!POGxW(@~$9}D-liVyIjU&w8RnYo43{7)XDT!dzXJyvNyWIvck1O$HK%?)fLN4z;9xALr@4*n^*sWFN1;c7aO&9 A+5i9m literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/glsl/fs_denoise_temporal.bin b/examples/runtime/shaders/glsl/fs_denoise_temporal.bin new file mode 100644 index 0000000000000000000000000000000000000000..0ea5bf97013533f90fed9cbdb7c1bd79b4902b2f GIT binary patch literal 3723 zcmcInOK;Oa5H?&2K?r^U+N0fu5U=y9+HwI^oO%Exjw{P?gC!@fZ0FUg|Ck@a%&d34 zcDAM}YE`B2Ja^`s`PRMs@$Gf?;q$ozKSxe)EjCHK{T@f<%NLFVzs?&e`Wf%yRLPDC z5oKwXqXF8yD#R+wmtm>|y+Ul_G>ejQuSD+(ag)cJBwH6>OyBpl{HhV-&s)dYg!w*M zE!ZZGINOLa-bGoK&xdEd^(wgrbJUDjeQK+M&R%PZaCwu)IseM4GVCf@kzJ>Xvuhi5 zcALnO&w86InX_e>teETd?s|+}r&(CCFpAd8bsCoO{8yYTewV^O10@x0=%}HBS1Qh^ zNrf1Spe^#ns4e1R+!h65f+Bea3MR8-$ms(bU(6oRaB=#8CJ;VAN%aIw_FMRMg*0Qr zS9Oh8xx9gi5I&WqAJ_$h!6?^x43N4NrhRi2&}7W>}0T`JWVee5M?s6dF{!o zJ#lEM+@{bI^?i0|4O3`Z!#TR`VKz;BGx}s;ISo#%>CjBjaH0J}TYdI1J88qG5hjj; z*{Vw_1T3rF-`DVW`om`1{b_t4h*wXTEkDqJ-JkMz_!B=VlxX?&0|0UXlS^w~3->nR ztxdSCg@*(PIvo?Y(y&nUS-Z>G+)7&IbfCt#aw4KC9{0gu;1_Qs-|G-bq7j+8ihaOqr);ETs>*kJQdXf9&8V-sFJbBp0d-O8d mY3A~SzIlc!55A>J{-P^rk&4SaT#yowi|`*odcnW%^_;)?ta*L_ literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/glsl/fs_denoise_txaa.bin b/examples/runtime/shaders/glsl/fs_denoise_txaa.bin new file mode 100644 index 0000000000000000000000000000000000000000..5be4fec280060d2daf5a83b7be6302498e825f37 GIT binary patch literal 6595 zcmb_hS#R4$5LWZhLq*Z&z7+dNDzYN(QWxw71yUG&Yk}r12nf}dT>+BiQiml4`R|>X zy=HltF)A3gxW_l&%+9s+^}3 zE`Jk-ZuL~ayn2aMqI#C}x$GV`%UiLYFCRn{cRzInl2(H3^v8aeC6_RCx7y5iBF`i~ z(EM6v%0DDo>KRr}nIvBO+1AKu^G zW)m|YfgTt-o4RKMI%5K6gA<4t4&XSP(AnS!W9&c%$09_+*C)dY5Ev|?oJuHUHp41O zKxh;(4-psE471pOPljG&yOk{N9tKC$$vy@$hj2Clgn60xo+fcSToAmxSE4v>>oTW9X)pAnHqSnsAx$|&vMz8Wiig?Rs zTFYnp<$SnB8Y%D`lR!ejh^z>O*_TrYOyW2|D7fzxCXqZ}Nk~oAd|;-|QNkm#sg~s# z%0bIJNMY9U3c#*hC_w6oWAO>ntPmC3DR+EErwuSLHm}_JW(W%RG6V)!TL_BRG6cp? zTL=ovG6Xt*TL_A$G6ec-TL=n)G6ectTL^OD5(K$OO9<_C1Ob^#T33*~B*~xW$k%gkdroeUi&IMp4a8H4lmH21NsbE zXKzT33Ok8LOufQmnjK}#9$dl_{n3-$ zF^iMD4_3UID7r)iDLl9VSEO>D?(J2$?>mm;3Hss_c2^O#E|Gm*@L~87CZI3o1Lvn0 zSM`FT1KVQ+Im2QM8qzONftNBRA#&$`Z(Sb+I=wN%xBuq5Xgxo|oeZwu5tO?=-d*We z47yPiU%+3C|DF~GvNfe-kivKDu-N4rvxEs`n^!=YDRcVUEw z6NoO%pZfv;NHWMIq5AEBh2UjZVOG1{>|FMth=NPL)={B)WBg@mLWsmmtir5ENKD0g zAb3@Q`U6uqnFwvJkJyp)w;q+E6=OIF(v;Q(Is;3)jg}%`s0yUIk%NDF_ywr6$CXJn0p%#6jPWxeI~4e&RJ$Eoiq$)cw08*jcHv@Wf*l~q@dBnG0MbQa6js6@ zO7H7!^i}E?Qa%5FqmqOMhSV04Ud_3Kiw@L1v3fG zY-tU`8ork^VGYEWfnW(T_R(#wPNXjfVZG+dL0FIV zVi4W{wE(e|*u|Q6DLDNgoYRUh2^SZ$ rPvEjpTiJ_97{oRRUxmRxSlmAj?pN7$KEHp5gD`0W1K>V)yPf|4-mQvV literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/glsl/vs_denoise_gbuffer.bin b/examples/runtime/shaders/glsl/vs_denoise_gbuffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..4b88fa296f20b5fa93953fccdb35358139cfaa97 GIT binary patch literal 1540 zcmZ`(U5?W*5H_d?HWK0reoPTmaekUa74JN*gtRZ!Dhk`MGHQ}4n`D0whu{(%hbwRu z#*XdGlx$^Ht-qOXX1?*v)tAo~3_gE;{XMMM#Zbs*ypq-Z>IGwP*z2LF+e39+amL`V zcjmvcx$N5S*6z(voXW1O?_R!T3=aGKim|fqJNb3!D?U~?5ig6T?dtNl^9T?FaBA;l zFWcq}3IXb?hnu$T4#CbF%kEJ&w-ejw*wA&z$7dX3ga|@~k?>h0+Id4Gzd-@KF8i1} zrT)9udhMg|dKt0G%_xYY_$K}KA_B*;l?R9;8}6^YA0D4d?&}lOT0r`o+u!^Xl5g|y z_@qG1Xe^*4XB}?t!Cq`YOLPZLaBB$_bbBl=O-BIn&w9?^TjOx8thFA-9b|IM(LT5a zcUPGz41t1xF$F^cCKQYaxYZy|B1UoIvm{bX74K;h83kz)IR$AFBG4!nK0#R|3&JO= zTW7-SIWbahk^dI8K^5dWkgS(_o+8(DA#_SZ>qt2D4EbjZ{+aRjZP(|7(#3>wM3`&B zfIPz768d&Y0Asib*8HP-uKJnTfd?t3JtChf-@Z*EO0IJ-PHumIAK4}_CT>>QWw26;Q<5gFj>1LzxmKOE4{d6F#Fp%X@@XAfR`DY8ukE%6HkMcg$Mt9vfXLotqWorNc literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/fs_denoise_apply_lighting.bin b/examples/runtime/shaders/metal/fs_denoise_apply_lighting.bin new file mode 100644 index 0000000000000000000000000000000000000000..d007881c51072985ab47278019f28d52f8e7f0b2 GIT binary patch literal 862 zcmZuv$xg#C5KTF-_`vc5$P$+XRUu7xwT(ESic3%BR#lOkxFwQfSFrBJd0AgNzE2F}E}{WRLk!0;7Kr#>eKw0S}TIdHMahc_K- zxg3G7;~){OW4Q3m7dQ(TOF{*Za-5ii1+>CgOZ*tqe^lz!%sS18LisKhT%Zm1Bf4BR zq3ofQcf)Z`-Gf?6nWjTI>!XRqj%t}LIbRdOZ89 zmRrq&Lfdq0*U7EZ?+x1hR(D`oRn)ON-TdhdoMCI&>-1d}ZP|`hOqXD}BC3Xzmj?jD Hcr<@TPonOZ1))(i$#nkhEduNDBu2sWlb9`Z32Jv% z>?oJHYArbtz^H_x_8AU!)#Y1F>lbS264JES?k|HZTa(ZKAzaQGEBMGQ>k6fG=>V-^ z+83&j7ebX(xK$bV(i0e+!?v;1s}8{o#!vO} z9~^Bc^licTg!!##4!F626TaM;guCdMLz{^k4LJYjcZVoEni&!dAk9#_bUbvG5YGee E8;LW#cK`qY literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/fs_denoise_deferred_combine.bin b/examples/runtime/shaders/metal/fs_denoise_deferred_combine.bin new file mode 100644 index 0000000000000000000000000000000000000000..ec9a74449bf7c4502808d0f26d7a43643d58cac2 GIT binary patch literal 1694 zcmaJ?U2mH(6ir{I4o&;|0h6Uklj$h44FqV@>|s;4mp*lGQG^H~84`gA%tluAw|DoN zuQbbQ<;(WD$M>FlOs;RgKhNJyCnFfoMlZ`C%G12K4L92~F5a9xJein9>(dj(dd1WMQOE+$z&7DFbzt% zOq0b_8`jHYv;2e~=Fh37Rb`T`NfvJ6avMf5QBjwgR?4D^BniHy`65iUJB`3trFkfA z(j_wE*8EbtAJBCg${U&9gh>|Um3$nvSg-2fx(L@_Lm4tMn<=d*)93#uNwR$oL4E}? zuA@9JmPk;QaY55O$_9=V@NeQwl7m$C=X8HB$q2k=vr>lZI7qVj+#pe&!4yc^>OYWR zBh#P)*rHmk;7~l!Adm|-R4gY-m?`NR0>E~Ua)m4#NA!TU<#=wh)8blz*QpZf3l<8P zZC)la$ssqrm1DmG5Srloirhgf`Zkfkc6l>TR)liXA(OTab7&x+n>$?vH66xq3{dI z;(DgAEXKIYV9T~#N7s=Ge|e^3k3Cp{{Fw<~pW%y<3&Ks-)OW86h|@AYs6-`*^@s+D z)u9+b^RyTU90b7`l7J|Rky4su2a4>-IiUjbIzvoeJl1Om%b2?=O6VZoKLquY1a(iK zldCHg-FWhGFBJ)NqtD_v2$K<*jXvnoh=`^3LgM783cd1(7y?`440<e*k0L+c6Y{XZbCnAuZRJ)ntRt?2k-rX@<3`G6jR(5xSMqbv*Ochk0zU(1BTrX==is%VG{90&L*Hj=_>MJpZHtew zvk=VuX1MOy_JrdJjvLfbjoIeE6@x(>ZowJK6DVDC7+huZ;8EGy&W11#F3bMskYo|V RvnazS0X&j54gL;J{sOl3+?)Ua literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/fs_denoise_gbuffer.bin b/examples/runtime/shaders/metal/fs_denoise_gbuffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..bc7d7c2d76c0f3066afc3bfc44eb985c26f4da9d GIT binary patch literal 2299 zcmaJ?+iu%N5LKRKmC`^@O(4-zKxS&ou&1=q`WJ#=Eof`x*YbKK_tr_poNgPY6u0pB~TrfjHoSK{+1{Z=TL( zKj+n^=+lh7D6=jp;2XkQ77Gk8RV|4e*oUpRof+1rvN^SLoZ;zv5&N;0}Vyez3z(FNnXWu z-<<|r@3uqys!4XgCtbqX&CRAR>c-~I?d_F$@CnWvao$&PV#EoF`vR%FGCBF|gBBOw zeVa9QQE#dpUi1mn5gbZaT9I+m8{z!Zak;)88pXq6?ZF4WL z82)c=P@i2E=hf})oQ>tu)%}IVWmm*KVC#OnMY5FWoFO-AVv)X(g}-LU4g^r|eHp1* z(?F6abJnVX8AIuTAcks3N1SjcgJa-SJl&GJLiW!~Dg`iib(?p2jcgrVRZ|o6AETG* z&{2N$?K0%B-qrVZvTp5Tx~?KKt~KX5ly#?yamO9@2WPH_z!t9H@>zsI5jqj)F+%Ku370$UQqdB&hm}DCu_I#9zHd6( z12C;Sn@&VJb9pqroKp{05uBv>z6NV7`C$CrgFYn&)I9)92N?$=tANwV={6ly>f~ixX=Y6;msq+(-Wg7ZOUz97!!)L|Gh_vFr|wAIAg z$04Jcn}&wFi>3SvBEIrg!6Fn6cE1vm}$vW~1vdI2WwU2b;UmNtM@9+MeIWd3 z;YG`Es8ZQZ^AI4tBx$*e YUcs9_w8?PgS9Cq%r`@tD} zsLVHfEy|o%<{yh7N%NRR4R~#D;E`s*GjZF1SLO!#Ss7$3XL0fB`OA~%mi`T7V*3EJ@r!}oNVKR@(1&8aH3l;^1SVZA$Y}ku1Uc93p=XTs` zl_U!Zi+PbD1;|xbt(M@pB~0*Za)zeirVT}S&jSH}m+`n&h`gK&D6+eMl7cPy?|IHP zt%nxia|1l$F;7G>K`i`AE8r5k(k!ZzB z+&RaZNVWoq)U=-^a-JrI;Oi_0InpEtbxh!qY7)NScOjy7j`T4J{*2NYi}d+e zMky10FdB?H7`0t>qDj}o(l%Jucp^WD=mQIrAT7l%+ibO52k)`f{J{isVKR}e)pG6W z(mMhG2bn7E=&ObCX+(26%WE>I&G_i zNkyfzat1*xqM$_BtXwWplmy#=zEJ<~Ev{u^#-O_|;`G$hKy_0X$nH~=vR@m(o&*-^ zN~$FF9fB}Dp_R9=ovX>fDcvp3ypjwUsoHuo%>3at1O5L34_FA)0CkZBgTVl;)dl1z z1*>}hsQhnZ_8ntaZG*#uVBmL=!y+zcy}5_C&~scHI!eT|y7*?S=;Ml^D^+C}TTwHr zLz)nH7b7Gf9%pS(L(q47G$AX&sV?J5w>Ndx=vQs8@8rGZ!{e53l=|)krYfxs-WWX{ zbQOd~6L*UAuG1SBwyv_7rrs`0ZWhMYrM;3oxCWyk#_BrM7~dNj`M^~B-i6k}5iZx& zzfHRN?X;J4QbF08L1w2mR7K?Xb&Qo0{hoFL?_(>ZGwORjMSi75*Wk$!ITYaPHkl>2 z-|xv6|NPu;G-m|Ly{$u}I;PcJNKa`*Pq+ljLZ>J+Z)C&S8Pu=7t)f!M=1Q`XgDNXJhZR&$$`Ys(ZcgGQ zBf_NQ+wf>sea}=lxj{X0j1J)Lsoq^j5Ii$Y5Sq?0Jjr9J_&PubTvm$P(+7QD&yzuU z?w*S0Z1z<)yDC0)lda0|F%|3vF4dET_p@bn*sQQ*o9JN4AM7(2g=af!+lF_rWQDlA zjM>^6IV1TD`$KPdF&uP#-f>;3ghzVI#_TQCwTnGxgQRaEOFvnP7(d?Q%vz)Zo28Fc zUpLzAaoo89WT}yJPjpqgjm^Yu%hWvX*}e1tGl8vwwyW+N_~K6+c{FMd^H2F~?pD7l zKiZ$Kv=W(DN(}NCLaHFuFWyN`~Ri!!|szFSb_DANEL$-#o3> J$;of0{{eIH9}55g literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/fs_denoise_spatial_5x5.bin b/examples/runtime/shaders/metal/fs_denoise_spatial_5x5.bin new file mode 100644 index 0000000000000000000000000000000000000000..a97049585a6b2c627cbf3f22fc9d8059c0705754 GIT binary patch literal 3676 zcmbVPZExE)5Kca=^U$vA4;T(D5J-)!%GQhXHg5xlZeO}D&9D!KA<#DM2+$iODzy{z zw|93WWz$ORE)`lsJU)D$yW{bwy#M&u_xaDGt0R1VIC@bo}7JOrG;gM#}3vtteH|7@lSsfLuWNG#4`OBl{bavTjHHo2I5(qC zrzTm*SjwvcDL}5e>~sXjEn$LRkux+6*IlUMJ01!6yG}=)N|g0XKvmrSlT~cNe=ke6 z?%a0(pKIU=PkAP)F=F9YS^*c(pIpjm`6F&h&gO7^0(rqpCh`)-6R@;Ikykb-h(s%1 z;x0MXM79+`q^A8Wk+VFj1YZ>;$Tv-LP{#y5QBA_<{5Hna9STK3o=Dyf<$vg?P`*j+ z(d*Ao2fqSBi^zK1fd|gq+_HDYK@f_)668)LvQO|wdhox<>tnfsB5XlD?XHi~@UAF0Rv5MYftNDWo=E8U^TdU>T zll>%dW^xSRHo~Jb%ky$h1a-wrE6HcsmSaJ!3=cN6TrtO%ITvSu|Hk7=uqBV;Y%=Mp zgGoiDvvLAaDw3#1*tA|OP?Q9_fWA=wAIz_0Vn(35FXHsnexSN33}k;FrR>)Ra3Fz| zx{?}6eTN`SPiW;WZ0Bk+a7uTJGp{5AMyj^n3^RYY%|QQufqN_jYJj>(q97cgwYh*C zrC>GVkIMfpX5TS()iyXhK#nuyu!zf9t?%G13>?>no)Yn_K8}nPKCT!JrK${LD{4k{ zNE0IO+(!c9aaIO3M7~SRsuG;$G9LE_6K92fZS0#nxxKtk>Da)U6SRN%TB+||V5-u( z5RA~%AXE@~8@m&vcO5qz1ctS*tUI=WvUx)j=sg1T+mv!^F%ttB3aKI&#;)FWvK9Ki zF~*Gv{YDPo&{cg_wp(O=X+78?QbAcA8In^jH-@IS?ty>M+ft5C+=wjS=yIr+kw(!@qFj zUkHtVN-yxFOG+mg$U)fnM>O}+Q+lH(UVvrcPRYQig&;D(hVpZ%3s+cuWO*N1y7+!rR=kdTd%*vZ?g2Geojd398TvzScs30BA@8|vh?_6(q}@)mHan5gV%A9d7K;3n zrHJuUKP{|zE~vF))o&~|P51`fPQ zE00F)Vg4zf?cEBR@}vFvN-L3hrNkhQA*2dY{o)Pgw7yT|sR?K+c4Ot5#JMEz(|+L*t?cK_F;pr4Zl?yGuuQ zkpI?ymd-wCE=k+5BP2F8GvCZNvopi>tG9oBQ#@YG2lzP}e9xkFkrwoAoRuk|M+cui z9iXBXpOHPUD5=FiuqZER7N;HaYk9~1h(#A9E!GLY>>vzH2lFF~N=h!0qGG?_z^tq8 zpnq!6Z*F4R(|7Pz6r?#uam5MTXp!04j_lToX`J!kyR!{nibAD%X zDrjx`z$B%)I_VuPs`AcAhTQ|hNOrxp_FQg&f`?i$2Kiu9ze6F z)LgX0a#x*NhL1hY<7=M2j*~npD&F>WzT57jSGZ{YhevQ?u@o((Yw`@D zjHgkBX6My*i$O)eAs`p=-tsqdS(gRqeT{Znb-ST@!x@&N+g#S~HpAAx&9=4r4Z!$w z0QF8V5P?K6^}?`9rOK_bY^BsUSDE$ARYHAxk(Z;J z%IpJdR}lYyfe+Y=VvXxwk0zmyQhl_pie$GhG(+qm*XT{B<6%0I9#nA}7p-F5161Fr5J3&x zWd|pMN}N`-uLx?IhY&0v8qdZGE1s6#N&=AAr}6A4rKBj2lv3r zxr8VT^~uz88isXg@CnaJ$+0Kv!4MFXqd=8mix)ba4qWr09C7bdJN z@p1DV?;4F@k3WY`?; zi6=j!(aiT|{>dzKG-(x_q>OYNMGvF3NI^`H5mLi_#YRG BgbV-x literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/fs_denoise_txaa.bin b/examples/runtime/shaders/metal/fs_denoise_txaa.bin new file mode 100644 index 0000000000000000000000000000000000000000..2d39dc3e3a730e3c87eda79cb506b5c957b99c11 GIT binary patch literal 8341 zcmcgxOK;;w5>}8yvI>ybVUHUE0Rkz}Fzn{5nVB^Z>>|k}r|iuz2->D60`zc1Y9f1( z|CWE2{i?djZa!qmi%p0bQz~|URrOU>cQy6hPe1%^`;UuP6I>ro-rwi<#jaRapT7I? zA_fxUHUzNL0-udGXSB$asepfc)zgPMF zu6(oH?yG-|%<4bx&Hkjy-;}HE&9Z*zsdJw6=D@z&^1lALH=z;tM!#pg`Is2M^-<5> zMn1KcG$Kr8Ma}nbKbuTGUv6$z``glcwJz&ol~?ucYI%Jzweo7YzWtj1_&;Avr~7KT zdF^eAby?jPH>Jn3UQVY~z1!c^UjF@RdtI!iZ>JuvyVbU+BhQJ+%dT7p?^1+UFQ>m* zl*6j1f2>zO7Ryb(-Pc{T>(_UO{JUN8`rk!e1m5K(2X_|Cua1K(H!g+gOY{;IGR~?e$D?zt7xf%Z-PBmzPytye{+Q=IUzU-E24b3J}A}7hb-u zSNR^puJ?C$P)Z6H9&Diul((;#unW(G7~{;N;{!Xn>P!&Fkel>$#>!)3Ia{28ArGu{ z9W_>(w8b#vEgMHanK?HBcVmQ9^639Ry|+j~5Em6VH5=kue zm@tMoq;Vt^rOL@jL{j$kONu*~P+3PmgK{KJfOgI6qld=3Om)yH&QUbtok_T!cS%4- zo-l+F`}!m89RP;<;~1d*IJX(kJP$H1;c%U{B#!%*B$Obz0Km|agjnhl=^fE8$>`Ji zlccYoO9lF6Jk-zYOrME164Zc^lc;P6X6D6>d#V%fqZ-7k#*KuUO#*0Vsx`D}37cWT|FBL8xvRUKx14HXp zVm9WcDX=?JBQb5l!mBJPo#>_b1}9YHDn;UIr5rtM*lvcg;#Ai}r+Km3JL1-aS-

eoU#*O~=zpkLYxBm#Iv2OOy&UA_FQ@q5F$x zsiH(C_lBx8l>i-6C8_^A6}3K%D*D5SQr?ZAG(v;XQA*Jj3HdiO7obo$j1-}zLKF5*K2amPZ9kHQZ#zS0D}xrihH z`gED21I=ZZiL)=gneYQp=aAOYmkUr#$433(0VLXCTK2er>Hs#l+GxC>s=VFSM6HP0 z4Lb0{fGmJQdxQfs{yE(6kaM0A$*q|F4pe zmTDpi%e2K$?=b>*Zmv z!wc;2&c52b@w)PHc4uH$73vH2n~GcBA)A{0*vyld=Uu?$V9qZv-tyn}RR$1Keq}(G z2p`=W=+;*hN>pqVXvf3IsO?Uryr8Ksz{ztbXS5Vj4D5T14tQoZ3XG%bNV7y!=new? z0HKGvl*a~1k(zfGG?PgpFtl@&>@6Rb85#y!Q-Y>dMo6768E$NeLh@1qC9Obl-R1)J zaP?!>3!wl{#mV#SL`a4c;Q%1aBS4eNc8c};x7OTpRh8|RuI*en>_KQ}_I-x}>pMN{ zVvvM5puT`t+83NDC*8K5X(B=d*ErH})Vj(fksYn4L}~=u1~U>(&dfg3@G^1-cfGY- zkux2>NO0U=nEVt{lNwBq9ca}V{)N(A6=xFflL=p`NWw6cfvH-urJcbCb*c$UO^a=x z>*KS+ILZ#(#|G*BR^dQ$Y!Ga6)a#lc(33Sm`Y9itQxm##pjpqW2{`a4)I@9 z&#Vc4b;RyGSre_>XVgUNDwA|hO(0r-RZWn;XVe6zCKAgbcvur?5rIafL&=G@;ABl? z+%oH$h^Q9|<51xwrjnp}v+I^nhSraE1e!+KkGlw+$M6Y!FOzX6iH{(32%X z+ZtuZGfD*Z{BPR^9QYGT#PsGwD_ySdXO>8N;COP|ux_7GBCV@umk6TuSCt6)dq#;+ zYEbSw1P@9CT{<7eDgREC2%z|VDs^D(#VtB#HTECQDoD&p2i2z;A(1|?xT2;SoKfaj z(;3AR$;=i@pRGC~eMIVr^m)jL+OrDYaq&(`rYU;jL!{66_FuSGBA literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/vs_denoise_gbuffer.bin b/examples/runtime/shaders/metal/vs_denoise_gbuffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..b9149950701dc56451a14761f70ae4c54092646e GIT binary patch literal 1720 zcmb7F-EPw`6i!D4U3LMFU-{`F_pb)IUR$rjD8>I@>{p_zSo7_ zp2iE3ldL#8`Pn-G;7M%=22N>ktT7YIlGg1wYvl7|$9c}glt*F>wHzN3}+q(e@{9(WH0zQ4a5=~E=c{sq`p1vQUY5>?B z^}IbHS25Saa*9F-7rm_ViM%rS0UE;EFSD@{X*=0ebp-VQCANSOU>`34aW~hB3Aw%x z-at*aY1G(x3--1J3x;XmsOmjHl<$ q4Bg0B|34TRpsMTA)=OFHe*(>^(zz(70j>(ybsil#9p{;|===pEvN&A; literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/metal/vs_denoise_screenquad.bin b/examples/runtime/shaders/metal/vs_denoise_screenquad.bin new file mode 100644 index 0000000000000000000000000000000000000000..c739d7e5915cfaaecae9363bdf0b726195b27f1b GIT binary patch literal 699 zcmaJ;O=|)%5RKxcxwOBa3>1nM*{XQ+gWlSUR48~^B*filQ?iLsOtz8*LL4QU$|Ip)?}L z)ab>Sm;ZB}rT4`Vy^c8#xwb2?GE@0lCJGL$0p~gTkRXUh;}+8JJqKD#<0SUCeK->^ z;GZRBYa~w4Or*BL1*}qgAe2SFn>uarIp8{=*a%eH#5tdKAdW+>eABqZc3SiKpi&f8 zMoe9JO{-~{OwR9h(_$3ivB6__DsG4VmdW5SADuLaDqN3PFd4j-Mqt&=s zebW|b$8nBwrG&Qgc^eXEDYPepZ$V^eL?GBAESF?Lp{sT5pqh;=h2k5*p0pHGp(v`l zGdkvq&ZY!+#{o9AjY>D7?e3!u-Kf(l)BQiD5%og}PB#7Uyu#pZp@v4nIjh&$6?+k#Xo(THF|K$n_Q{~<=LB@%E!A_RkR)jVjeCdFskYPxizD^1j` zeiA=|Q8zC9^9hJMqYGkU@ceG?=z~e7@1Aq!&di;AyRdqFFrKJfcCwrs?$kEoR@`2V zHXpPa?UB^ZPD+B&Hygd~!*;{yVy70h)*JPBPxwE6PkcUPFS{z`(gU`??B+aiH|ty` zeIoB%%I$XpZj9ox5tkO@+>l!2ySTKvRO)o=rE}-Y=)*3<$7@CH+ov_Y8Qr@N|6Kl3 z{ziUKv1lryRmQ2L`=z8=vB^DI)tZ*Jdm_Z;+?oG;IAWil@%*lt`1sibZyU%%U(Hr+ zp&d1^#c{hXsQ9e!^--Ew&*m>!R_QD%5b>*avyHK~Kq zt8(J$IjMuuSAtIONga$n7j(`rBrgWcoG0WM`RUv6A>CtIc*;*+FF@;Y{pR#NB91$m zl#jVV-D3ZaQu-#%@ThWAmp$YG9|)Lx1RoBV*D)zv(ifxNrGS|qhJL!g^tI8!%nP%4 z=7X8eJg{Txe_mhuKB>L*bzA=7eW7YN5}tm$RWbZj*ty47sr^hV&c+$Y4Q94*=r6*W zGoyoBP)pRKfIQZv0X4k$CeNy*Osd95Hfii{Za=MJ)5v^Yd>H z%$d*1nU(1mv}RVOU(}jefjPq~IsLCGD|6eDlY_js#Bq-Aa(H5Is0wFzCx^!|weEYh zJU@h~MSaWjQ)~ESo{z%tIA(bYVOHCLV+O<)bfeV&9r$s5iD9Ns)Gyj6IsFl1d9u>U zO&)a1lMg(3h~W-al!tfoS&ohw_eE>lW8BxkaaYUQ$;`gV*-tz=Z>*vlL)Mv9gaNv;>=9s!R#W+-|Ec2Ch7;X z8@OG4A>P2;7~HoQNAXZB^RLR89n4G{a%Kgic_~NlT3yWjE~kdR8*%7<$l<9yk_!58 LKbmqcnAGeSP@hmd literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/spirv/fs_denoise_deferred_combine.bin b/examples/runtime/shaders/spirv/fs_denoise_deferred_combine.bin new file mode 100644 index 0000000000000000000000000000000000000000..fa6ea8b9b33aa84595d18bb7f463be9ebb622897 GIT binary patch literal 2986 zcmZ9N%Wu_H5XVnjS_+hxqDC}WBw?Y1un>b0`otCq3RpoCBki@ch0yjkw=FagV|n8v zEEIGDmP8W;cfJ=sNQ{301T^Z>kg#y2@sVKte17*g^~%YeocVrd&di)S=lpI**QPne zWlif7qb@BN?d~lO7E4`)p^?G9(&DP==_(9CKixMmHeTurdf{mIaIrL07`(-OhQGzX ze!P36P$~?K-qw&3zf>Oq)}&Sos9rKJrJCv$vs0={b5ni#!R(C1sTwAwdDQ6Kxut8% z>d~>?s~=jsM*D(P{-g@N{B zv9!C)*JgF^?;h)$=#^cQ`MQr62Kpy5?~R4A0$u2&dh5O4g^a+<(YNQ=x-0Es?X9`} z7;_!62-!rYsCxz2(Mip6)tNpktG_ay`!Sw}Vt)6;k*0+?_6(MxJG8rxz_%0CxXZIs zDd$)UV?B4md0vgk-DwUdTs}XR|8E4oi`eFMjWNaZbL<(5m*m)+a|d`Ob0cDmM(|o@ zhxx_pGQ_22el)k+Z--dB{d7DCHeN@DQo4uwH>0~&n|19f+gbwmWO%YUa6kBwt6N(G z4}iBEINP?0b8&Bb5Pjss#|!uRuMczvJ_vRMKAfRT&F=RIdSiymcJWcfKHZDmyPz(& zEI)TSdvSu3dIp zM)Z+)4!>W}-y3;p=FF~x=U!W}KI}KZDV=X|&o|NBzdf~QV&6yfqzXmdo@p2RUgTWg zfZ|<^^b?T*fe8t}tNOL1jSq8|?2B*0VXQx4YMD2{)-g9_dKr6v zTM+NLc59hN+=tlOYlv;#t(>ms+=}kq=s>&^-UEF-`!@ZMFUaT}Xh%Ps^M6oHKiKg% z{@OMoTM=u_bfTEE1I{`W2XH2TC!jLFcO~*~!lwNc`Hk~9y7x=|P|j^eH@>#haMt|> zy8IsW2D&8oCYWE=6Ya-x-_z*Y_Eq?v0rTTOj+iL1|0mG3O~GHt<30(N_bvAzf3bJ_ zVh`*^^m!Y4yd9hN$nzAsIppngAG51G(?O@>h^}lVj-eeybaZ?Ow*g{P;emq&?nH^!EUqK7Pl2@2;5u z%U?%t$b0f4m|t3o=5QleL+JY?FC}Wi zlY(fBCO#NnjDG?zz8G(`V&p+%jMo?9qcMhP2!7u_Yub&v=yc8RH?wBVnl*dxvaP#w z&X=cu?9CXBS2TGOy`d9gX z`8SRBj2$UI16!_KOJHNqjy>D6&A_Y{qPn;}+maKf=4Dx3?dnGTugPX-Q?=4rwjis` zBQ#|V`P|Xf-PLw{q_^$%JKD9+%j%qRTMK9wx-w8YGAQ5A_D7K+WEdGm#*x#=8RSi5 zJ~3Rw=4f?UJ@X&%^{~ZQ_2lp%wm3{G2!0LO!v#JSBs?%L{atS}y1#eOgx->{wO@|z&~DwuOgXmZ;)rj(#dfv+)>>>IitSsmLu`G;OgXl$ zV&g63xARJ(8Bcu~zZ;kr!PxJu$kJ>MPh7SCjZ++f7qge=a*c7sa}zc<@xp}NYYF%| zW;t;y_yJ~z``?wY_wCUBP*u%y(JuCW9pUeNI>PS#Ij+ZlKXWT$&bKD~IJ)BovN*u( zIkkC?31n)rd2`^C;M>k#+!XjVFyo%UU*~XQ8ehoqOuWlgJRR?566x#foAz%5zw`Fe zY1|H;n(Uh1Z=;E46CW)nS9{L-T9*!VYcA)x#rB+rJt`6Fivm~V&%xURAvI}_N%%Y2gNf{`!`v>x z_^r4b)}HP{>}Rokwga($!`_W<|Au`py8SD*HitR0_aydg6?E@K?D>wNdk^7%9Nl{e zJcjN)>?5lK_^%~lb1<*{aPF}lPTp8M$kKf#(d88fFh6T^s{ICdInSSl7u$%tL9U^hl^lsin*F|(f%8D?^tm-X+Mv*_puM4&x;8^lJHjvKMI!9 z-}66)Zq4-C(_yhbk}Q(wa3ui!~A0H(_r_|7Jeh>?osp`1-pl~*kcUc_1fazpG9|X zCAjC%^;N8e_x?P(@ozy~qfhk23(3}+{36#A@W$01+{@_3mD@@0_&t3cU0-e1`E;tu zTVQSWP&|Xbqnn4%p!TT2pOEqFHuDZJ=XbH|+lKhvvX}LHCEejAbbD66;Qj*3`(3kc z#`zmvz8#5Y=`yl5#?1?ckT620&qy7w8m`V74fC-3(x?)(FEt^*M(` z9T%Z%cLcXMamEU6N#b`WbZhQul8T*IcH88}cg7&;9|(1cA>0 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/spirv/fs_denoise_spatial_3x3.bin b/examples/runtime/shaders/spirv/fs_denoise_spatial_3x3.bin new file mode 100644 index 0000000000000000000000000000000000000000..6ab12ddecbfa8648e23587b75ce38aceceb83fa9 GIT binary patch literal 4308 zcmaKuS!|Y75Qe|Dv}}a}6+twF+bCCX>IS?(%I27y5iiq3W$Jj?%3Y9wWlNCy56?U-926Hon__& zc$q!htnb*;cYB$6z-87$wzh3)?`iMq9W!E7Hp1|>wr$(gaVKUSonr{g$Ofl?8?wb{ zS>=!jsUd610St1V4Ow%pe_5AhRh3f*yL?zSBs)+koMof4%6!A&Sxr7KUEaEUR&U?> zS##z#$dAmboN}&?b%T%{UG1ATi_amNMn(%`6t;3WL8{8CnNNUMgBI6Rcwk_#t-`LXK-Lt-gSLgZK*4EdteVu4SZfpM5 zZrrdvx4xmhubu2KC2QtsiVOKdpNzaB>E^4*tIIEDXm8$%yt@3##GAh&uP)z|c=K4~ z)#Xv1BopZ4Z9^m=x6-^qD}mV8VAF~*C*tldeBOR}LIUqPr9#4ej2l%{udxgf0=&iZhzbN!IxnAJt4*&NfR}}OI zki8rIA7s{6%=L$oE*|GQjGSft3qwDW>sfw&{U2o%<9P$!{VxJN#vE~+F4*aF7(u=K z;k5bsy&BmV&5P$$x8}v<=O@`(lvno+@XO?z5%d}B)qN9UGpRglawkEyGRK(5TQzR$ z*iYL(LG0=JFc%xx!&t9Jw|=KHtX*|$WG2H}4c_-^tp-0E*;);L9d?T{8s_xmY z!Jp^4l_*-!rxBn&^t&3&^Gyd6*EZ}L(DkMNu$v7guH8*UYYy%~7I*le<~caP-tzOn zwXx=RC9Dxl-28>VMaahPdBR^4n7IB{vevxqM;7;M0k9;Bw*TF=W?@H{rV; z+sBcu6W1=Jiqw5G!9S6B&nvI)8w&o(#QToq)r;$&Dy)yVo(2<-J)Zd&n`gnqBlhQz zT_^5+TT3q>TTgO-Q3)?G`v#m(awFR8f!EG=u@89^L(co!oz}|TTj1;qyU@*v+?d3@ zj%H7Ih5h916{ooS`oyDVXVB$eWr!P(vhlsftnM5!=+`*3xsSP)5wRPu@yqQmaK^V6 z-T352CNAPT1|}cz)g?aSlYf&TZam5_rn9|;>|MJ)u(uQDnV(N~?|^A5cba=Y&K zy~_}De`U|_+0@0m)0=q@Szo?a@8o@SG5cKDe1NX+u=xmGOq-~WkI`N0TZ;Gl39|3m zZ%5kyQ*?3dSMg5mTX(Uyc-Y3CpMmMicN2Slp6r+g_WS}xT>IFwpFPFhn`xl;EJH8v zi8q3YyC!H!|M%_i*yH>8<^M%rBe3a9{a2>uvZf@5gJw#64@| z{zuk``$aAM1ZJO>tK~k8`)6c+R9KeD-;E^x3$k3q|0{Aa{$lU^4Ng96fBzrbKj7q} zZcZS(UOdi!5}9BA_t*bBXtOWn{uHvYX%lBSjm(e#)6ruPV(&A^a^^Go=ikWoaNkkn z|17$A*qlo?zN4_QP{d>J3Ut?r2UdlwPr2y3HOSViobSp$I|SK&B^PgPII{gsyW;zg z9_O9d^R%%?Xn&kJ`ky_|J>}eAt{;E)KYO0%wO18;t36HKK5wsajy3ji&mU{z?}dFj u?rAJCZt{DwFMC%js2U?cj)U;?yXV;1; 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 KwPk+AHupbAxJy9* literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/spirv/fs_denoise_txaa.bin b/examples/runtime/shaders/spirv/fs_denoise_txaa.bin new file mode 100644 index 0000000000000000000000000000000000000000..370bc762a094d5b85df3e840468de785b009e609 GIT binary patch literal 11172 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 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/spirv/vs_denoise_gbuffer.bin b/examples/runtime/shaders/spirv/vs_denoise_gbuffer.bin new file mode 100644 index 0000000000000000000000000000000000000000..436c6db437170b1c15f3d8e96abd5e1f1527f0fe GIT binary patch literal 3399 zcmaKv+izP%5XLugoY+aB1;ph6f)i6nn<#`!OQ95!hLV6PHK?MG7-Ku=w2n`3>{1Yy zK*bXmQHhJh<&8e@H}F63Mk|VFc?1b0kdQ!?kobNlGtLsKY&6~ZzPaq|?(DI8;`m}Q zXncF_N_`;g>{eFNdZT$FX}rADPEQW>%@zh^Ep#iZ)pm7dt$$>_Fd|~e9OV#jD0VAJ z>oj2leIa9h+7UvozdRH|UlNPd(2A^B7Cm*lD>r@uU$7RoC+j?Zh|M`U}pl`N<2mBY=n zc1lp}29Ms`OvdLke`>Z(%X28>`Zqa0v0ff_;io25b>zaMC>nYFv5#t{r4p)5=h#=Z zvYM_Xog~$6@nfIWN~dwAmZt6cjF{nU-?NQYr~S%OnzTB{x}DW-XZpovWvLe`zhZVa z|MxrHTa{d#-L-r+SEP>N?0mQLyH9!W;|{j-yWjk{r|tY6kbOZn_mJj6d<{Qrc$ely z*||#wcf6>Z5%ZTeSR&Nwi3?g2aT;2RB->t^9`%_Rx-O&aFx z920w&gn=ErJ45S_yZ?kR`5f<&&U%jbN~a#jlhXNaIKEdpgL?L9E&(*KgZE2HlG}yp zQ}zhw_5;$dlVGQBITN^1Rmz&UrYl zNssNz!o<5hkzR~2AEaT!|Ho_YWYrcpM<-mb})Slhy8og$$=eA zAG`fW)9D55;LDNyXX)IZ$Ajr}IP(8$x~~tW@7?~p>7F0VJHXC*aC?9GoOD6~XoT`}@w_7@K zyeGu>akqGE?sre*$6Is13Gvw6Z%^dM8+5iNB3T-*mWam3?~eWMQsRYwSQVa}v1;hRTooMS^gHt&UH>Ewa; zniA>o4ChW72kqP`tJ@anR0vVO*T|OL4^G zkDa`a>1)jIMOhe~-v-VCo!=Ug{tIu9 BO}hX9 literal 0 HcmV?d00001 diff --git a/examples/runtime/shaders/spirv/vs_denoise_screenquad.bin b/examples/runtime/shaders/spirv/vs_denoise_screenquad.bin new file mode 100644 index 0000000000000000000000000000000000000000..da03129e904007f4d090b2cef7fe39af075608ee GIT binary patch literal 1078 zcmZXSO;1x%5Qe8;fF%(L0bx^HSrSW?j}X8X*t820YluWdy=mHNF0?(Q+zRge3I13Y zY)pKg<2^=kn(3MMojLP$?&(SEfk|AczMrwVNi=l5{@^t3-?Yc>BA+qK%bV9*;C@m(#MLYFEBHJc}+0)7KImdmZj8NOYx#?1WfFTN8cLD!qg@w`AX@m zaAh%No#bWyy%bkc4nKRqAL5_MrY`x22eS`2;-9BBJ>v(npWxq>&6gVPgV`4x_v@)0 z?t|GM{%yS{y)Xv?cPEDL_@!8Ku1o0$OpXmH^L`6P