├── .gitignore ├── LICENSE ├── README.md ├── ReSTIRGIPassWithGBuffer.py ├── RenderPasses ├── GlobalIlluminationPass │ ├── GlobalIlluminationPass.3d.slang │ ├── GlobalIlluminationPass.cpp │ ├── GlobalIlluminationPass.h │ ├── GlobalIlluminationPass.vcxproj │ ├── GlobalIlluminationPass.vcxproj.filters │ └── GlobalIlluminationPass.vcxproj.user ├── ReSTIRGI │ ├── FinalShading.cs.slang │ ├── InitialSampling.cs.slang │ ├── LoadShadingData.slang │ ├── PrepareSurfaceData.cs.slang │ ├── ReSTIRGI.vcxproj │ ├── ReSTIRGI.vcxproj.filters │ ├── ReSTIRGI.vcxproj.user │ ├── ReSTIRGIPass.cpp │ └── ReSTIRGIPass.h └── ReSTIRGIGBuffer │ ├── ReSTIRGIGBuffer.cpp │ ├── ReSTIRGIGBuffer.h │ ├── ReSTIRGIGBuffer.rt.slang │ ├── ReSTIRGIGBuffer.vcxproj │ ├── ReSTIRGIGBuffer.vcxproj.filters │ └── ReSTIRGIGBuffer.vcxproj.user └── Rendering └── ReSTIRGI ├── EvalContext.slang ├── FinalSample.slang ├── GIClearReservoirs.cs.slang ├── GIResampling.cs.slang ├── GIReservoir.slang ├── Params.slang ├── ReflectTypes.cs.slang ├── ScreenSpaceReSTIR.cpp ├── ScreenSpaceReSTIR.h ├── ScreenSpaceReSTIR.slang └── SurfaceData.slang /.gitignore: -------------------------------------------------------------------------------- 1 | .vs/ 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2022, Alegruz 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Screen-Space-ReSTIR-GI 2 | ReSTIR GI Implementation on Falcor 5.1 based on the source code provided by Daqi Lin for his ReSTIR PT paper 3 | 4 | [Original Source Code from ReSTIR PT](https://github.com/DQLin/ReSTIR_PT) 5 | 6 | # Integration 7 | 8 | 1. Add the `Rendering/ReSTIRGI` folder into `Falcor/Source/Falcor/...` directory so that you have a `Falcor/Source/Falcor/Rendering/ReSTIRGI` directory. 9 | 2. Add the passes(`ReSTIR` and `ReSTIRGIBuffer` folders) in `RenderPasses` into `Falcor/Source/RenderPasses` directory so that you have a `Falcor/Source/RenderPasses/ReSTIRGI` and `Falcor/Source/RenderPasses/ReSTIRGIGBuffer` directories 10 | 3. Add the script `ReSTIRGIPassWithGBuffer` into `Falcor\Source\Mogwai\Data` directory. 11 | 12 | Build the Falcor solution, run Mogwai and load the `ReSTIRGIPassWithGBuffer` script. 13 | 14 | # 1. Introduction 15 | 16 | Global illumination research focuses on methods for computing light transport along some of the paths photons draw. Algorithms that solve the full rendering equation can generate stunning photorealistic images. Those methods, however, are too computationally expensive for real-time applications. 17 | 18 | So, why Ray-Traced Global Illumination(RTGI)? 19 | 20 | RTGI resembles feature-film image quality. Ray tracing using path tracing with Rendering equation generates correct solution with global illumination. 21 | 22 | Recent trends focuses on hybrid rendering 23 | * G-buffer is rendered using rasterization 24 | * Direct lighting and post-processing is done using compute shaders 25 | * GI, reflections, and transparency & translucency are done using pure ray tracing 26 | 27 | ## ReSTIR GI: Path Resampling for Real-Time Path Tracing 28 | 29 | * 2021.06.24. CGF HPG 30 | * Effective path sampling algorithm for indirect lighting that is suitable to highly parallel GPU architectures 31 | Screen-space spatio-temporal resampling 32 | * Resamples multi-bounce indirect lighting paths obtained by path tracing 33 | 34 | # 2. Implementation Details 35 | 36 | * Uses NVIDIA Falcor 5.1 37 | 38 | Initial sampling is implemented with Ray-Traced GBuffer `ReSTIRGIPassWithGBuffer`. GBuffer samples the position, normal, radiance of the visible point, and the sample point scattered from the visible point and its PDF. 39 | 40 | Initial Sampling: 41 | 1. for each pixel q do 42 | 1. Retrieve visible point xv and normal nv from GBuffer (`ReSTIRGIPassWithGBuffer.rt.slang`) 43 | 2. Sample random ray direction ωi from source PDF p1 (`ReSTIRGIPassWithGBuffer.rt.slang`) 44 | 3. Trace ray to find sample point xs and normal ns (`ReSTIRGIPassWithGBuffer.rt.slang`) 45 | 4. Estimate outgoing radiance Lo at xs (`ReSTIRGIPassWithGBuffer.rt.slang`) 46 | 5. InitialSampleBuffer[q] ← SAMPLE(xv, nv, xs, ns, Lo) (`InitialSampling.cs.slang`) 47 | 48 | Spatiotemporal resampling is implemented `GIResampling` compute shader. 49 | 50 | Spatial resampling (`GIResampling.cs.slang`): 51 | 52 | 1. for each pixel q do 53 | 1. S ← InitialSampleBuffer[q] 54 | 2. R ← TemporalReservoirBuffer[q] 55 | 3. w ← p^q(S) / pq(S) 56 | 4. R.UPDATE(S, w) 57 | 5. R.W ← R.w / (R.M · p^(R.z)) 58 | 6. TemporalReservoirBuffer[q] ← R 59 | 60 | Temporal resampling (`GIResampling.cs.slang`): 61 | 62 | 1. for each pixel q do 63 | 1. Rs ← SpatialReservoirBuffer[q] 64 | 2. Q ← q 65 | 3. for s = 1 to maxIterations do 66 | 1. Randomly choose a neighbor pixel qn 67 | 2. Calculate geometric similarity betwen q and qn 68 | 3. if similarity is lower than the given threshold then 69 | 1. continue 70 | 4. Rn ← SpatialReservoirBuffer[qn] 71 | 5. Calculate |Jqn → q| 72 | 6. p^'q ← p^q(Rn.z) / |Jqn → q| 73 | 7. if Rn's sample point is not visible to xv at q then 74 | 1. p^'q ← 0 75 | 8. Rs.MERGE(Rn, p^'q) 76 | 9. Q ← Q ∩ qn 77 | 4. Z ← 0 78 | 5. for each qn in Q do 79 | 1. if p^qn(R.z) > 0 then 80 | 1. Z ← Z + Rn.M 81 | 6. Rs.W ← Rs.w / (Z · p^q(Rs.z)) 82 | 7. SpatialReservoirBuffer[q] ← Rs 83 | 84 | Final shading (`FinalShading.cs.slang`) 85 | 86 | 1. for each pixel q do 87 | 1. radiance, weight ← Reservoir[q] 88 | 2. pixel radiance ← radiance × weight 89 | 90 | # 3. Results 91 | 92 | Hardware: 93 | * CPU: 11th Gen Intel(R) Core(TM) i5-11300H @ 3.10GHz (8 CPUs), ~3.1GHz 94 | * Memory: 16384 MB RAM 95 | * DirectX Version: DirectX 12 96 | * Display: 97 | * Device: NVIDIA GeForce RTX 3060 Laptop GPU 98 | * Approx. Total Memory: 14053 MB 99 | 100 | MinimalPathTracer without direct illumination took approximately 35~36 ms per frame (28 FPS) 101 | ReSTIR GI took approximately 44~51 ms per frame (22 FPS) 102 | 103 | Comparing the results, ReSTIR GI provides better image quality with just a tiny bit of fps drop. 104 | 105 | # License from the Original Source Code 106 | 107 | Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. 108 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions 109 | are met: 110 | * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 111 | * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in 112 | the documentation and/or other materials provided with the distribution. 113 | * Neither the name of NVIDIA CORPORATION nor the names of its contributors may be used to endorse or promote products derived 114 | from this software without specific prior written permission. 115 | 116 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 117 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 118 | IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, 119 | OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 120 | OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 121 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, 122 | EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------------------------------------------------------------- /ReSTIRGIPassWithGBuffer.py: -------------------------------------------------------------------------------- 1 | from falcor import * 2 | 3 | def render_graph_ReSTIRGI(): 4 | g = RenderGraph("ReSTIRGI") 5 | loadRenderPassLibrary("AccumulatePass.dll") 6 | loadRenderPassLibrary("GBuffer.dll") 7 | loadRenderPassLibrary('ReSTIRGIGBuffer.dll') 8 | loadRenderPassLibrary("ReSTIRGIPass.dll") 9 | loadRenderPassLibrary("RTXDIPass.dll") 10 | loadRenderPassLibrary("GlobalIlluminationPass.dll") 11 | loadRenderPassLibrary("ToneMapper.dll") 12 | 13 | VBufferRT = createPass("VBufferRT") 14 | g.addPass(VBufferRT, "VBufferRT") 15 | 16 | ReSTIRGIGBuffer = createPass('ReSTIRGIGBuffer', {'maxBounces': 1, 'useImportanceSampling': True}) 17 | g.addPass(ReSTIRGIGBuffer, 'ReSTIRGIGBuffer') 18 | 19 | ReSTIRGIPass = createPass("ReSTIRGIPass") 20 | g.addPass(ReSTIRGIPass, "ReSTIRGIPass") 21 | 22 | RTXDIPass = createPass("RTXDIPass") 23 | g.addPass(RTXDIPass, "RTXDIPass") 24 | 25 | GlobalIlluminationPass = createPass("GlobalIlluminationPass") 26 | g.addPass(GlobalIlluminationPass, "GlobalIlluminationPass") 27 | 28 | AccumulatePass = createPass("AccumulatePass", {'enabled': False, 'precisionMode': AccumulatePrecision.Single}) 29 | g.addPass(AccumulatePass, "AccumulatePass") 30 | 31 | ToneMapper = createPass("ToneMapper", {'autoExposure': False, 'exposureCompensation': 0.0}) 32 | g.addPass(ToneMapper, "ToneMapper") 33 | 34 | g.addEdge('VBufferRT.vbuffer', 'ReSTIRGIGBuffer.vbuffer') 35 | g.addEdge('VBufferRT.viewW', 'ReSTIRGIGBuffer.viewW') 36 | 37 | g.addEdge("ReSTIRGIGBuffer.vPosW", "ReSTIRGIPass.vPosW") 38 | g.addEdge("ReSTIRGIGBuffer.vNormW", "ReSTIRGIPass.vNormW") 39 | g.addEdge("ReSTIRGIGBuffer.random", "ReSTIRGIPass.random") 40 | g.addEdge("ReSTIRGIGBuffer.sPosW", "ReSTIRGIPass.sPosW") 41 | g.addEdge("ReSTIRGIGBuffer.sNormW", "ReSTIRGIPass.sNormW") 42 | g.addEdge("ReSTIRGIGBuffer.vColor", "ReSTIRGIPass.vColor") 43 | g.addEdge("ReSTIRGIGBuffer.sColor", "ReSTIRGIPass.sColor") 44 | 45 | g.addEdge("VBufferRT.vbuffer", "ReSTIRGIPass.vbuffer") 46 | g.addEdge("VBufferRT.mvec", "ReSTIRGIPass.motionVectors") 47 | 48 | g.addEdge("VBufferRT.vbuffer", "RTXDIPass.vbuffer") 49 | g.addEdge("VBufferRT.mvec", "RTXDIPass.mvec") 50 | 51 | g.addEdge("RTXDIPass.color", "GlobalIlluminationPass.diColor") 52 | g.addEdge("ReSTIRGIPass.color", "GlobalIlluminationPass.giColor") 53 | 54 | g.addEdge("GlobalIlluminationPass.color", "AccumulatePass.input") 55 | 56 | g.addEdge("AccumulatePass.output", "ToneMapper.src") 57 | 58 | g.markOutput('ReSTIRGIGBuffer.vPosW') 59 | g.markOutput('ReSTIRGIGBuffer.vNormW') 60 | g.markOutput('ReSTIRGIGBuffer.vColor') 61 | g.markOutput('ReSTIRGIGBuffer.random') 62 | g.markOutput('ReSTIRGIGBuffer.sPosW') 63 | g.markOutput('ReSTIRGIGBuffer.sNormW') 64 | g.markOutput('ReSTIRGIGBuffer.vColor') 65 | g.markOutput('ReSTIRGIGBuffer.sColor') 66 | g.markOutput("ToneMapper.dst") 67 | 68 | return g 69 | 70 | ReSTIRGI = render_graph_ReSTIRGI() 71 | try: m.addGraph(ReSTIRGI) 72 | except NameError: None 73 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.3d.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #include "Scene/VertexAttrib.slangh" 29 | 30 | import Scene.Raster; 31 | import Scene.SceneTypes; 32 | import Scene.ShadingData; 33 | import Rendering.Materials.IBSDF; 34 | import Utils.Math.MathHelpers; 35 | import Utils.Math.FormatConversion; 36 | import Rendering.Materials.TexLODHelpers; 37 | 38 | RWTexture2D gColor; 39 | 40 | struct GIPass 41 | { 42 | Texture2D diColor; 43 | Texture2D giColor; 44 | 45 | uint2 frameDim; 46 | 47 | void execute(uint2 pixel) 48 | { 49 | if (any(pixel >= frameDim)) return; 50 | 51 | gColor[pixel] = diColor[pixel] + giColor[pixel]; 52 | } 53 | }; 54 | 55 | ParameterBlock gGIPass; 56 | 57 | [numthreads(16, 16, 1)] 58 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 59 | { 60 | gGIPass.execute(dispatchThreadId.xy); 61 | } 62 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #include "GlobalIlluminationPass.h" 29 | 30 | const RenderPass::Info GlobalIlluminationPass::kInfo { "GlobalIlluminationPass", "Add two input into one." }; 31 | 32 | // Don't remove this. it's required for hot-reload to function properly 33 | extern "C" FALCOR_API_EXPORT const char* getProjDir() 34 | { 35 | return PROJECT_DIR; 36 | } 37 | 38 | extern "C" FALCOR_API_EXPORT void getPasses(Falcor::RenderPassLibrary& lib) 39 | { 40 | lib.registerPass(GlobalIlluminationPass::kInfo, GlobalIlluminationPass::create); 41 | } 42 | 43 | namespace 44 | { 45 | const char kShaderFile[] = "RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.3d.slang"; 46 | const std::string kShaderModel = "6_5"; 47 | 48 | const std::string kInputDiColor = "diColor"; 49 | const std::string kInputGiColor = "giColor"; 50 | 51 | const ChannelList kInputChannels = 52 | { 53 | { kInputDiColor, "gDiColor", "Direct Illumination Color" }, 54 | { kInputGiColor, "gGiColor", "Global Illumination Color" }, 55 | }; 56 | 57 | const ChannelList kOutputChannels = 58 | { 59 | { "color", "gColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, 60 | }; 61 | } 62 | 63 | GlobalIlluminationPass::SharedPtr GlobalIlluminationPass::create(RenderContext* pRenderContext, const Dictionary& dict) 64 | { 65 | SharedPtr pPass = SharedPtr(new GlobalIlluminationPass()); 66 | return pPass; 67 | } 68 | 69 | Dictionary GlobalIlluminationPass::getScriptingDictionary() 70 | { 71 | return Dictionary(); 72 | } 73 | 74 | RenderPassReflection GlobalIlluminationPass::reflect(const CompileData& compileData) 75 | { 76 | // Define the required resources here 77 | RenderPassReflection reflector; 78 | 79 | addRenderPassInputs(reflector, kInputChannels); 80 | addRenderPassOutputs(reflector, kOutputChannels); 81 | //reflector.addOutput("dst"); 82 | //reflector.addInput("src"); 83 | return reflector; 84 | } 85 | 86 | void GlobalIlluminationPass::compile(RenderContext* pRenderContext, const CompileData& compileData) 87 | { 88 | mFrameDim = compileData.defaultTexDims; 89 | } 90 | 91 | void GlobalIlluminationPass::execute(RenderContext* pRenderContext, const RenderData& renderData) 92 | { 93 | // Clear outputs if no scene is loaded. 94 | if (!mpScene) 95 | { 96 | clearRenderPassChannels(pRenderContext, kOutputChannels, renderData); 97 | return; 98 | } 99 | 100 | // renderData holds the requested resources 101 | // auto& pTexture = renderData["src"]->asTexture(); 102 | const auto& pGiColor = renderData[kInputDiColor]->asTexture(); 103 | const auto& pDiColor = renderData[kInputGiColor]->asTexture(); 104 | 105 | if (!mpShader) 106 | { 107 | Program::Desc desc; 108 | desc.addShaderLibrary(kShaderFile).setShaderModel(kShaderModel).csEntry("main"); 109 | desc.addTypeConformances(mpScene->getTypeConformances()); 110 | 111 | auto defines = mpScene->getSceneDefines(); 112 | mpShader = ComputePass::create(desc, defines, false); 113 | mpShader->setVars(nullptr); 114 | } 115 | 116 | auto var = mpShader["gGIPass"]; 117 | var["giColor"] = pGiColor; 118 | var["diColor"] = pDiColor; 119 | var["frameDim"] = mFrameDim; 120 | 121 | // Bind output channels as UAV buffers. 122 | var = mpShader->getRootVar(); 123 | auto bind = [&](const ChannelDesc& channel) 124 | { 125 | Texture::SharedPtr pTex = renderData[channel.name]->asTexture(); 126 | var[channel.texname] = pTex; 127 | }; 128 | for (const auto& channel : kOutputChannels) bind(channel); 129 | 130 | mpShader->execute(pRenderContext, mFrameDim.x, mFrameDim.y); 131 | } 132 | 133 | void GlobalIlluminationPass::renderUI(Gui::Widgets& widget) 134 | { 135 | } 136 | 137 | void GlobalIlluminationPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) 138 | { 139 | mpScene = pScene; 140 | mpShader = nullptr; 141 | } 142 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #pragma once 29 | #include "Falcor.h" 30 | 31 | using namespace Falcor; 32 | 33 | class GlobalIlluminationPass : public RenderPass 34 | { 35 | public: 36 | using SharedPtr = std::shared_ptr; 37 | 38 | static const Info kInfo; 39 | 40 | /** Create a new render pass object. 41 | \param[in] pRenderContext The render context. 42 | \param[in] dict Dictionary of serialized parameters. 43 | \return A new object, or an exception is thrown if creation failed. 44 | */ 45 | static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); 46 | 47 | virtual Dictionary getScriptingDictionary() override; 48 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 49 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; 50 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 51 | virtual void renderUI(Gui::Widgets& widget) override; 52 | virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; 53 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 54 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 55 | 56 | private: 57 | GlobalIlluminationPass() : RenderPass(kInfo) {} 58 | 59 | Scene::SharedPtr mpScene; 60 | uint2 mFrameDim = uint2(0); 61 | 62 | ComputePass::SharedPtr mpShader; 63 | }; 64 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {38693E67-EB25-445E-9781-E000DD87001C} 15 | Win32Proj 16 | GlobalIlluminationPass 17 | 10.0.19041.0 18 | GlobalIlluminationPass 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {2c535635-e4c5-4098-a928-574f0e7cd5f9} 36 | 37 | 38 | 39 | 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v142 45 | Unicode 46 | Shaders\RenderPasses\$(ProjectName) 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | Shaders\RenderPasses\$(ProjectName) 55 | 56 | 57 | 58 | 59 | 60 | 61 | true 62 | 63 | 64 | false 65 | 66 | 67 | 68 | 69 | 70 | Level3 71 | Disabled 72 | PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) 73 | true 74 | true 75 | stdcpp17 76 | 77 | 78 | Windows 79 | true 80 | 81 | 82 | 83 | 84 | Level3 85 | 86 | 87 | MaxSpeed 88 | true 89 | true 90 | PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) 91 | true 92 | true 93 | stdcpp17 94 | 95 | 96 | Windows 97 | true 98 | true 99 | true 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RenderPasses/GlobalIlluminationPass/GlobalIlluminationPass.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/FinalShading.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import LoadShadingData; 11 | import Scene.RaytracingInline; 12 | //import Rendering.Materials.StandardMaterial; 13 | import Rendering.Materials.Microfacet; 14 | import Rendering.ReSTIRGI.ScreenSpaceReSTIR; 15 | import Utils.Sampling.TinyUniformSampleGenerator; 16 | 17 | RWTexture2D gColor; 18 | RWTexture2D gEmission; 19 | RWTexture2D gDiffuseIllumination; 20 | RWTexture2D gDiffuseReflectance; 21 | RWTexture2D gSpecularIllumination; 22 | RWTexture2D gSpecularReflectance; 23 | RWTexture2D gDebug; 24 | 25 | #define is_valid(name) (is_valid_##name != 0) 26 | static const bool kComputeDirect = COMPUTE_DIRECT; 27 | 28 | /** Perform shading with final samples from ReSTIR. 29 | */ 30 | struct FinalShading 31 | { 32 | // Static configuration. 33 | //static const bool kUseEnvBackground = USE_ENV_BACKGROUND; 34 | 35 | Texture2D vbuffer; 36 | Texture2D vColor; 37 | 38 | //ScreenSpaceReSTIR screenSpaceReSTIR; 39 | //StandardMaterial standardMaterial; 40 | 41 | uint2 frameDim; 42 | 43 | uint numReSTIRInstances; 44 | uint ReSTIRInstanceID; 45 | 46 | void execute(uint2 pixel) 47 | { 48 | if (any(pixel >= frameDim)) return; 49 | 50 | float3 color = {}; 51 | float3 emission = {}; 52 | float3 diffuseReflectance = {}; 53 | float3 diffuseIllumination = {}; 54 | float3 specularReflectance = {}; 55 | float3 specularIllumination = {}; 56 | float hitT = 10000.f; // TODO: Remove magic number also used in SpatioTemporalResampling pass. 57 | 58 | ShadingData sd; 59 | let lod = ExplicitLodTextureSampler(0.f); // TODO: Implement texture level-of-detail. 60 | if (loadShadingData(pixel, frameDim, gScene.camera, vbuffer, lod, sd)) 61 | { 62 | // Create BSDF instance and query its properties. 63 | let bsdf = gScene.materials.getBSDF(sd, lod); 64 | let bsdfProperties = bsdf.getProperties(sd); 65 | 66 | // Get final sample from ReSTIR. 67 | float3 dir; 68 | float distance; 69 | float3 Li; 70 | uint baseIndex = 0; 71 | uint sampleIndex = 0; 72 | uint elementCount = 0; 73 | 74 | float3 creationPoint; 75 | float3 creationNormal; 76 | float3 samplePosition; 77 | float3 sampleNormal; 78 | float3 sampleRadiance; 79 | 80 | float3 vcolor; 81 | //if (kComputeDirect) 82 | //{ 83 | vcolor = float3(vColor[pixel].x, vColor[pixel].y, vColor[pixel].z); 84 | //} 85 | 86 | float avgWeight; 87 | gScreenSpaceReSTIR.getGIFinalSample(pixel, 0, creationPoint, creationNormal, samplePosition, sampleNormal, sampleRadiance, avgWeight); 88 | 89 | // Evaluate diffuse and specular lobes. 90 | //if (valid) 91 | //{ 92 | //TinyUniformSampleGenerator sg = TinyUniformSampleGenerator(pixel, gScreenSpaceReSTIR.frameIndex); 93 | color = avgWeight * sampleRadiance; 94 | //color = samplePosition; 95 | //if (kComputeDirect) 96 | //{ 97 | // color += vcolor; 98 | //} 99 | 100 | //} 101 | 102 | //float3 diffuse = {}; 103 | //float3 specular = {}; 104 | //if (valid) 105 | //{ 106 | // TinyUniformSampleGenerator sg = TinyUniformSampleGenerator(pixel, gScreenSpaceReSTIR.frameIndex); 107 | // 108 | // sd.mtl.setActiveLobes((uint)LobeType::DiffuseReflection); 109 | // diffuse = bsdf.eval(sd, dir, sg) * Li; 110 | // 111 | // sd.mtl.setActiveLobes((uint)LobeType::SpecularReflection); 112 | // specular = bsdf.eval(sd, dir, sg) * Li; 113 | // 114 | // hitT = distance; 115 | //} 116 | // 117 | //emission = bsdfProperties.emission; 118 | //color = diffuse + specular + emission; 119 | 120 | // Demodulate diffuse reflectance (albedo) from diffuse color. 121 | diffuseReflectance = bsdfProperties.diffuseReflectionAlbedo; 122 | float3 diffuseFactor = diffuseReflectance <= 0.f ? 0.f : 1.f / diffuseReflectance; 123 | diffuseIllumination = color * diffuseFactor; 124 | 125 | // Demodulate preintegrated specular reflectance from specular color. 126 | /* 127 | specularReflectance += approxSpecularIntegralGGX(sd.specular, sd.linearRoughness * sd.linearRoughness, sd.NdotV); 128 | float3 specularFactor = specularReflectance <= 0.f ? 0.f : 1.f / specularReflectance; 129 | specularIllumination += specular * specularFactor; 130 | */ 131 | // Demodulate preintegrated specular reflectance from specular color. 132 | float NdotV = saturate(dot(sd.N, sd.V)); 133 | float ggxAlpha = bsdfProperties.roughness * bsdfProperties.roughness; 134 | specularReflectance = approxSpecularIntegralGGX(bsdfProperties.specularReflectance, ggxAlpha, NdotV); 135 | float3 specularFactor = specularReflectance <= 0.f ? 0.f : 1.f / specularReflectance; 136 | specularIllumination = color * specularFactor; 137 | } 138 | else 139 | { 140 | //if (kUseEnvBackground) 141 | //{ 142 | // float3 dir = gScene.camera.computeRayPinhole(pixel, frameDim).dir; 143 | // emission = gScene.envMap.eval(dir); 144 | // color = emission; 145 | //} 146 | } 147 | 148 | // Write active outputs. 149 | 150 | if (ReSTIRInstanceID == 0) 151 | { 152 | if (is_valid(gColor)) gColor[pixel] = float4(color, 1.f) / numReSTIRInstances; 153 | if (is_valid(gEmission)) gEmission[pixel] = float4(emission, 1.f) / numReSTIRInstances; 154 | if (is_valid(gDiffuseReflectance)) gDiffuseReflectance[pixel] = float4(diffuseReflectance, 1.f) / numReSTIRInstances; 155 | if (is_valid(gDiffuseIllumination)) gDiffuseIllumination[pixel] = float4(diffuseIllumination, hitT) / numReSTIRInstances; 156 | if (is_valid(gSpecularReflectance)) gSpecularReflectance[pixel] = float4(specularReflectance, 1.f) / numReSTIRInstances; 157 | if (is_valid(gSpecularIllumination)) gSpecularIllumination[pixel] = float4(specularIllumination, hitT) / numReSTIRInstances; 158 | } 159 | else 160 | { 161 | if (is_valid(gColor)) gColor[pixel] += float4(color, 1.f) / numReSTIRInstances; 162 | if (is_valid(gEmission)) gEmission[pixel] += float4(emission, 1.f) / numReSTIRInstances; 163 | if (is_valid(gDiffuseReflectance)) gDiffuseReflectance[pixel] += float4(diffuseReflectance, 1.f) / numReSTIRInstances; 164 | if (is_valid(gDiffuseIllumination)) gDiffuseIllumination[pixel] += float4(diffuseIllumination, hitT) / numReSTIRInstances; 165 | if (is_valid(gSpecularReflectance)) gSpecularReflectance[pixel] += float4(specularReflectance, 1.f) / numReSTIRInstances; 166 | if (is_valid(gSpecularIllumination)) gSpecularIllumination[pixel] += float4(specularIllumination, hitT) / numReSTIRInstances; 167 | } 168 | } 169 | }; 170 | 171 | ParameterBlock gFinalShading; 172 | //cbuffer CB 173 | //{ 174 | // FinalShading gFinalShading; 175 | //}; 176 | 177 | [numthreads(16, 16, 1)] 178 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 179 | { 180 | gFinalShading.execute(dispatchThreadId.xy); 181 | } 182 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/InitialSampling.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import Utils.Color.ColorHelpers; 11 | import Rendering.ReSTIRGI.ScreenSpaceReSTIR; 12 | 13 | /** Prepares ReSTIR samples 14 | */ 15 | struct InitialSampling 16 | { 17 | Texture2D vPosW; 18 | Texture2D vNormW; 19 | Texture2D sPosW; 20 | Texture2D sNormW; 21 | Texture2D sColor; 22 | Texture2D random; 23 | 24 | //ScreenSpaceReSTIR screenSpaceReSTIR; 25 | 26 | uint2 frameDim; 27 | 28 | void execute(uint2 pixel) 29 | { 30 | if (any(pixel >= frameDim)) return; 31 | 32 | gScreenSpaceReSTIR.setGIInitialSample(pixel, vPosW[pixel].xyz, vNormW[pixel].xyz, sPosW[pixel].xyz, sNormW[pixel].xyz, sColor[pixel].xyz, random[pixel]); 33 | } 34 | }; 35 | 36 | ParameterBlock gInitialSampling; 37 | //cbuffer CB 38 | //{ 39 | // PrepareSurfaceData gPrepareSurfaceData; 40 | //}; 41 | 42 | [numthreads(16, 16, 1)] 43 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 44 | { 45 | gInitialSampling.execute(dispatchThreadId.xy); 46 | } 47 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/LoadShadingData.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | __exported import Scene.Scene; 29 | __exported import Scene.Shading; 30 | __exported import Scene.HitInfo; 31 | import Scene.Material.ShadingUtils; 32 | import Utils.Math.MathHelpers; 33 | 34 | /** Helper for setting up the ShadingData struct based on loaded data. 35 | \param[in] pixel Current pixel coordinates. 36 | \param[in] frameDim Frame dimensions in pixel. 37 | \param[in] camera Current camera. 38 | \param[in] vbuffer VBuffer texture. 39 | \param[in] lod Method for computing texture level-of-detail. 40 | \param[out] sd ShadingData struct. 41 | \return True if the pixel has valid data (not a background pixel). Note sd.V is always valid. 42 | */ 43 | bool loadShadingData(const uint2 pixel, const uint2 frameDim, const Camera camera, Texture2D vbuffer, const ITextureSampler lod, out ShadingData sd) 44 | { 45 | sd = {}; 46 | 47 | const float3 rayDir = camera.computeRayPinhole(pixel, frameDim).dir; 48 | bool valid = false; 49 | 50 | const HitInfo hit = HitInfo(vbuffer[pixel]); 51 | if (hit.isValid() && hit.getType() == HitType::Triangle) 52 | { 53 | const TriangleHit triangleHit = hit.getTriangleHit(); 54 | 55 | // Evaluate Falcor's material parameters at the hit point. 56 | // TODO: Implement texLOD to enable texture filtering in prepareShadingData(). 57 | const VertexData v = gScene.getVertexData(triangleHit); 58 | const uint materialID = gScene.getMaterialID(triangleHit.instanceID); 59 | sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); 60 | 61 | // Adjust shading normals if GBuffer pass has flag enabled. 62 | #if GBUFFER_ADJUST_SHADING_NORMALS 63 | adjustShadingNormal(sd, v); 64 | #endif 65 | valid = true; 66 | } 67 | 68 | sd.V = -rayDir; 69 | return valid; 70 | } 71 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/PrepareSurfaceData.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import LoadShadingData; 11 | import Utils.Color.ColorHelpers; 12 | import Rendering.ReSTIRGI.ScreenSpaceReSTIR; 13 | 14 | /** Prepares ReSTIR surface data. 15 | */ 16 | struct PrepareSurfaceData 17 | { 18 | Texture2D vbuffer; 19 | Texture2D texGrads; 20 | 21 | //ScreenSpaceReSTIR screenSpaceReSTIR; 22 | 23 | uint2 frameDim; 24 | 25 | void execute(uint2 pixel) 26 | { 27 | if (any(pixel >= frameDim)) return; 28 | 29 | ShadingData sd; 30 | let lod = ExplicitLodTextureSampler(0.f); // TODO: Implement texture level-of-detail. 31 | bool isValidSurface = loadShadingData(pixel, frameDim, gScene.camera, vbuffer, lod, sd); 32 | 33 | if (isValidSurface) 34 | { 35 | let bsdf = gScene.materials.getBSDF(sd, lod); 36 | let bsdfProperties = bsdf.getProperties(sd); 37 | 38 | float depth = distance(gScene.camera.getPosition(), sd.posW); 39 | float diffuseWeight = luminance(bsdfProperties.diffuseReflectionAlbedo); 40 | float specularWeight = luminance(bsdfProperties.specularReflectance); 41 | gScreenSpaceReSTIR.setSurfaceData(pixel, sd.computeNewRayOrigin(), depth, sd.N, sd.faceN, diffuseWeight, specularWeight, bsdfProperties.roughness); 42 | } 43 | else 44 | { 45 | gScreenSpaceReSTIR.setInvalidSurfaceData(pixel); 46 | } 47 | } 48 | }; 49 | 50 | ParameterBlock gPrepareSurfaceData; 51 | //cbuffer CB 52 | //{ 53 | // PrepareSurfaceData gPrepareSurfaceData; 54 | //}; 55 | 56 | [numthreads(16, 16, 1)] 57 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 58 | { 59 | gPrepareSurfaceData.execute(dispatchThreadId.xy); 60 | } 61 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/ReSTIRGI.vcxproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {625AAE5D-5E0D-4232-916D-AA9210A0056D} 15 | Win32Proj 16 | ReSTIRGI 17 | 10.0.19041.0 18 | ReSTIRGIPass 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {2c535635-e4c5-4098-a928-574f0e7cd5f9} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | DynamicLibrary 46 | true 47 | v142 48 | Unicode 49 | Shaders\RenderPasses\$(ProjectName) 50 | 51 | 52 | DynamicLibrary 53 | false 54 | v142 55 | true 56 | Unicode 57 | Shaders\RenderPasses\$(ProjectName) 58 | 59 | 60 | 61 | 62 | 63 | 64 | true 65 | 66 | 67 | false 68 | 69 | 70 | 71 | 72 | 73 | Level3 74 | Disabled 75 | PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) 76 | true 77 | true 78 | stdcpp17 79 | %(AdditionalIncludeDirectories) 80 | 81 | 82 | Windows 83 | true 84 | 85 | 86 | 87 | 88 | Level3 89 | 90 | 91 | MaxSpeed 92 | true 93 | true 94 | PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) 95 | true 96 | true 97 | stdcpp17 98 | %(AdditionalIncludeDirectories) 99 | 100 | 101 | Windows 102 | true 103 | true 104 | true 105 | 106 | 107 | 108 | 109 | 110 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/ReSTIRGI.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/ReSTIRGI.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/ReSTIRGIPass.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #include "ReSTIRGIPass.h" 29 | 30 | const RenderPass::Info ReSTIRGIPass::kInfo { "ReSTIRGIPass", "Standalone pass for indirect lighting using ReSTIR GI." }; 31 | 32 | namespace 33 | { 34 | // Shader location 35 | const std::string kPrepareSurfaceDataFile = "RenderPasses/ReSTIRGIPass/PrepareSurfaceData.cs.slang"; 36 | #if INITIAL_SAMPLING 37 | const std::string kInitialSamplingFile = "RenderPasses/ReSTIRGIPass/InitialSampling.cs.slang"; 38 | #endif 39 | const std::string kFinalShadingFile = "RenderPasses/ReSTIRGIPass/FinalShading.cs.slang"; 40 | 41 | const std::string kShaderModel = "6_5"; 42 | 43 | const std::string kInputVBuffer = "vbuffer"; 44 | const std::string kInputMotionVectors = "motionVectors"; 45 | #if INITIAL_SAMPLING 46 | const std::string kInputVPosW = "vPosW"; 47 | const std::string kInputVNormW = "vNormW"; 48 | const std::string kInputVColor = "vColor"; 49 | const std::string kInputSPosW = "sPosW"; 50 | const std::string kInputSNormW = "sNormW"; 51 | const std::string kInputSColor = "sColor"; 52 | const std::string kInputRandom = "random"; 53 | #endif 54 | 55 | const Falcor::ChannelList kInputChannels = 56 | { 57 | //{ kInputVPosW, "gVPosW", "Visibile point position" }, 58 | //{ kInputVNormW, "gVNormW", "Visibile point normal" }, 59 | //{ kInputSPosW, "gSPosW", "Sample point position" }, 60 | //{ kInputSNormW, "gSNormW", "Sample point normal" }, 61 | //{ kInputSColor, "gSColor", "Sample point radiance" }, 62 | { kInputVBuffer, "gVBuffer", "Visibility buffer in packed format", false, ResourceFormat::Unknown }, 63 | { kInputMotionVectors, "gMotionVectors", "Motion vector buffer (float format)", true /* optional */, ResourceFormat::RG32Float }, 64 | #if INITIAL_SAMPLING 65 | { kInputVPosW, "gVPosW", "Visible point", false, ResourceFormat::RGBA32Float }, 66 | { kInputVNormW, "gVNormW", "Visible surface normal", false, ResourceFormat::RGBA32Float }, 67 | { kInputVColor, "gVColor", "Outgoing radiance at visible point in RGBA", false, ResourceFormat::RGBA32Float }, 68 | { kInputSPosW, "gSPosW", "Sample point", false, ResourceFormat::RGBA32Float }, 69 | { kInputSNormW, "gSNormW", "Sample surface normal", false, ResourceFormat::RGBA32Float }, 70 | { "sColor", "gSColor", "Outgoing radiance at sample point in RGBA", false, ResourceFormat::RGBA32Float }, 71 | { kInputRandom, "gRandom", "Random numbers used for path", false, ResourceFormat::R32Float }, 72 | #endif 73 | }; 74 | 75 | const Falcor::ChannelList kOutputChannels = 76 | { 77 | //{ "color", "gColor", "Final color", true /* optional */, ResourceFormat::RGBA32Float }, 78 | { "color", "gColor", "Final color", true /* optional */, ResourceFormat::RGBA32Float }, 79 | { "emission", "gEmission", "Emissive color", true /* optional */, ResourceFormat::RGBA32Float }, 80 | { "diffuseIllumination", "gDiffuseIllumination", "Diffuse illumination", true /* optional */, ResourceFormat::RGBA32Float }, 81 | { "diffuseReflectance", "gDiffuseReflectance", "Diffuse reflectance", true /* optional */, ResourceFormat::RGBA32Float }, 82 | { "specularIllumination", "gSpecularIllumination", "Specular illumination", true /* optional */, ResourceFormat::RGBA32Float }, 83 | { "specularReflectance", "gSpecularReflectance", "Specular reflectance", true /* optional */, ResourceFormat::RGBA32Float }, 84 | { "debug", "gDebug", "Debug output", true /* optional */, ResourceFormat::RGBA32Float }, 85 | }; 86 | 87 | // Scripting options. 88 | const char* kOptions = "options"; 89 | const char* kNumReSTIRInstances = "NumReSTIRInstances"; 90 | const char kComputeDirect[] = "computeDirect"; 91 | } 92 | 93 | // Don't remove this. it's required for hot-reload to function properly 94 | extern "C" FALCOR_API_EXPORT const char* getProjDir() 95 | { 96 | return PROJECT_DIR; 97 | } 98 | 99 | extern "C" FALCOR_API_EXPORT void getPasses(Falcor::RenderPassLibrary& lib) 100 | { 101 | lib.registerPass(ReSTIRGIPass::kInfo, ReSTIRGIPass::create); 102 | } 103 | 104 | ReSTIRGIPass::SharedPtr ReSTIRGIPass::create(RenderContext* pRenderContext, const Dictionary& dict) 105 | { 106 | SharedPtr pPass = SharedPtr(new ReSTIRGIPass(dict)); 107 | return pPass; 108 | } 109 | 110 | Dictionary ReSTIRGIPass::getScriptingDictionary() 111 | { 112 | Dictionary d; 113 | d[kOptions] = mOptions;// mpScreenSpaceReSTIR->getOptions(); 114 | d[kNumReSTIRInstances] = mNumReSTIRInstances; 115 | d[kComputeDirect] = mComputeDirect; 116 | return d; 117 | } 118 | 119 | RenderPassReflection ReSTIRGIPass::reflect(const CompileData& compileData) 120 | { 121 | // Define the required resources here 122 | RenderPassReflection reflector; 123 | //reflector.addOutput("dst"); 124 | //reflector.addInput("src"); 125 | addRenderPassOutputs(reflector, kOutputChannels); 126 | addRenderPassInputs(reflector, kInputChannels); 127 | 128 | return reflector; 129 | } 130 | 131 | void ReSTIRGIPass::compile(RenderContext* pRenderContext, const CompileData& compileData) 132 | { 133 | mFrameDim = compileData.defaultTexDims; 134 | } 135 | 136 | void ReSTIRGIPass::execute(RenderContext* pRenderContext, const RenderData& renderData) 137 | { 138 | if (mNeedRecreateReSTIRInstances) 139 | { 140 | setScene(pRenderContext, mpScene); 141 | } 142 | 143 | // Clear outputs if no scene is loaded. 144 | if (!mpScene) 145 | { 146 | clearRenderPassChannels(pRenderContext, kOutputChannels, renderData); 147 | return; 148 | } 149 | 150 | const auto& pVBuffer = renderData[kInputVBuffer]->asTexture(); 151 | const auto& pMotionVectors = renderData[kInputMotionVectors]->asTexture(); 152 | #if INITIAL_SAMPLING 153 | const auto& pVPosW = renderData[kInputVPosW]->asTexture(); 154 | const auto& pVNormW = renderData[kInputVNormW]->asTexture(); 155 | const auto& pVColor = renderData[kInputVColor]->asTexture(); 156 | const auto& pSPosW = renderData[kInputSPosW]->asTexture(); 157 | const auto& pSNormW = renderData[kInputSNormW]->asTexture(); 158 | const auto& pSColor = renderData[kInputSColor]->asTexture(); 159 | const auto& pRandom = renderData[kInputRandom]->asTexture(); 160 | #endif 161 | 162 | // Clear outputs if ReSTIR module is not initialized. 163 | if (mpScreenSpaceReSTIR.empty()) 164 | { 165 | auto clear = [&](const ChannelDesc& channel) 166 | { 167 | auto pTex = renderData[channel.name]->asTexture(); 168 | if (pTex) 169 | { 170 | pRenderContext->clearUAV(pTex->getUAV().get(), float4(0.f)); 171 | } 172 | }; 173 | for (const auto& channel : kOutputChannels) 174 | { 175 | clear(channel); 176 | } 177 | return; 178 | } 179 | 180 | auto& dict = renderData.getDictionary(); 181 | 182 | // Update refresh flag if changes that affect the output have occured. 183 | if (mOptionsChanged) 184 | { 185 | auto flags = dict.getValue(kRenderPassRefreshFlags, Falcor::RenderPassRefreshFlags::None); 186 | flags |= Falcor::RenderPassRefreshFlags::RenderOptionsChanged; 187 | dict[Falcor::kRenderPassRefreshFlags] = flags; 188 | mOptionsChanged = false; 189 | } 190 | 191 | // Check if GBuffer has adjusted shading normals enabled. 192 | mGBufferAdjustShadingNormals = dict.getValue(Falcor::kRenderPassGBufferAdjustShadingNormals, false); 193 | 194 | for (size_t i = 0; i < mpScreenSpaceReSTIR.size(); ++i) 195 | { 196 | mpScreenSpaceReSTIR[i]->beginFrame(pRenderContext, mFrameDim); 197 | 198 | prepareSurfaceData(pRenderContext, pVBuffer, i); 199 | 200 | #if INITIAL_SAMPLING 201 | initialSample(pRenderContext, pVPosW, pVNormW, pSPosW, pSNormW, pSColor, pRandom, i); 202 | #endif 203 | 204 | mpScreenSpaceReSTIR[i]->updateReSTIRGI(pRenderContext, pMotionVectors); 205 | 206 | finalShading(pRenderContext, pVBuffer, pVColor, renderData, i); 207 | 208 | mpScreenSpaceReSTIR[i]->endFrame(pRenderContext); 209 | } 210 | 211 | auto copyTexture = [pRenderContext](Texture* pDst, const Texture* pSrc) 212 | { 213 | if (pDst && pSrc) 214 | { 215 | assert(pDst && pSrc); 216 | assert(pDst->getFormat() == pSrc->getFormat()); 217 | assert(pDst->getWidth() == pSrc->getWidth() && pDst->getHeight() == pSrc->getHeight()); 218 | pRenderContext->copyResource(pDst, pSrc); 219 | } 220 | else if (pDst) 221 | { 222 | pRenderContext->clearUAV(pDst->getUAV().get(), uint4(0, 0, 0, 0)); 223 | } 224 | }; 225 | // Copy debug output if available. (only support first ReSTIR instance for now) 226 | //if (const auto& pDebug = renderData["debug"]->asTexture()) 227 | //{ 228 | // copyTexture(pDebug.get(), mpScreenSpaceReSTIR[0]->getDebugOutputTexture().get()); 229 | //} 230 | } 231 | 232 | void ReSTIRGIPass::renderUI(Gui::Widgets& widget) 233 | { 234 | mNeedRecreateReSTIRInstances = widget.var("Num ReSTIR Instances", mNumReSTIRInstances, 1, 8); 235 | 236 | bool dirty = false; 237 | 238 | dirty |= widget.checkbox("Evaluate direct illumination", mComputeDirect); 239 | widget.tooltip("Compute direct illumination.\nIf disabled only indirect is computed (when max bounces > 0).", true); 240 | 241 | if (!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[0]) 242 | { 243 | mOptionsChanged = mpScreenSpaceReSTIR[0]->renderUI(widget); 244 | for (size_t i = 1; i < mpScreenSpaceReSTIR.size(); ++i) 245 | { 246 | mpScreenSpaceReSTIR[i]->copyRecompileStateFromOtherInstance(mpScreenSpaceReSTIR[0]); 247 | 248 | if (mOptionsChanged || dirty) 249 | { 250 | mOptions = mpScreenSpaceReSTIR[i]->getOptions(); 251 | } 252 | } 253 | } 254 | 255 | 256 | if (dirty) 257 | { 258 | mOptionsChanged = true; 259 | } 260 | } 261 | 262 | void ReSTIRGIPass::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) 263 | { 264 | mpScene = pScene; 265 | mpPrepareSurfaceData = nullptr; 266 | mpFinalShading = nullptr; 267 | 268 | if (!mpScreenSpaceReSTIR.empty()) 269 | { 270 | //mOptions = mpScreenSpaceReSTIR->getOptions(); 271 | mpScreenSpaceReSTIR.clear(); 272 | } 273 | 274 | if (mpScene) 275 | { 276 | if (pScene->hasProceduralGeometry()) 277 | { 278 | logError("This render pass does not support procedural primitives such as curves."); 279 | } 280 | 281 | mpScreenSpaceReSTIR.resize(mNumReSTIRInstances); 282 | for (int i = 0; i < mNumReSTIRInstances; i++) 283 | { 284 | mpScreenSpaceReSTIR[i] = ScreenSpaceReSTIR::create(mpScene, mOptions, mNumReSTIRInstances, i); 285 | } 286 | } 287 | } 288 | 289 | bool ReSTIRGIPass::onMouseEvent(const MouseEvent& mouseEvent) 290 | { 291 | return !mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[0] ? mpScreenSpaceReSTIR[0]->getPixelDebug()->onMouseEvent(mouseEvent) : false; 292 | } 293 | 294 | ReSTIRGIPass::ReSTIRGIPass(const Dictionary& dict) 295 | : RenderPass(kInfo) 296 | { 297 | parseDictionary(dict); 298 | } 299 | 300 | void ReSTIRGIPass::parseDictionary(const Dictionary& dict) 301 | { 302 | for (const auto& [key, value] : dict) 303 | { 304 | if (key == kOptions) 305 | { 306 | mOptions = value; 307 | } 308 | else if (key == kNumReSTIRInstances) 309 | { 310 | mNumReSTIRInstances = value; 311 | } 312 | else if (key == kComputeDirect) 313 | { 314 | mComputeDirect = value; 315 | } 316 | else 317 | { 318 | logWarning("Unknown field '" + key + "' in ScreenSpaceReSTIRPass dictionary"); 319 | } 320 | } 321 | 322 | if (!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[0]) 323 | { 324 | mpScreenSpaceReSTIR[0]->mOptions = mOptions; 325 | } 326 | } 327 | 328 | void ReSTIRGIPass::prepareSurfaceData(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, size_t instanceID) 329 | { 330 | assert(!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[instanceID]); 331 | 332 | FALCOR_PROFILE("prepareSurfaceData"); 333 | 334 | if (!mpPrepareSurfaceData) 335 | { 336 | Program::Desc desc; 337 | desc.addShaderLibrary(kPrepareSurfaceDataFile).setShaderModel(kShaderModel).csEntry("main"); 338 | desc.addTypeConformances(mpScene->getTypeConformances()); 339 | 340 | auto defines = mpScene->getSceneDefines(); 341 | defines.add("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 342 | mpPrepareSurfaceData = ComputePass::create(desc, defines, false); 343 | mpPrepareSurfaceData->setVars(nullptr); 344 | } 345 | 346 | mpPrepareSurfaceData->addDefine("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 347 | 348 | mpPrepareSurfaceData["gScene"] = mpScene->getParameterBlock(); 349 | 350 | auto var = mpPrepareSurfaceData["gPrepareSurfaceData"]; 351 | //auto var = mpPrepareSurfaceData["CB"]["gPrepareSurfaceData"]; 352 | 353 | var["vbuffer"] = pVBuffer; 354 | var["frameDim"] = mFrameDim; 355 | //mpScreenSpaceReSTIR[instanceID]->setShaderData(var["screenSpaceReSTIR"]); 356 | mpScreenSpaceReSTIR[instanceID]->setShaderDataRoot(mpPrepareSurfaceData->getRootVar()); 357 | 358 | if (instanceID == 0 && mpFinalShading && mpScreenSpaceReSTIR[0]->mRequestParentRecompile) 359 | { 360 | mpFinalShading->setVars(nullptr); 361 | mpScreenSpaceReSTIR[0]->mRequestParentRecompile = false; 362 | } 363 | 364 | mpPrepareSurfaceData->execute(pRenderContext, mFrameDim.x, mFrameDim.y); 365 | } 366 | 367 | #if INITIAL_SAMPLING 368 | void ReSTIRGIPass::initialSample( 369 | RenderContext* pRenderContext, 370 | const Texture::SharedPtr& pVPosW, 371 | const Texture::SharedPtr& pVNormW, 372 | const Texture::SharedPtr& pSPosW, 373 | const Texture::SharedPtr& pSNormW, 374 | const Texture::SharedPtr& pSColor, 375 | const Texture::SharedPtr& pRandom, 376 | size_t instanceID 377 | ) 378 | { 379 | assert(!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[instanceID]); 380 | 381 | FALCOR_PROFILE("initialSample"); 382 | 383 | if (!mpInitialSampling) 384 | { 385 | Program::Desc desc; 386 | desc.addShaderLibrary(kInitialSamplingFile).setShaderModel(kShaderModel).csEntry("main"); 387 | desc.addTypeConformances(mpScene->getTypeConformances()); 388 | 389 | auto defines = mpScene->getSceneDefines(); 390 | defines.add("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 391 | mpInitialSampling = ComputePass::create(desc, defines, false); 392 | mpInitialSampling->setVars(nullptr); 393 | } 394 | 395 | mpInitialSampling->addDefine("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 396 | 397 | mpInitialSampling["gScene"] = mpScene->getParameterBlock(); 398 | 399 | auto var = mpInitialSampling["gInitialSampling"]; 400 | //auto var = mpPrepareSurfaceData["CB"]["gPrepareSurfaceData"]; 401 | 402 | var["vPosW"] = pVPosW; 403 | var["vNormW"] = pVNormW; 404 | var["sPosW"] = pSPosW; 405 | var["sNormW"] = pSNormW; 406 | var["sColor"] = pSColor; 407 | var["random"] = pRandom; 408 | var["frameDim"] = mFrameDim; 409 | //mpScreenSpaceReSTIR[instanceID]->setShaderData(var["screenSpaceReSTIR"]); 410 | mpScreenSpaceReSTIR[instanceID]->setShaderDataRoot(mpInitialSampling->getRootVar()); 411 | 412 | if (instanceID == 0 && mpFinalShading && mpScreenSpaceReSTIR[0]->mRequestParentRecompile) 413 | { 414 | mpFinalShading->setVars(nullptr); 415 | mpScreenSpaceReSTIR[0]->mRequestParentRecompile = false; 416 | } 417 | 418 | mpInitialSampling->execute(pRenderContext, mFrameDim.x, mFrameDim.y); 419 | } 420 | #endif 421 | 422 | void ReSTIRGIPass::finalShading(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, const Texture::SharedPtr& pVColor, const RenderData& renderData, size_t instanceID) 423 | { 424 | assert(!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[instanceID]); 425 | 426 | FALCOR_PROFILE("finalShading"); 427 | 428 | if (!mpFinalShading) 429 | { 430 | Program::Desc desc; 431 | desc.addShaderLibrary(kFinalShadingFile).setShaderModel(kShaderModel).csEntry("main"); 432 | desc.addTypeConformances(mpScene->getTypeConformances()); 433 | 434 | auto defines = mpScene->getSceneDefines(); 435 | defines.add("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 436 | defines.add("COMPUTE_DIRECT", mComputeDirect ? "1" : "0"); 437 | //defines.add("USE_ENV_BACKGROUND", mpScene->useEnvBackground() ? "1" : "0"); 438 | defines.add(getValidResourceDefines(kOutputChannels, renderData)); 439 | mpFinalShading = ComputePass::create(desc, defines, false); 440 | mpFinalShading->setVars(nullptr); 441 | } 442 | 443 | mpFinalShading->addDefine("GBUFFER_ADJUST_SHADING_NORMALS", mGBufferAdjustShadingNormals ? "1" : "0"); 444 | //mpFinalShading->addDefine("USE_ENV_BACKGROUND", mpScene->useEnvBackground() ? "1" : "0"); 445 | mpFinalShading->addDefine("_USE_LEGACY_SHADING_CODE", "0"); 446 | 447 | // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. 448 | // TODO: This should be moved to a more general mechanism using Slang. 449 | mpFinalShading->getProgram()->addDefines(getValidResourceDefines(kOutputChannels, renderData)); 450 | 451 | mpFinalShading["gScene"] = mpScene->getParameterBlock(); 452 | 453 | auto var = mpFinalShading["gFinalShading"]; 454 | //auto var = mpFinalShading["CB"]["gFinalShading"]; 455 | 456 | var["vbuffer"] = pVBuffer; 457 | var["vColor"] = pVColor; 458 | var["frameDim"] = mFrameDim; 459 | var["numReSTIRInstances"] = mNumReSTIRInstances; 460 | var["ReSTIRInstanceID"] = instanceID; 461 | 462 | //mpScreenSpaceReSTIR[instanceID]->setShaderData(var["screenSpaceReSTIR"]); 463 | mpScreenSpaceReSTIR[instanceID]->setShaderDataRoot(mpFinalShading->getRootVar()); 464 | 465 | // Bind output channels as UAV buffers. 466 | var = mpFinalShading->getRootVar(); 467 | auto bind = [&](const ChannelDesc& channel) 468 | { 469 | Texture::SharedPtr pTex = renderData[channel.name]->asTexture(); 470 | var[channel.texname] = pTex; 471 | }; 472 | for (const auto& channel : kOutputChannels) bind(channel); 473 | 474 | mpFinalShading->execute(pRenderContext, mFrameDim.x, mFrameDim.y); 475 | } 476 | 477 | void ReSTIRGIPass::updateDict(const Dictionary& dict) 478 | { 479 | parseDictionary(dict); 480 | mOptionsChanged = true; 481 | if (!mpScreenSpaceReSTIR.empty() && mpScreenSpaceReSTIR[0]) 482 | { 483 | mpScreenSpaceReSTIR[0]->resetReservoirCount(); 484 | } 485 | } 486 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGI/ReSTIRGIPass.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #pragma once 29 | #include "Falcor.h" 30 | #include "Rendering/ReSTIRGI/ScreenSpaceReSTIR.h" 31 | 32 | #define INITIAL_SAMPLING (1) 33 | 34 | using namespace Falcor; 35 | 36 | class ReSTIRGIPass : public RenderPass 37 | { 38 | public: 39 | using SharedPtr = std::shared_ptr; 40 | 41 | static const Info kInfo; 42 | 43 | /** Create a new render pass object. 44 | \param[in] pRenderContext The render context. 45 | \param[in] dict Dictionary of serialized parameters. 46 | \return A new object, or an exception is thrown if creation failed. 47 | */ 48 | static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); 49 | 50 | virtual Dictionary getScriptingDictionary() override; 51 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 52 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override; 53 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 54 | virtual void renderUI(Gui::Widgets& widget) override; 55 | virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; 56 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override; 57 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 58 | 59 | private: 60 | ReSTIRGIPass(const Dictionary& dict); 61 | 62 | void parseDictionary(const Dictionary& dict); 63 | 64 | void prepareSurfaceData(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, size_t instanceID); 65 | #if INITIAL_SAMPLING 66 | void initialSample( 67 | RenderContext* pRenderContext, 68 | const Texture::SharedPtr& pVPosW, 69 | const Texture::SharedPtr& pVNormW, 70 | const Texture::SharedPtr& pSPosW, 71 | const Texture::SharedPtr& pSNormW, 72 | const Texture::SharedPtr& pSColor, 73 | const Texture::SharedPtr& pRandom, 74 | size_t instanceID 75 | ); 76 | #endif 77 | void finalShading(RenderContext* pRenderContext, const Texture::SharedPtr& pVBuffer, const Texture::SharedPtr& pVColor, const RenderData& renderData, size_t instanceID); 78 | 79 | void updateDict(const Dictionary& dict); 80 | 81 | // Internal state 82 | Scene::SharedPtr mpScene; 83 | std::vector mpScreenSpaceReSTIR; 84 | ScreenSpaceReSTIR::Options mOptions; 85 | bool mOptionsChanged = false; 86 | uint2 mFrameDim = uint2(0); 87 | bool mGBufferAdjustShadingNormals = false; 88 | 89 | ComputePass::SharedPtr mpPrepareSurfaceData; 90 | #if INITIAL_SAMPLING 91 | ComputePass::SharedPtr mpInitialSampling; 92 | #endif 93 | ComputePass::SharedPtr mpFinalShading; 94 | 95 | int mNumReSTIRInstances = 1; 96 | bool mNeedRecreateReSTIRInstances = false; 97 | 98 | bool mComputeDirect = true; ///< Compute direct illumination (otherwise indirect only). 99 | }; 100 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.cpp: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #include "ReSTIRGIGBuffer.h" 29 | 30 | const RenderPass::Info ReSTIRGIGBuffer::kInfo { "ReSTIRGIGBuffer", "Minimal path tracer." }; 31 | 32 | // Don't remove this. it's required for hot-reload to function properly 33 | extern "C" FALCOR_API_EXPORT const char* getProjDir() 34 | { 35 | return PROJECT_DIR; 36 | } 37 | 38 | extern "C" FALCOR_API_EXPORT void getPasses(Falcor::RenderPassLibrary& lib) 39 | { 40 | lib.registerPass(ReSTIRGIGBuffer::kInfo, ReSTIRGIGBuffer::create); 41 | } 42 | 43 | namespace 44 | { 45 | const char kShaderFile[] = "RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.rt.slang"; 46 | 47 | // Ray tracing settings that affect the traversal stack size. 48 | // These should be set as small as possible. 49 | const uint32_t kMaxPayloadSizeBytes = 88u; 50 | const uint32_t kMaxRecursionDepth = 2u; 51 | 52 | const char kInputViewDir[] = "viewW"; 53 | 54 | const ChannelList kInputChannels = 55 | { 56 | { "vbuffer", "gVBuffer", "Visibility buffer in packed format" }, 57 | { kInputViewDir, "gViewW", "World-space view direction (xyz float format)", true /* optional */ }, 58 | }; 59 | 60 | const ChannelList kOutputChannels = 61 | { 62 | //{ "color", "gOutputColor", "Output color (sum of direct and indirect)", false, ResourceFormat::RGBA32Float }, 63 | { "vPosW", "gVPosW", "Visible point", false, ResourceFormat::RGBA32Float }, 64 | { "vNormW", "gVNormW", "Visible surface normal", false, ResourceFormat::RGBA32Float }, 65 | { "vColor", "gVColor", "Outgoing radiance at visible point in RGBA", false, ResourceFormat::RGBA32Float }, 66 | { "sPosW", "gSPosW", "Sample point", false, ResourceFormat::RGBA32Float }, 67 | { "sNormW", "gSNormW", "Sample surface normal", false, ResourceFormat::RGBA32Float }, 68 | { "sColor", "gSColor", "Outgoing radiance at sample point in RGBA", false, ResourceFormat::RGBA32Float }, 69 | { "random", "gPdf", "Random numbers used for path", false, ResourceFormat::R32Float }, 70 | }; 71 | 72 | const char kMaxBounces[] = "maxBounces"; 73 | const char kUseImportanceSampling[] = "useImportanceSampling"; 74 | } 75 | 76 | ReSTIRGIGBuffer::SharedPtr ReSTIRGIGBuffer::create(RenderContext* pRenderContext, const Dictionary& dict) 77 | { 78 | SharedPtr pPass = SharedPtr(new ReSTIRGIGBuffer(dict)); 79 | return pPass; 80 | } 81 | 82 | Dictionary ReSTIRGIGBuffer::getScriptingDictionary() 83 | { 84 | Dictionary d; 85 | d[kMaxBounces] = mMaxBounces; 86 | d[kUseImportanceSampling] = mUseImportanceSampling; 87 | return d; 88 | } 89 | 90 | RenderPassReflection ReSTIRGIGBuffer::reflect(const CompileData& compileData) 91 | { 92 | RenderPassReflection reflector; 93 | 94 | // Define our input/output channels. 95 | addRenderPassInputs(reflector, kInputChannels); 96 | addRenderPassOutputs(reflector, kOutputChannels); 97 | 98 | return reflector; 99 | } 100 | 101 | void ReSTIRGIGBuffer::execute(RenderContext* pRenderContext, const RenderData& renderData) 102 | { 103 | // Update refresh flag if options that affect the output have changed. 104 | auto& dict = renderData.getDictionary(); 105 | if (mOptionsChanged) 106 | { 107 | auto flags = dict.getValue(kRenderPassRefreshFlags, RenderPassRefreshFlags::None); 108 | dict[Falcor::kRenderPassRefreshFlags] = flags | Falcor::RenderPassRefreshFlags::RenderOptionsChanged; 109 | mOptionsChanged = false; 110 | } 111 | 112 | // If we have no scene, just clear the outputs and return. 113 | if (!mpScene) 114 | { 115 | for (auto it : kOutputChannels) 116 | { 117 | Texture* pDst = renderData[it.name]->asTexture().get(); 118 | if (pDst) pRenderContext->clearTexture(pDst); 119 | } 120 | return; 121 | } 122 | 123 | if (is_set(mpScene->getUpdates(), Scene::UpdateFlags::GeometryChanged)) 124 | { 125 | throw RuntimeError("MinimalPathTracer: This render pass does not support scene geometry changes."); 126 | } 127 | 128 | // Request the light collection if emissive lights are enabled. 129 | if (mpScene->getRenderSettings().useEmissiveLights) 130 | { 131 | mpScene->getLightCollection(pRenderContext); 132 | } 133 | 134 | // Configure depth-of-field. 135 | const bool useDOF = mpScene->getCamera()->getApertureRadius() > 0.f; 136 | if (useDOF && renderData[kInputViewDir] == nullptr) 137 | { 138 | logWarning("Depth-of-field requires the '{}' input. Expect incorrect shading.", kInputViewDir); 139 | } 140 | 141 | // Specialize program. 142 | // These defines should not modify the program vars. Do not trigger program vars re-creation. 143 | mTracer.pProgram->addDefine("MAX_BOUNCES", std::to_string(mMaxBounces)); 144 | mTracer.pProgram->addDefine("USE_IMPORTANCE_SAMPLING", mUseImportanceSampling ? "1" : "0"); 145 | mTracer.pProgram->addDefine("USE_ANALYTIC_LIGHTS", mpScene->useAnalyticLights() ? "1" : "0"); 146 | mTracer.pProgram->addDefine("USE_EMISSIVE_LIGHTS", mpScene->useEmissiveLights() ? "1" : "0"); 147 | mTracer.pProgram->addDefine("USE_ENV_LIGHT", mpScene->useEnvLight() ? "1" : "0"); 148 | mTracer.pProgram->addDefine("USE_ENV_BACKGROUND", mpScene->useEnvBackground() ? "1" : "0"); 149 | 150 | // For optional I/O resources, set 'is_valid_' defines to inform the program of which ones it can access. 151 | // TODO: This should be moved to a more general mechanism using Slang. 152 | mTracer.pProgram->addDefines(getValidResourceDefines(kInputChannels, renderData)); 153 | mTracer.pProgram->addDefines(getValidResourceDefines(kOutputChannels, renderData)); 154 | 155 | // Prepare program vars. This may trigger shader compilation. 156 | // The program should have all necessary defines set at this point. 157 | if (!mTracer.pVars) 158 | { 159 | prepareVars(); 160 | } 161 | FALCOR_ASSERT(mTracer.pVars); 162 | 163 | // Set constants. 164 | auto var = mTracer.pVars->getRootVar(); 165 | var["CB"]["gFrameCount"] = mFrameCount; 166 | var["CB"]["gPRNGDimension"] = dict.keyExists(kRenderPassPRNGDimension) ? dict[kRenderPassPRNGDimension] : 0u; 167 | 168 | // Bind I/O buffers. These needs to be done per-frame as the buffers may change anytime. 169 | auto bind = [&](const ChannelDesc& desc) 170 | { 171 | if (!desc.texname.empty()) 172 | { 173 | var[desc.texname] = renderData[desc.name]->asTexture(); 174 | } 175 | }; 176 | for (auto channel : kInputChannels) bind(channel); 177 | for (auto channel : kOutputChannels) bind(channel); 178 | 179 | // Get dimensions of ray dispatch. 180 | const uint2 targetDim = renderData.getDefaultTextureDims(); 181 | FALCOR_ASSERT(targetDim.x > 0 && targetDim.y > 0); 182 | 183 | // Spawn the rays. 184 | mpScene->raytrace(pRenderContext, mTracer.pProgram.get(), mTracer.pVars, uint3(targetDim, 1)); 185 | 186 | mFrameCount++; 187 | } 188 | 189 | void ReSTIRGIGBuffer::renderUI(Gui::Widgets& widget) 190 | { 191 | bool dirty = false; 192 | 193 | dirty |= widget.var("Max bounces", mMaxBounces, 0u, 1u << 16); 194 | widget.tooltip("Maximum path length for indirect illumination.\n0 = direct only\n1 = one indirect bounce etc.", true); 195 | 196 | dirty |= widget.checkbox("Use importance sampling", mUseImportanceSampling); 197 | widget.tooltip("Use importance sampling for materials", true); 198 | 199 | // If rendering options that modify the output have changed, set flag to indicate that. 200 | // In execute() we will pass the flag to other passes for reset of temporal data etc. 201 | if (dirty) 202 | { 203 | mOptionsChanged = true; 204 | } 205 | } 206 | 207 | void ReSTIRGIGBuffer::setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) 208 | { 209 | // Clear data for previous scene. 210 | // After changing scene, the raytracing program should to be recreated. 211 | mTracer.pProgram = nullptr; 212 | mTracer.pBindingTable = nullptr; 213 | mTracer.pVars = nullptr; 214 | mFrameCount = 0; 215 | 216 | // Set new scene. 217 | mpScene = pScene; 218 | 219 | if (mpScene) 220 | { 221 | if (pScene->hasGeometryType(Scene::GeometryType::Custom)) 222 | { 223 | logWarning("MinimalPathTracer: This render pass does not support custom primitives."); 224 | } 225 | 226 | // Create ray tracing program. 227 | RtProgram::Desc desc; 228 | desc.addShaderLibrary(kShaderFile); 229 | desc.setMaxPayloadSize(kMaxPayloadSizeBytes); 230 | desc.setMaxAttributeSize(mpScene->getRaytracingMaxAttributeSize()); 231 | desc.setMaxTraceRecursionDepth(kMaxRecursionDepth); 232 | 233 | mTracer.pBindingTable = RtBindingTable::create(2, 2, mpScene->getGeometryCount()); 234 | auto& sbt = mTracer.pBindingTable; 235 | sbt->setRayGen(desc.addRayGen("rayGen")); 236 | sbt->setMiss(0, desc.addMiss("scatterMiss")); 237 | sbt->setMiss(1, desc.addMiss("shadowMiss")); 238 | 239 | if (mpScene->hasGeometryType(Scene::GeometryType::TriangleMesh)) 240 | { 241 | sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("scatterTriangleMeshClosestHit", "scatterTriangleMeshAnyHit")); 242 | sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("", "shadowTriangleMeshAnyHit")); 243 | } 244 | 245 | if (mpScene->hasGeometryType(Scene::GeometryType::DisplacedTriangleMesh)) 246 | { 247 | sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("scatterDisplacedTriangleMeshClosestHit", "", "displacedTriangleMeshIntersection")); 248 | sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::DisplacedTriangleMesh), desc.addHitGroup("", "", "displacedTriangleMeshIntersection")); 249 | } 250 | 251 | if (mpScene->hasGeometryType(Scene::GeometryType::Curve)) 252 | { 253 | sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("scatterCurveClosestHit", "", "curveIntersection")); 254 | sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::Curve), desc.addHitGroup("", "", "curveIntersection")); 255 | } 256 | 257 | if (mpScene->hasGeometryType(Scene::GeometryType::SDFGrid)) 258 | { 259 | sbt->setHitGroup(0, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("scatterSdfGridClosestHit", "", "sdfGridIntersection")); 260 | sbt->setHitGroup(1, mpScene->getGeometryIDs(Scene::GeometryType::SDFGrid), desc.addHitGroup("", "", "sdfGridIntersection")); 261 | } 262 | 263 | mTracer.pProgram = RtProgram::create(desc, mpScene->getSceneDefines()); 264 | } 265 | } 266 | 267 | ReSTIRGIGBuffer::ReSTIRGIGBuffer(const Dictionary& dict) 268 | : RenderPass(kInfo) 269 | { 270 | parseDictionary(dict); 271 | 272 | // Create a sample generator. 273 | mpSampleGenerator = SampleGenerator::create(SAMPLE_GENERATOR_UNIFORM); 274 | FALCOR_ASSERT(mpSampleGenerator); 275 | } 276 | 277 | void ReSTIRGIGBuffer::parseDictionary(const Dictionary& dict) 278 | { 279 | for (const auto& [key, value] : dict) 280 | { 281 | if (key == kMaxBounces) 282 | { 283 | mMaxBounces = value; 284 | } 285 | else if (key == kUseImportanceSampling) 286 | { 287 | mUseImportanceSampling = value; 288 | } 289 | else 290 | { 291 | logWarning("Unknown field '{}' in MinimalPathTracer dictionary.", key); 292 | } 293 | } 294 | } 295 | 296 | void ReSTIRGIGBuffer::prepareVars() 297 | { 298 | FALCOR_ASSERT(mpScene); 299 | FALCOR_ASSERT(mTracer.pProgram); 300 | 301 | // Configure program. 302 | mTracer.pProgram->addDefines(mpSampleGenerator->getDefines()); 303 | mTracer.pProgram->setTypeConformances(mpScene->getTypeConformances()); 304 | 305 | // Create program variables for the current program. 306 | // This may trigger shader compilation. If it fails, throw an exception to abort rendering. 307 | mTracer.pVars = RtProgramVars::create(mTracer.pProgram, mTracer.pBindingTable); 308 | 309 | // Bind utility classes into shared data. 310 | auto var = mTracer.pVars->getRootVar(); 311 | mpSampleGenerator->setShaderData(var); 312 | } 313 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | #pragma once 29 | #include "Falcor.h" 30 | #include "Utils/Sampling/SampleGenerator.h" 31 | 32 | using namespace Falcor; 33 | 34 | class ReSTIRGIGBuffer : public RenderPass 35 | { 36 | public: 37 | using SharedPtr = std::shared_ptr; 38 | 39 | static const Info kInfo; 40 | 41 | /** Create a new render pass object. 42 | \param[in] pRenderContext The render context. 43 | \param[in] dict Dictionary of serialized parameters. 44 | \return A new object, or an exception is thrown if creation failed. 45 | */ 46 | static SharedPtr create(RenderContext* pRenderContext = nullptr, const Dictionary& dict = {}); 47 | 48 | virtual Dictionary getScriptingDictionary() override; 49 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 50 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override {} 51 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 52 | virtual void renderUI(Gui::Widgets& widget) override; 53 | virtual void setScene(RenderContext* pRenderContext, const Scene::SharedPtr& pScene) override; 54 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 55 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 56 | 57 | private: 58 | ReSTIRGIGBuffer(const Dictionary& dict); 59 | void parseDictionary(const Dictionary& dict); 60 | void prepareVars(); 61 | 62 | // Internal state 63 | Scene::SharedPtr mpScene; ///< Current scene. 64 | SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. 65 | 66 | // Configuration 67 | uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). 68 | bool mUseImportanceSampling = true; ///< Use importance sampling for materials. 69 | 70 | // Runtime data 71 | uint mFrameCount = 0; ///< Frame count since scene was loaded. 72 | bool mOptionsChanged = false; 73 | 74 | // Ray tracing program. 75 | struct 76 | { 77 | RtProgram::SharedPtr pProgram; 78 | RtBindingTable::SharedPtr pBindingTable; 79 | RtProgramVars::SharedPtr pVars; 80 | } mTracer; 81 | }; 82 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.rt.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # Redistribution and use in source and binary forms, with or without 5 | # modification, are permitted provided that the following conditions 6 | # are met: 7 | # * Redistributions of source code must retain the above copyright 8 | # notice, this list of conditions and the following disclaimer. 9 | # * Redistributions in binary form must reproduce the above copyright 10 | # notice, this list of conditions and the following disclaimer in the 11 | # documentation and/or other materials provided with the distribution. 12 | # * Neither the name of NVIDIA CORPORATION nor the names of its 13 | # contributors may be used to endorse or promote products derived 14 | # from this software without specific prior written permission. 15 | # 16 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 17 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 19 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 20 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 21 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 22 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 23 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 24 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 25 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 26 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 27 | **************************************************************************/ 28 | 29 | /** Minimal path tracer. 30 | 31 | The purpose is to use it for validation of more complex renderers. 32 | The implementation here should be kept as simple/naive as possible. 33 | 34 | At each hit point (including the primary hit loaded from the V-buffer), 35 | analytic light sources (point, directional) are sampled uniformly using 36 | 1 shadow ray, and 1 scatter ray is traced to sample the hemisphere. 37 | At hit/miss the scatter ray includes light from emissive surface and 38 | the environment map, respectively. Traversal stops at a fixed path length. 39 | 40 | Each type of light (analytic, emissive, env map) can be individually 41 | enabled/disabled from the host. This clutters the code a bit, but it is 42 | important as not all other renderes may support all three light types. 43 | 44 | The host sets the following defines: 45 | 46 | MAX_BOUNCES Maximum number of indirect bounces (0 means no indirect). 47 | USE_IMPORTANCE_SAMPLING Nonzero if importance sampling should be used for sampling materials. 48 | USE_ANALYTIC_LIGHTS Nonzero if Falcor's analytic lights should be used. 49 | USE_EMISSIVE_LIGHTS Nonzero if emissive geometry should be used as lights. 50 | USE_ENV_LIGHT Nonzero if env map is available and should be used as light source. 51 | USE_ENV_BACKGROUND Nonzero if env map is available and should be used as background. 52 | is_valid_ 1 if optional I/O buffer with this name should be used. 53 | */ 54 | 55 | #include "Scene/SceneDefines.slangh" 56 | #include "Utils/Math/MathConstants.slangh" 57 | 58 | import Scene.Raytracing; 59 | import Scene.Intersection; 60 | import Utils.Math.MathHelpers; 61 | import Utils.Geometry.GeometryHelpers; 62 | import Utils.Sampling.SampleGenerator; 63 | import Rendering.Lights.LightHelpers; 64 | 65 | cbuffer CB 66 | { 67 | uint gFrameCount; // Frame count since scene was loaded. 68 | uint gPRNGDimension; // First available PRNG dimension. 69 | } 70 | 71 | // Inputs 72 | Texture2D gVBuffer; 73 | Texture2D gViewW; // Optional 74 | 75 | // Outputs 76 | RWTexture2D gVPosW; 77 | RWTexture2D gVNormW; 78 | RWTexture2D gVColor; 79 | RWTexture2D gSPosW; 80 | RWTexture2D gSNormW; 81 | RWTexture2D gSColor; 82 | RWTexture2D gPdf; 83 | 84 | // Static configuration based on defines set from the host. 85 | #define is_valid(name) (is_valid_##name != 0) 86 | static const uint kMaxBounces = MAX_BOUNCES; 87 | static const bool kUseImportanceSampling = USE_IMPORTANCE_SAMPLING; 88 | static const bool kUseAnalyticLights = USE_ANALYTIC_LIGHTS; 89 | static const bool kUseEmissiveLights = USE_EMISSIVE_LIGHTS; 90 | static const bool kUseEnvLight = USE_ENV_LIGHT; 91 | static const bool kUseEnvBackground = USE_ENV_BACKGROUND; 92 | static const float3 kDefaultBackgroundColor = float3(0, 0, 0); 93 | static const float kRayTMax = FLT_MAX; 94 | 95 | /** Payload for shadow ray. 96 | */ 97 | struct ShadowRayData 98 | { 99 | bool visible; 100 | }; 101 | 102 | /** Payload for scatter ray (up to 72B). 103 | */ 104 | struct ScatterRayData 105 | { 106 | float3 radiance; ///< Accumulated outgoing radiance from path. 107 | bool terminated; ///< Set to true when path is terminated. 108 | float3 thp; ///< Current path throughput. This is updated at each path vertex. 109 | uint pathLength; ///< Path length in number of path segments (0 at origin, 1 at first secondary hit, etc.). Max 2^31. 110 | float3 origin; ///< Next path segment origin. 111 | float3 direction; ///< Next path segment direction. 112 | float3 normal; ///< Next path segment normal. 113 | float pdf; ///< Next path pdf 114 | 115 | SampleGenerator sg; ///< Per-ray state for the sample generator (up to 16B). 116 | 117 | /** Initializes ray payload with default parameters. 118 | */ 119 | __init(SampleGenerator sg) 120 | { 121 | this.terminated = false; 122 | this.pathLength = 0; 123 | this.radiance = float3(0, 0, 0); 124 | this.thp = float3(1, 1, 1); 125 | this.origin = float3(0, 0, 0); 126 | this.direction = float3(0, 0, 0); 127 | this.normal = float3(0, 0, 0); 128 | this.pdf = 0; 129 | this.sg = sg; 130 | } 131 | }; 132 | 133 | struct Sample 134 | { 135 | float3 vPosW; // visible point 136 | float3 vNormW; // visible surface normal 137 | float3 vColor; // outgoing radiance at visible point in RGB 138 | float3 sPosW; // sample point 139 | float3 sNormW; // sample surface normal 140 | float3 sColor; // outgoing radiance at sample point in RGB 141 | float random; // random numbers used for path 142 | }; 143 | 144 | /** Setup ShadingData based on loaded vertex/material attributes for a hit point. 145 | \param[in] hit Hit information. 146 | \param[in] rayOrigin Ray origin. 147 | \param[in] rayDir Normalized ray direction. 148 | \param[in] lod Method for computing texture level-of-detail. 149 | \return ShadingData struct. 150 | */ 151 | ShadingData loadShadingData(const HitInfo hit, const float3 rayOrigin, const float3 rayDir, const ITextureSampler lod) 152 | { 153 | VertexData v = {}; 154 | uint materialID = {}; 155 | 156 | #if SCENE_HAS_GEOMETRY_TYPE(GEOMETRY_TYPE_TRIANGLE_MESH) 157 | if (hit.getType() == HitType::Triangle) 158 | { 159 | const TriangleHit triangleHit = hit.getTriangleHit(); 160 | v = gScene.getVertexData(triangleHit); 161 | materialID = gScene.getMaterialID(triangleHit.instanceID); 162 | } 163 | #endif 164 | #if SCENE_HAS_GEOMETRY_TYPE(GEOMETRY_TYPE_DISPLACED_TRIANGLE_MESH) 165 | if (hit.getType() == HitType::DisplacedTriangle) 166 | { 167 | const DisplacedTriangleHit displacedTriangleHit = hit.getDisplacedTriangleHit(); 168 | v = gScene.getVertexData(displacedTriangleHit, -rayDir); 169 | materialID = gScene.getMaterialID(displacedTriangleHit.instanceID); 170 | } 171 | #endif 172 | #if SCENE_HAS_GEOMETRY_TYPE(GEOMETRY_TYPE_CURVE) 173 | if (hit.getType() == HitType::Curve) 174 | { 175 | const CurveHit curveHit = hit.getCurveHit(); 176 | v = gScene.getVertexDataFromCurve(curveHit); 177 | materialID = gScene.getMaterialID(curveHit.instanceID); 178 | } 179 | #endif 180 | #if SCENE_HAS_GEOMETRY_TYPE(GEOMETRY_TYPE_SDF_GRID) 181 | if (hit.getType() == HitType::SDFGrid) 182 | { 183 | const SDFGridHit sdfGridHit = hit.getSDFGridHit(); 184 | v = gScene.getVertexDataFromSDFGrid(sdfGridHit, rayOrigin, rayDir); 185 | materialID = gScene.getMaterialID(sdfGridHit.instanceID); 186 | } 187 | #endif 188 | 189 | ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -rayDir, lod); 190 | 191 | return sd; 192 | } 193 | 194 | /** Returns the primary ray's direction. 195 | */ 196 | float3 getPrimaryRayDir(uint2 launchIndex, uint2 launchDim, const Camera camera) 197 | { 198 | if (is_valid(gViewW)) 199 | { 200 | // If we have the view vector bound as a buffer, just fetch it. No need to compute anything. 201 | return -gViewW[launchIndex].xyz; 202 | } 203 | else 204 | { 205 | // Compute the view vector. This must exactly match what the G-buffer pass is doing (jitter etc.). 206 | // Note that we do not take depth-of-field into account as it would require exactly matching the 207 | // sample generator between the passes, which is error prone. The host side will issue a warning instead. 208 | return camera.computeRayPinhole(launchIndex, launchDim).dir; 209 | } 210 | } 211 | 212 | /** Traces a shadow ray towards a light source. 213 | \param[in] origin Ray origin for the shadow ray. 214 | \param[in] dir Direction from shading point towards the light source (normalized). 215 | \param[in] distance Distance to the light source. 216 | \return True if light is visible, false otherwise. 217 | */ 218 | bool traceShadowRay(float3 origin, float3 dir, float distance) 219 | { 220 | RayDesc ray; 221 | ray.Origin = origin; 222 | ray.Direction = dir; 223 | ray.TMin = 0.f; 224 | ray.TMax = distance; 225 | 226 | ShadowRayData rayData; 227 | rayData.visible = false; // Set to true by miss shader if ray is not terminated before 228 | TraceRay(gScene.rtAccel, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff /* instanceInclusionMask */, 1 /* hitIdx */, rayTypeCount, 1 /* missIdx */, ray, rayData); 229 | 230 | return rayData.visible; 231 | } 232 | 233 | /** Traces a scatter ray based on ray parameters stored in the ray payload. 234 | \param[in] rayData Describes the ray parameters. The struct is modified based on the result. 235 | */ 236 | void traceScatterRay(inout ScatterRayData rayData) 237 | { 238 | RayDesc ray; 239 | ray.Origin = rayData.origin; 240 | ray.Direction = rayData.direction; 241 | ray.TMin = 0.f; 242 | ray.TMax = kRayTMax; 243 | 244 | uint rayFlags = 0; // TODO: Set cull mode from the app 245 | TraceRay(gScene.rtAccel, rayFlags, 0xff /* instanceInclusionMask */, 0 /* hitIdx */, rayTypeCount, 0 /* missIdx */, ray, rayData); 246 | } 247 | 248 | /** Evaluates the direct illumination from analytic lights. 249 | This function samples Falcor's light list uniformly with one shadow ray. 250 | \param[in] sd Shading data. 251 | \param[in] bsdf BSDF instance. 252 | \param[in,out] sg SampleGenerator object. 253 | \return Outgoing radiance in view direction. 254 | */ 255 | float3 evalDirectAnalytic(const ShadingData sd, const IBSDF bsdf, inout SampleGenerator sg) 256 | { 257 | const uint lightCount = gScene.getLightCount(); 258 | if (lightCount == 0) return float3(0.f); 259 | 260 | // Pick one of the analytic light sources randomly with equal probability. 261 | const uint lightIndex = min(uint(sampleNext1D(sg) * lightCount), lightCount - 1); 262 | float invPdf = lightCount; // Light selection pdf = 1.0 / lightCount. 263 | 264 | // Sample local light source. 265 | AnalyticLightSample ls; 266 | if (!sampleLight(sd.posW, gScene.getLight(lightIndex), sg, ls)) return float3(0.f); 267 | 268 | // Reject sample if not in the hemisphere of a BSDF lobe. 269 | const uint lobes = bsdf.getLobes(sd); 270 | const bool hasReflection = lobes & uint(LobeType::Reflection); 271 | const bool hasTransmission = lobes & uint(LobeType::Transmission); 272 | if (dot(ls.dir, sd.N) <= kMinCosTheta && !hasTransmission) return float3(0.f); 273 | if (dot(ls.dir, sd.N) >= -kMinCosTheta && !hasReflection) return float3(0.f); 274 | 275 | // Get origin with offset applied in direction of the geometry normal to avoid self-intersection. 276 | const float3 origin = computeRayOrigin(sd.posW, dot(sd.faceN, ls.dir) >= 0.f ? sd.faceN : -sd.faceN); 277 | 278 | // Test visibility by tracing a shadow ray. 279 | bool V = traceShadowRay(origin, ls.dir, ls.distance); 280 | if (!V) return float3(0.f); 281 | 282 | // Evaluate contribution. 283 | return bsdf.eval(sd, ls.dir, sg) * ls.Li * invPdf; 284 | } 285 | 286 | /** Generate a new scatter ray or terminate. 287 | \param[in] sd Shading data. 288 | \param[in] bsdf BSDF instance. 289 | \param[in] isCurveHit True if on curve hit. 290 | \param[in] rayOrigin Ray origin for the new ray. 291 | \param[in,out] rayData Ray payload. 292 | \return True if the path continues. 293 | */ 294 | bool generateScatterRay(const ShadingData sd, const IBSDF bsdf, bool isCurveHit, float3 rayOrigin, inout ScatterRayData rayData) 295 | { 296 | // Sample material. 297 | BSDFSample bsdfSample; 298 | if (bsdf.sample(sd, rayData.sg, bsdfSample, kUseImportanceSampling)) 299 | { 300 | rayData.origin = rayOrigin; 301 | if (!isCurveHit && bsdfSample.isLobe(LobeType::Transmission)) 302 | { 303 | rayData.origin = sd.computeNewRayOrigin(false); 304 | } 305 | rayData.normal = sd.N; 306 | rayData.direction = bsdfSample.wo; 307 | rayData.thp *= bsdfSample.weight; 308 | rayData.pdf = bsdfSample.pdf; 309 | return any(rayData.thp > 0.f); 310 | } 311 | 312 | return false; 313 | } 314 | 315 | /** Process a hit. 316 | Loads the shading data, samples analytic lights and samples a new scatter ray. 317 | Terminates the path if maximum number of bounces is reached. 318 | \param[in] hit Hit info. 319 | \param[in,out] rayData Ray payload. 320 | 321 | */ 322 | void handleHit(const HitInfo hit, inout ScatterRayData rayData) 323 | { 324 | const bool isCurveHit = hit.getType() == HitType::Curve; 325 | let lod = ExplicitLodTextureSampler(0.f); 326 | 327 | // Load shading data. 328 | ShadingData sd = loadShadingData(hit, rayData.origin, rayData.direction, lod); 329 | 330 | // Create BSDF instance. 331 | let bsdf = gScene.materials.getBSDF(sd, lod); 332 | 333 | // Add emitted light. 334 | if (kUseEmissiveLights && (rayData.pathLength > 0)) 335 | { 336 | rayData.radiance += rayData.thp * bsdf.getProperties(sd).emission; 337 | } 338 | 339 | // Check whether to terminate based on max depth. 340 | if (rayData.pathLength >= kMaxBounces) 341 | { 342 | rayData.terminated = true; 343 | return; 344 | } 345 | 346 | // Compute ray origin for new rays spawned from the hit. 347 | float3 rayOrigin; 348 | if (isCurveHit) 349 | { 350 | // For curves, we set the new origin at the sphere center. 351 | rayOrigin = sd.posW - sd.curveRadius * sd.N; 352 | } 353 | else 354 | { 355 | rayOrigin = sd.computeNewRayOrigin(); 356 | } 357 | 358 | // Add contribution of direct light from analytic lights. 359 | if (kUseAnalyticLights) 360 | { 361 | float3 Lr = evalDirectAnalytic(sd, bsdf, rayData.sg); 362 | rayData.radiance += rayData.thp * Lr; 363 | } 364 | 365 | // Generate scatter ray for the next path segment. 366 | // The raygen shader will continue the path based on the returned payload. 367 | if (!generateScatterRay(sd, bsdf, isCurveHit, rayOrigin, rayData)) 368 | { 369 | rayData.terminated = true; 370 | return; 371 | } 372 | rayData.normal = sd.N; 373 | rayData.pathLength++; 374 | } 375 | 376 | /** This is the main entry point for the minimal path tracer. 377 | 378 | One path per pixel is generated, which is traced into the scene. 379 | The path tracer is written as a for-loop over path segments. 380 | 381 | Built-in light sources (point, directional) are sampled explicitly at each 382 | path vertex. The contributions from area lights (env map and mesh lights) 383 | are explicitly added by the scatter ray hit/miss shaders. 384 | 385 | \param[in] pixel Pixel to trace a path for. 386 | \param[in] frameDim Dimension of the frame in pixels. 387 | \return Returns the estimated color (radiance). 388 | */ 389 | Sample tracePath(const uint2 pixel, const uint2 frameDim) 390 | { 391 | Sample outSample = {}; 392 | 393 | const float3 primaryRayOrigin = gScene.camera.getPosition(); 394 | const float3 primaryRayDir = getPrimaryRayDir(pixel, frameDim, gScene.camera); 395 | 396 | const HitInfo hit = HitInfo(gVBuffer[pixel]); 397 | if (hit.isValid()) 398 | { 399 | // Pixel represents a valid primary hit. Compute its contribution. 400 | 401 | const bool isCurveHit = hit.getType() == HitType::Curve; 402 | let lod = ExplicitLodTextureSampler(0.f); 403 | 404 | // Load shading data. 405 | ShadingData sd = loadShadingData(hit, primaryRayOrigin, primaryRayDir, lod); 406 | 407 | outSample.vPosW = sd.posW; 408 | outSample.vNormW = sd.N; 409 | 410 | // Create BSDF instance at shading point. 411 | let bsdf = gScene.materials.getBSDF(sd, lod); 412 | 413 | // Create sample generator. 414 | SampleGenerator sg = SampleGenerator(pixel, gFrameCount); 415 | 416 | // Advance the generator to the first available dimension. 417 | // TODO: This is potentially expensive. We may want to store/restore the state from memory if it becomes a problem. 418 | for (uint i = 0; i < gPRNGDimension; i++) 419 | { 420 | sampleNext1D(sg); 421 | } 422 | 423 | // Compute ray origin for new rays spawned from the G-buffer. 424 | float3 rayOrigin; 425 | if (isCurveHit) 426 | { 427 | // For curves, we set the new origin at the sphere center. 428 | rayOrigin = sd.posW - sd.curveRadius * sd.N; 429 | } 430 | else 431 | { 432 | rayOrigin = sd.computeNewRayOrigin(); 433 | } 434 | 435 | // Prepare ray payload. 436 | ScatterRayData rayData = ScatterRayData(sg); 437 | 438 | // Generate scatter ray. 439 | if (!generateScatterRay(sd, bsdf, isCurveHit, rayOrigin, rayData)) 440 | { 441 | rayData.terminated = true; 442 | } 443 | rayData.normal = sd.N; 444 | 445 | // Follow path into the scene and compute its total contribution. 446 | for (uint depth = 0; depth <= kMaxBounces && !rayData.terminated; depth++) 447 | { 448 | // Trace scatter ray. If it hits geometry, the closest hit shader samples 449 | // direct illumination and generates the next scatter ray. 450 | traceScatterRay(rayData); 451 | 452 | if (depth == kMaxBounces - 1 && !rayData.terminated) 453 | { 454 | outSample.sColor = rayData.radiance; 455 | } 456 | } 457 | 458 | // Store contribution from scatter ray. 459 | outSample.vColor += rayData.radiance; 460 | 461 | outSample.sPosW = rayData.origin; 462 | outSample.sNormW = rayData.normal; 463 | outSample.random = rayData.pdf; 464 | } 465 | else 466 | { 467 | // Background pixel. 468 | outSample.sColor = kUseEnvBackground ? gScene.envMap.eval(primaryRayDir) : kDefaultBackgroundColor; 469 | outSample.vColor = outSample.sColor; 470 | outSample.sPosW = float3(0.0f, 0.0f, 0.0f); 471 | outSample.sNormW = float3(0.0f, 0.0f, 0.0f); 472 | outSample.random = 0.0f; 473 | } 474 | 475 | return outSample; 476 | } 477 | 478 | 479 | 480 | // 481 | // Shader entry points for miss shaders. 482 | // 483 | 484 | [shader("miss")] 485 | void scatterMiss(inout ScatterRayData rayData) 486 | { 487 | // Ray missed the scene. Mark the ray as terminated. 488 | rayData.terminated = true; 489 | 490 | // Add contribution from distant light (env map) in this direction. 491 | if (kUseEnvLight && (rayData.pathLength > 0)) 492 | { 493 | float3 Le = gScene.envMap.eval(WorldRayDirection()); 494 | rayData.radiance += rayData.thp * Le; 495 | } 496 | } 497 | 498 | [shader("miss")] 499 | void shadowMiss(inout ShadowRayData rayData) 500 | { 501 | // The miss shader is executed if the ray misses all geometry. Mark as visible. 502 | rayData.visible = true; 503 | } 504 | 505 | // 506 | // Shader entry points for TriangleMesh hit groups. 507 | // 508 | 509 | [shader("anyhit")] 510 | void scatterTriangleMeshAnyHit(inout ScatterRayData rayData, BuiltInTriangleIntersectionAttributes attribs) 511 | { 512 | // Alpha test for non-opaque geometry. 513 | GeometryInstanceID instanceID = getGeometryInstanceID(); 514 | VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); 515 | const uint materialID = gScene.getMaterialID(instanceID); 516 | if (gScene.materials.alphaTest(v, materialID, 0.f)) 517 | { 518 | IgnoreHit(); 519 | } 520 | } 521 | 522 | [shader("closesthit")] 523 | void scatterTriangleMeshClosestHit(inout ScatterRayData rayData, BuiltInTriangleIntersectionAttributes attribs) 524 | { 525 | TriangleHit triangleHit; 526 | triangleHit.instanceID = getGeometryInstanceID(); 527 | triangleHit.primitiveIndex = PrimitiveIndex(); 528 | triangleHit.barycentrics = attribs.barycentrics; 529 | handleHit(HitInfo(triangleHit), rayData); 530 | } 531 | 532 | [shader("anyhit")] 533 | void shadowTriangleMeshAnyHit(inout ShadowRayData rayData, BuiltInTriangleIntersectionAttributes attribs) 534 | { 535 | // Alpha test for non-opaque geometry. 536 | GeometryInstanceID instanceID = getGeometryInstanceID(); 537 | VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); 538 | const uint materialID = gScene.getMaterialID(instanceID); 539 | if (gScene.materials.alphaTest(v, materialID, 0.f)) IgnoreHit(); 540 | } 541 | 542 | // 543 | // Shader entry points for DisplacedTriangleMesh hit groups. 544 | // 545 | 546 | [shader("intersection")] 547 | void displacedTriangleMeshIntersection() 548 | { 549 | const Ray ray = Ray(WorldRayOrigin(), WorldRayDirection(), RayTMin(), RayTCurrent()); 550 | DisplacedTriangleMeshIntersector::Attribs attribs; 551 | float t; 552 | if (DisplacedTriangleMeshIntersector::intersect(ray, getGeometryInstanceID(), PrimitiveIndex(), attribs, t)) 553 | { 554 | ReportHit(t, 0, attribs); 555 | } 556 | } 557 | 558 | [shader("closesthit")] 559 | void scatterDisplacedTriangleMeshClosestHit(inout ScatterRayData rayData, DisplacedTriangleMeshIntersector::Attribs attribs) 560 | { 561 | DisplacedTriangleHit displacedTriangleHit; 562 | displacedTriangleHit.instanceID = getGeometryInstanceID(); 563 | displacedTriangleHit.primitiveIndex = PrimitiveIndex(); 564 | displacedTriangleHit.barycentrics = attribs.barycentrics; 565 | displacedTriangleHit.displacement = attribs.displacement; 566 | handleHit(HitInfo(displacedTriangleHit), rayData); 567 | } 568 | 569 | // 570 | // Shader entry points for Curve hit groups. 571 | // 572 | 573 | [shader("intersection")] 574 | void curveIntersection() 575 | { 576 | const Ray ray = Ray(WorldRayOrigin(), WorldRayDirection(), RayTMin(), RayTCurrent()); 577 | CurveIntersector::Attribs attribs; 578 | float t; 579 | if (CurveIntersector::intersect(ray, getGeometryInstanceID(), PrimitiveIndex(), attribs, t)) 580 | { 581 | ReportHit(t, 0, attribs); 582 | } 583 | } 584 | 585 | [shader("closesthit")] 586 | void scatterCurveClosestHit(inout ScatterRayData rayData, CurveIntersector::Attribs attribs) 587 | { 588 | CurveHit curveHit; 589 | curveHit.instanceID = getGeometryInstanceID(); 590 | curveHit.primitiveIndex = PrimitiveIndex(); 591 | curveHit.barycentrics = attribs.barycentrics; 592 | handleHit(HitInfo(curveHit), rayData); 593 | } 594 | 595 | // 596 | // Shader entry points for SDFGrid hit groups. 597 | // 598 | 599 | [shader("intersection")] 600 | void sdfGridIntersection() 601 | { 602 | const Ray ray = Ray(WorldRayOrigin(), WorldRayDirection(), RayTMin(), RayTCurrent()); 603 | SDFGridHitData sdfGridHitData; 604 | float t; 605 | if (SDFGridIntersector::intersect(ray, getGeometryInstanceID(), PrimitiveIndex(), sdfGridHitData, t)) 606 | { 607 | ReportHit(t, 0, sdfGridHitData); 608 | } 609 | } 610 | 611 | [shader("closesthit")] 612 | void scatterSdfGridClosestHit(inout ScatterRayData rayData, SDFGridHitData sdfGridHitData) 613 | { 614 | SDFGridHit sdfGridHit; 615 | sdfGridHit.instanceID = getGeometryInstanceID(); 616 | sdfGridHit.hitData = sdfGridHitData; 617 | handleHit(HitInfo(sdfGridHit), rayData); 618 | } 619 | 620 | // 621 | // Shader entry point for ray generation shader. 622 | // 623 | 624 | [shader("raygeneration")] 625 | void rayGen() 626 | { 627 | uint2 pixel = DispatchRaysIndex().xy; 628 | uint2 frameDim = DispatchRaysDimensions().xy; 629 | 630 | Sample sample = tracePath(pixel, frameDim); 631 | 632 | gVPosW[pixel] = float4(sample.vPosW, 1.f); 633 | gVNormW[pixel] = float4(sample.vNormW, 0.f); 634 | gVColor[pixel] = float4(sample.vColor, 1.f); 635 | gSPosW[pixel] = float4(sample.sPosW, 1.f); 636 | gSNormW[pixel] = float4(sample.sNormW, 0.f); 637 | gSColor[pixel] = float4(sample.sColor, 1.f); 638 | gPdf[pixel] = sample.random; 639 | } 640 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | x64 7 | 8 | 9 | Release 10 | x64 11 | 12 | 13 | 14 | {EFE5B208-694F-4972-9484-91B0C564C446} 15 | Win32Proj 16 | ReSTIRGIGBuffer 17 | 10.0.19041.0 18 | ReSTIRGIGBuffer 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {2c535635-e4c5-4098-a928-574f0e7cd5f9} 36 | 37 | 38 | 39 | 40 | 41 | 42 | DynamicLibrary 43 | true 44 | v142 45 | Unicode 46 | Shaders\RenderPasses\$(ProjectName) 47 | 48 | 49 | DynamicLibrary 50 | false 51 | v142 52 | true 53 | Unicode 54 | Shaders\RenderPasses\$(ProjectName) 55 | 56 | 57 | 58 | 59 | 60 | 61 | true 62 | 63 | 64 | false 65 | 66 | 67 | 68 | 69 | 70 | Level3 71 | Disabled 72 | PROJECT_DIR=R"($(ProjectDir))";_DEBUG;%(PreprocessorDefinitions) 73 | true 74 | true 75 | stdcpp17 76 | 77 | 78 | Windows 79 | true 80 | 81 | 82 | 83 | 84 | Level3 85 | 86 | 87 | MaxSpeed 88 | true 89 | true 90 | PROJECT_DIR=R"($(ProjectDir))";NDEBUG;%(PreprocessorDefinitions) 91 | true 92 | true 93 | stdcpp17 94 | 95 | 96 | Windows 97 | true 98 | true 99 | true 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /RenderPasses/ReSTIRGIGBuffer/ReSTIRGIGBuffer.vcxproj.user: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/EvalContext.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | #include "Utils/Math/MathConstants.slangh" 11 | import Scene.RaytracingInline; 12 | __exported import Scene.HitInfo; 13 | import Scene.Shading; 14 | import Utils.Math.MathHelpers; 15 | import Rendering.Materials.Microfacet; 16 | import Rendering.Materials.Fresnel; 17 | //import Lights; 18 | __exported import SurfaceData; 19 | 20 | #define ROUGHNESS_THRESHOLD 0.01 // default falcor value is 0.08 21 | 22 | /** Context for evaluating light samples. 23 | */ 24 | struct EvalContext 25 | { 26 | // Static configuration. 27 | static const bool kUseAlphaTest = USE_ALPHA_TEST; 28 | 29 | float3 pos; 30 | float depth; 31 | float3 N; 32 | float3 V; 33 | float NdotV; 34 | float diffuseWeight; 35 | float specularWeight; 36 | float diffuseSpecularMix; 37 | float ggxAlpha; 38 | uint lobes; 39 | 40 | bool valid; 41 | 42 | static EvalContext create(uint2 pixel, uint2 frameDim, SurfaceData surfaceData) 43 | { 44 | const Ray ray = gScene.camera.computeRayPinhole(pixel, frameDim); 45 | 46 | EvalContext evalContext = {}; 47 | evalContext.pos = surfaceData.pos; 48 | evalContext.depth = surfaceData.depth; 49 | evalContext.N = surfaceData.normal; 50 | evalContext.V = -ray.dir; 51 | evalContext.NdotV = saturate(dot(evalContext.N, evalContext.V)); 52 | evalContext.diffuseWeight = surfaceData.diffuseWeight; 53 | evalContext.specularWeight = surfaceData.specularWeight; 54 | float weightSum = surfaceData.diffuseWeight + surfaceData.specularWeight; 55 | evalContext.diffuseSpecularMix = weightSum > 1e-7f ? (surfaceData.diffuseWeight / weightSum) : 1.f; 56 | // Clamp roughness to avoid numerical instability when evaluating the GGX lobe. 57 | // This is the same threshold as used in the legacy Falcor BRDF. 58 | float roughness = max(ROUGHNESS_THRESHOLD, surfaceData.specularRoughness); 59 | evalContext.ggxAlpha = roughness * roughness; 60 | evalContext.lobes = surfaceData.lobes; 61 | evalContext.valid = surfaceData.isValid(); 62 | return evalContext; 63 | } 64 | 65 | bool isValid() 66 | { 67 | return valid; 68 | } 69 | 70 | /** Compare depth and normal between this and another pixel and determine if other sample is valid for reuse. 71 | \param[in] otherNormalDepth Normal and depth of other pixel. 72 | \param[in] normalThreshold Normal cosine threshold. 73 | \param[in] depthThreshold Relative depth threshold. 74 | \return Returns true if other sample should be reused. 75 | */ 76 | bool isValidNeighbor(float4 otherNormalDepth, float normalThreshold, float depthThreshold) 77 | { 78 | return 79 | dot(N, otherNormalDepth.xyz) >= normalThreshold && 80 | abs((depth - otherNormalDepth.w)) <= depthThreshold * depth; 81 | } 82 | 83 | /** Compare depth and normal between this and another pixel and determine if other sample is valid for reuse. 84 | \param[in] otherContext Other evaluation context. 85 | \param[in] normalThreshold Normal cosine threshold. 86 | \param[in] depthThreshold Relative depth threshold. 87 | \return Returns true if other sample should be reused. 88 | */ 89 | //bool isValidNeighbor(const EvalContext otherContext, float normalThreshold, float depthThreshold) 90 | //{ 91 | // return isValidNeighbor(float4(otherContext.N, otherContext.depth), normalThreshold, depthThreshold); 92 | //} 93 | 94 | /** Transform direction from world- to local-space. 95 | \param[in] w Direction in world-space. 96 | \return Returns direction in local-space. 97 | */ 98 | //float3 toLocal(float3 w) 99 | //{ 100 | // float3 B = perp_stark(N); 101 | // float3 T = cross(B, N); 102 | // return float3(dot(B, w), dot(T, w), dot(N, w)); 103 | //} 104 | 105 | /** Transform direction from local- to world-space. 106 | \param[in] w Direction in local-space. 107 | \return Returns direction in world-space. 108 | */ 109 | //float3 toGlobal(float3 w) 110 | //{ 111 | // float3 B = perp_stark(N); 112 | // float3 T = cross(B, N); 113 | // return B * w.x + T * w.y + N * w.z; 114 | //} 115 | 116 | /** Sample the BRDF. 117 | \param[in] specularOnly Use specular lobe only. 118 | \param[in] xi Uniform random numbers. 119 | \param[out] dir Sampled direction in world-space. 120 | \return Returns true if valid sample was generated. 121 | */ 122 | //bool sampleBRDF(bool specularOnly, float3 xi, out float3 dir) 123 | //{ 124 | // dir = {}; 125 | // 126 | // if (xi.x < diffuseSpecularMix) 127 | // { 128 | // if (specularOnly) return false; 129 | // float pdf; 130 | // dir = toGlobal(sample_cosine_hemisphere_concentric(xi.yz, pdf)); 131 | // } 132 | // else 133 | // { 134 | // float pdf; 135 | // // float3 h = sampleGGX_VNDF(ggxAlpha, toLocal(V), xi.yz, pdf); 136 | // float3 h = sampleGGX_NDF(ggxAlpha, xi.yz, pdf); 137 | // dir = reflect(-V, toGlobal(h)); 138 | // } 139 | // 140 | // return dot(N, dir) > 0.f; 141 | //} 142 | 143 | /** Evaluate the BRDF PDF. 144 | \param[in] specularOnly Use specular lobe only. 145 | \param[in] dir Direction in world-space. 146 | \return Returns the PDF. 147 | */ 148 | //float evalPdfBRDF(bool specularOnly, float3 dir) 149 | //{ 150 | // float cosTheta = saturate(dot(N, dir)); 151 | // float diffusePdf = specularOnly ? 0.f : cosTheta * M_1_PI; 152 | // float3 h = normalize(toLocal(dir + V)); 153 | // // float specularPdf = evalPdfGGX_VNDF(ggxAlpha, toLocal(dir), h) / (4.f * saturate(dot(h, toLocal(V)))); 154 | // float specularPdf = evalPdfGGX_NDF(ggxAlpha, h.z) / (4.f * saturate(dot(h, toLocal(V)))); 155 | // float pdf = cosTheta > 0.f ? lerp(specularPdf, diffusePdf, diffuseSpecularMix) : 0.f; 156 | // return pdf; 157 | //} 158 | 159 | /** Evaluate the BRDF. 160 | \param[in] L Direction to light in world-space. 161 | \return Returns the BRDF value. 162 | */ 163 | float evalBRDF(float3 L) 164 | { 165 | float NdotL = saturate(dot(N, L)); 166 | float3 H = normalize(V + L); 167 | float NdotH = saturate(dot(N, H)); 168 | float LdotH = saturate(dot(L, H)); 169 | 170 | float D = evalNdfGGX(ggxAlpha, NdotH); 171 | float G = evalMaskingSmithGGXSeparable(ggxAlpha, NdotV, NdotL); 172 | float F = specularWeight < 1e-8f ? 0.f : evalFresnelSchlick(specularWeight, 1.f, LdotH) / specularWeight; 173 | 174 | float diffuse = NdotL * M_1_PI; 175 | float specular = max(0.f, D * G * F / (4.f * NdotV)); 176 | return NdotL > 0.f ? lerp(specular, diffuse, diffuseSpecularMix) : 0.f; 177 | } 178 | 179 | /** Evaluate the target function (target pdf) of a light sample. 180 | \param[in] evaluatedLightSample Evaluated light sample. 181 | \param[in] withVisibility Evaluate visibility term. 182 | \return Returns the target function value. 183 | */ 184 | //float evalTargetFunction(const EvaluatedLightSample evaluatedLightSample, const bool withVisibility = false) 185 | //{ 186 | // float brdfWeight = evalBRDF(evaluatedLightSample.dir); 187 | // float weight = max(0.f, evaluatedLightSample.emission * evaluatedLightSample.geomFactor * brdfWeight); 188 | // if (withVisibility) weight *= evalVisibility(evaluatedLightSample); 189 | // return weight; 190 | //} 191 | 192 | /** Evaluate the target function (target pdf) of a light sample. 193 | \param[in] loadedLightSample Loaded light sample. 194 | \param[in] withVisibility Evaluate visibility term. 195 | \return Returns the target function value. 196 | */ 197 | //float evalTargetFunction(const LoadedLightSample loadedLightSample, const bool withVisibility = false) 198 | //{ 199 | // return evalTargetFunction(loadedLightSample.eval(pos), withVisibility); 200 | //} 201 | 202 | /** Evaluate the visibility of a light sample. 203 | \param[in] evaluatedLightSample Evaluated light sample. 204 | \return Returns the visibility, 1.f if visible, 0.f if occluded. 205 | */ 206 | //float evalVisibility(const EvaluatedLightSample evaluatedLightSample) 207 | //{ 208 | // RayDesc ray = { pos, 0.f, evaluatedLightSample.dir, max(0.f, evaluatedLightSample.distance - Lights::kRayEpsilon) }; 209 | // SceneRayQuery sceneRayQuery; 210 | // return sceneRayQuery.traceVisibilityRay(ray, RAY_FLAG_NONE, 0xff) ? 1.f : 0.f; 211 | //} 212 | 213 | /** Evaluate the visibility of a light sample. 214 | \param[in] loadedLightSample Loaded light sample. 215 | \return Returns the visibility, 1.f if visible, 0.f if occluded. 216 | */ 217 | //float evalVisibility(const LoadedLightSample loadedLightSample) 218 | //{ 219 | // return evalVisibility(loadedLightSample.eval(pos)); 220 | //} 221 | 222 | /** Trace a ray and report the hit point. 223 | \param[in] ray Ray description. 224 | \param[out] hit Hit info. 225 | \return Returns true on hit. 226 | */ 227 | //bool traceRay(const RayDesc ray, out HitInfo hit) 228 | //{ 229 | // SceneRayQuery sceneRayQuery; 230 | // float hitT; 231 | // return sceneRayQuery.traceRay(ray, hit, hitT, RAY_FLAG_NONE, 0xff); 232 | //} 233 | }; 234 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/FinalSample.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import Utils.Math.PackedFormats; 11 | 12 | // Use compact packing. Changing this requires restarting the renderer. 13 | #define USE_COMPACT_FINAL_SAMPLE 0 14 | 15 | #if USE_COMPACT_FINAL_SAMPLE 16 | typedef uint4 PackedFinalSample; 17 | #else 18 | struct PackedFinalSample 19 | { 20 | float3 dir; 21 | float distance; 22 | float3 Li; 23 | float _pad; 24 | }; 25 | #endif 26 | 27 | /** Represents a final light sample used for shading pixels. 28 | */ 29 | struct FinalSample 30 | { 31 | float3 dir; ///< Direction to the sampled light in world-space. 32 | float distance; ///< Distance to the sampled light. 33 | float3 Li; ///< Weighted incident radiance. 34 | 35 | static FinalSample createFromPacked(const PackedFinalSample packed) 36 | { 37 | FinalSample finalSample; 38 | finalSample.unpack(packed); 39 | return finalSample; 40 | } 41 | 42 | PackedFinalSample pack() 43 | { 44 | PackedFinalSample packed; 45 | #if USE_COMPACT_FINAL_SAMPLE 46 | packed.x = encodeNormal2x16(dir); 47 | packed.y = asuint(distance); 48 | packed.z = encodeLogLuvHDR(Li); 49 | packed.w = 0; 50 | #else 51 | packed.dir = dir; 52 | packed.distance = distance; 53 | packed.Li = Li; 54 | packed._pad = 0.f; 55 | #endif 56 | return packed; 57 | } 58 | 59 | [mutating] void unpack(const PackedFinalSample packed) 60 | { 61 | #if USE_COMPACT_FINAL_SAMPLE 62 | dir = decodeNormal2x16(packed.x); 63 | distance = asfloat(packed.y); 64 | Li = decodeLogLuvHDR(packed.z); 65 | #else 66 | dir = packed.dir; 67 | distance = packed.distance; 68 | Li = packed.Li; 69 | #endif 70 | } 71 | }; 72 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/GIClearReservoirs.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | 11 | /** Compute shader implementing a reservoir reset pass. 12 | 13 | The shader resets initial sample buffer. If frameCount is 0, or forceClearReservoirs is set, 14 | It also clears two GI reservoir buffers. 15 | 16 | The dispatch dimension is over pixels (XY). 17 | */ 18 | 19 | import GIReservoir; 20 | 21 | struct GIClearReservoirs 22 | { 23 | uint2 frameDim; 24 | uint frameCount; 25 | uint reservoirCount; 26 | bool forceClearReservoirs; 27 | 28 | RWStructuredBuffer initialSamples; 29 | RWStructuredBuffer reservoirBuffer0; 30 | RWStructuredBuffer reservoirBuffer1; 31 | 32 | uint pixelToLinearIndex(uint2 pixel) 33 | { 34 | return pixel.y * frameDim.x + pixel.x; 35 | } 36 | 37 | void execute(const uint2 pixel) 38 | { 39 | if (any(pixel >= frameDim)) return; 40 | 41 | uint pixelIndex = pixelToLinearIndex(pixel); 42 | 43 | GIReservoir reservoir = GIReservoir.createEmpty(); 44 | 45 | for (int sampleIndex = 0; sampleIndex < reservoirCount && (frameCount == 0 || forceClearReservoirs); sampleIndex++) 46 | { 47 | writeReservoir(reservoirBuffer0, pixelIndex, sampleIndex * 2, frameDim.x * frameDim.y, reservoir); 48 | writeReservoir(reservoirBuffer0, pixelIndex, sampleIndex * 2 + 1, frameDim.x * frameDim.y, reservoir); 49 | writeReservoir(reservoirBuffer1, pixelIndex, sampleIndex * 2, frameDim.x * frameDim.y, reservoir); 50 | writeReservoir(reservoirBuffer1, pixelIndex, sampleIndex * 2 + 1, frameDim.x * frameDim.y, reservoir); 51 | } 52 | 53 | writeReservoir(initialSamples, pixelIndex, 0, frameDim.x* frameDim.y, reservoir); 54 | //writeReservoir(initialSamples, pixelIndex, 1, frameDim.x * frameDim.y, reservoir); 55 | } 56 | }; 57 | 58 | //cbuffer CB 59 | //{ 60 | // GIClearReservoirs gGIClearReservoirs; 61 | //} 62 | ParameterBlock gGIClearReservoirs; 63 | 64 | [numthreads(16, 16, 1)] 65 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 66 | { 67 | gGIClearReservoirs.execute(dispatchThreadId.xy); 68 | } 69 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/GIResampling.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2020, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | 11 | import Scene.RaytracingInline; 12 | import Utils.Attributes; 13 | //import Params; 14 | //import Lights; 15 | import Scene.HitInfo; 16 | import Scene.ShadingData; 17 | import Utils.Debug.PixelDebug; 18 | import Utils.Sampling.TinyUniformSampleGenerator; 19 | import ScreenSpaceReSTIR; 20 | import GIReservoir; 21 | import SurfaceData; 22 | import EvalContext; 23 | import Utils.Geometry.GeometryHelpers; 24 | import Utils.Color.ColorHelpers; 25 | import Rendering.Materials.StandardMaterial; 26 | 27 | #ifndef RESTIR_TARGET_FUNCTION 28 | #define RESTIR_TARGET_FUNCTION 1 29 | #endif 30 | 31 | #ifndef NEIGHBOR_OFFSET_COUNT 32 | #define NEIGHBOR_OFFSET_COUNT 0 33 | #endif 34 | 35 | #define RESTIR_MIS_WEIGHT 0 36 | 37 | #ifndef RESTIR_MODE 38 | #define RESTIR_MODE 3 39 | #endif 40 | 41 | #ifndef RESTIR_GI_USE_RESTIR_N 42 | #define RESTIR_GI_USE_RESTIR_N 0 43 | #endif 44 | 45 | 46 | struct GIResampling 47 | { 48 | enum class ReSTIRMode 49 | { 50 | InputOnly = 0, ///< Output input samples directly. 51 | TemporalOnly = 1, ///< Output temporal reuse result. 52 | TemporalAndBiasedSpatial = 2, ///< Output temporal followed by biased spatial reuse result. 53 | TemporalAndUnbiasedSpatial = 3 ///< Output temporal followed by unbiased spatial reuse result. 54 | }; 55 | 56 | // Static configuration. 57 | static const bool kUseAlphaTest = USE_ALPHA_TEST; 58 | static const int kTargetFunction = RESTIR_TARGET_FUNCTION; 59 | static const ReSTIRMode kReSTIRMode = ReSTIRMode(RESTIR_MODE); 60 | static const int kReSTIRMISWeight = 0; 61 | static const float kGGXRoughnessClamp = 0.15f; ///< GGX roughness clamp threshold to improve stability. 62 | static const uint kNeighborOffsetCount = NEIGHBOR_OFFSET_COUNT; 63 | static const uint kNeighborOffsetMask = kNeighborOffsetCount - 1; 64 | static const bool kReSTIRGIUseReSTIRN = RESTIR_GI_USE_RESTIR_N; 65 | 66 | // Resources. 67 | [root] StructuredBuffer surfaceData; 68 | [root] StructuredBuffer prevSurfaceData; 69 | 70 | float4x4 viewProj; 71 | float4x4 prevViewProj; 72 | float3 cameraOrigin; 73 | float3 prevCameraOrigin; 74 | float normalThreshold; 75 | float depthThreshold; 76 | 77 | uint2 frameDim = { 0, 0 }; ///< Frame dimension in pixels. 78 | uint frameIndex; ///< Current frame index. 79 | 80 | uint temporalMaxSamples; ///< Maximum M value for temporal reservoir. 81 | uint spatialMaxSamples; ///< Maximum M value for spatial reservoir. 82 | uint reservoirCount; ///< Number of reservoirs per pixel. 83 | uint maxSampleAge; ///< Maximum frames that a sample can survive. 84 | bool forceClearReservoirs; ///< Clear temporal and spatial reservoirs. 85 | 86 | Texture2D vbuffer; ///< Fullscreen V-buffer for the primary hits. 87 | Texture2D sampleCount; ///< Optional input sample count buffer. Only valid when kSamplesPerPixel == 0. 88 | Texture2D sampleOffset; ///< Output offset into per-sample buffers. Only valid when kSamplesPerPixel == 0. 89 | 90 | Texture2D motionVectors; 91 | 92 | RWTexture2D outputColor; ///< Output resolved color. 93 | 94 | Texture2D normalDepth; 95 | Texture2D prevNormalDepth; 96 | 97 | RWStructuredBuffer initialSamples; 98 | 99 | Texture1D neighborOffsets; 100 | 101 | // Ping-pong buffers storing gather point samples. 102 | StructuredBuffer prevReservoirs; 103 | RWStructuredBuffer reservoirs; 104 | 105 | float spatialWeightClampThreshold; 106 | float jacobianClampThreshold; 107 | bool enableSpatialWeightClamping; 108 | bool enableJacobianClamping; 109 | bool enableTemporalJacobian; 110 | 111 | static const float largeFloat = 1e20f; 112 | 113 | uint getPixelIndex(uint2 pixel) { return pixel.y * frameDim.x + pixel.x; } 114 | 115 | float evalTargetFunction(float3 radiance, float3 normal, float3 position, float3 samplePosition, EvalContext evalContext) 116 | { 117 | if (kTargetFunction == 1) 118 | { 119 | float3 L = normalize(samplePosition - position); 120 | float3 fCos = max(0.1f, evalContext.evalBRDF(L) * saturate(dot(normal, L))); 121 | float pdf = luminance(radiance * fCos); 122 | return pdf; 123 | } 124 | else 125 | { 126 | return luminance(radiance); 127 | } 128 | } 129 | 130 | uint toLinearIndex(uint2 pixel) 131 | { 132 | return pixel.y * frameDim.x + pixel.x; 133 | } 134 | 135 | bool evalSegmentVisibility(float3 pos, float3 endPoint) 136 | { 137 | float3 offset = endPoint - pos; 138 | float tMax = length(offset); 139 | float3 dir = offset / tMax; 140 | //RayDesc ray = { pos, 0.001f, dir, 0.999* tMax }; 141 | const Ray ray = Ray(pos, dir, 0.001f, 0.999* tMax); 142 | SceneRayQuery sceneRayQuery; 143 | return sceneRayQuery.traceVisibilityRay(ray, RAY_FLAG_NONE, 0xff); 144 | } 145 | 146 | /** Randomly update reservoir. 147 | \param[in] weight The input reservoir's weight. 148 | \param[in] srcReservoir The input reservoir. 149 | \param[inout] sg Random number generator. 150 | \param[inout] weightSum The output reservoir's weight. 151 | \param[inout] dstReservoir The output reservoir. 152 | */ 153 | bool updateReservoir(float weight, GIReservoir srcReservoir, inout TinyUniformSampleGenerator sg, inout float weightSum, inout GIReservoir dstReservoir) 154 | { 155 | weightSum += weight; 156 | dstReservoir.M += srcReservoir.M; 157 | 158 | // Conditionally update reservoir. 159 | float random = sampleNext1D(sg); 160 | bool isUpdate = random * weightSum <= weight; 161 | if (isUpdate) 162 | { 163 | dstReservoir.position = srcReservoir.position; 164 | dstReservoir.normal = srcReservoir.normal; 165 | dstReservoir.radiance = srcReservoir.radiance; 166 | dstReservoir.age = srcReservoir.age; 167 | } 168 | return isUpdate; 169 | } 170 | 171 | void execute(const uint2 pixel) 172 | { 173 | if (any(pixel >= frameDim)) 174 | { 175 | return; 176 | } 177 | 178 | const uint pixelIndex = getPixelIndex(pixel); 179 | 180 | TinyUniformSampleGenerator sg = TinyUniformSampleGenerator(pixel, frameIndex); 181 | 182 | // Setup evaluation context. 183 | SurfaceData sfd = SurfaceData::createFromPacked(surfaceData[pixelIndex]); 184 | EvalContext evalContext = EvalContext::create(pixel, frameDim, sfd); 185 | if (!evalContext.isValid()) 186 | { 187 | return; 188 | } 189 | 190 | // Discard translucent and pure specular pixels. 191 | if ((evalContext.lobes & ((uint)LobeType::Transmission)) != 0 || 192 | ScreenSpaceReSTIR::ignoreReSTIRGI(evalContext.ggxAlpha, evalContext.diffuseWeight, evalContext.specularWeight)) 193 | { 194 | return; 195 | } 196 | // Clamp roughness to improve stability. 197 | evalContext.ggxAlpha = max(kGGXRoughnessClamp, evalContext.ggxAlpha); 198 | 199 | float4 posClip = mul(float4(evalContext.pos, 1), viewProj); 200 | float3 worldPosition = computeRayOrigin(sfd.pos, -sfd.faceNormal); 201 | float3 worldNormal = evalContext.N; 202 | 203 | 204 | // Reproject pixel position. 205 | float4 prevClip = mul(float4(worldPosition, 1.f), prevViewProj); 206 | float3 prevScreen = prevClip.xyz / prevClip.w; 207 | float2 prevUV = prevScreen.xy * float2(0.5f, -0.5f) + 0.5f; 208 | uint2 prevID = clamp(prevUV * frameDim.xy, 0, frameDim.xy - 1); 209 | uint prevIdx = toLinearIndex(prevID); 210 | 211 | // Validate temporal reprojection. 212 | bool isPrevValid = evalContext.isValid() && frameIndex > 0 && !forceClearReservoirs && all(prevUV > 0.f) && all(prevUV < 1.f); 213 | if (isPrevValid) 214 | { 215 | uint2 prevPixel = prevUV * frameDim.xy; 216 | const uint prevPixelIndex = getPixelIndex(prevPixel); 217 | 218 | // Setup evaluation context. 219 | EvalContext prevEvalContext = EvalContext::create(prevPixelIndex, frameDim, SurfaceData::createFromPacked(prevSurfaceData[prevPixelIndex])); 220 | 221 | isPrevValid &= length(prevEvalContext.pos - evalContext.pos) < 0.1f && dot(prevEvalContext.N, evalContext.N) > 0.8f; 222 | float rand = sampleNext1D(sg); 223 | float viewDepth = length(worldPosition - cameraOrigin); 224 | float prevViewDepth = length(worldPosition - prevCameraOrigin); 225 | if (viewDepth / prevViewDepth < 0.98f && rand < 0.15f) 226 | { 227 | isPrevValid = false; 228 | } 229 | } 230 | 231 | for (uint localIndex = 0; localIndex < reservoirCount; ++localIndex) 232 | { 233 | // Read latest samples generated by create gather point shader. 234 | uint initialSampleIndex = kReSTIRGIUseReSTIRN ? localIndex : 0;// 1; 235 | GIReservoir initialSample = readReservoir(initialSamples, pixelIndex, initialSampleIndex, frameDim.x * frameDim.y); 236 | 237 | int indexOffsetBegin = 2 * localIndex; 238 | int temporalSourceIndex = indexOffsetBegin; 239 | int spatialSourceIndex = indexOffsetBegin + 1; 240 | int temporalTargetIndex = temporalSourceIndex; 241 | int spatialTargetIndex = spatialSourceIndex; 242 | 243 | // Use input samples only. 244 | if (kReSTIRMode == ReSTIRMode::InputOnly) 245 | { 246 | writeReservoir(reservoirs, pixelIndex, temporalTargetIndex, frameDim.x * frameDim.y, initialSample); 247 | writeReservoir(reservoirs, pixelIndex, spatialTargetIndex, frameDim.x * frameDim.y, initialSample); 248 | continue; 249 | } 250 | 251 | // Temporal Reuse. 252 | // Read temporal reservoir. 253 | GIReservoir temporalReservoir = readReservoir(prevReservoirs, prevIdx, temporalSourceIndex, frameDim.x * frameDim.y); 254 | if (length(temporalReservoir.creationPoint - worldPosition) > 1.f) 255 | { 256 | isPrevValid = false; 257 | } 258 | 259 | float3 radiance = initialSample.radiance; 260 | temporalReservoir.M = clamp(temporalReservoir.M, 0, temporalMaxSamples); 261 | if (!isPrevValid || temporalReservoir.age > maxSampleAge) 262 | { 263 | temporalReservoir.M = 0; 264 | } 265 | 266 | // Compute reuse weight. 267 | float tf = evalTargetFunction(temporalReservoir.radiance, worldNormal, worldPosition, temporalReservoir.position, evalContext); 268 | 269 | float jacobian = 1.f; 270 | 271 | if (enableTemporalJacobian) 272 | { 273 | float3 offsetB = temporalReservoir.position - temporalReservoir.creationPoint; 274 | float3 offsetA = temporalReservoir.position - worldPosition; 275 | 276 | float RB2 = dot(offsetB, offsetB); 277 | float RA2 = dot(offsetA, offsetA); 278 | offsetB = normalize(offsetB); 279 | offsetA = normalize(offsetA); 280 | float cosA = dot(worldNormal, offsetA); 281 | float cosB = dot(temporalReservoir.creationNormal, offsetB); 282 | float cosPhiA = -dot(offsetA, temporalReservoir.normal); 283 | float cosPhiB = -dot(offsetB, temporalReservoir.normal); 284 | 285 | if (cosA <= 0.f || cosPhiA <= 0.f || RA2 <= 0.f || RB2 <= 0.f || cosB <= 0.f || cosPhiB <= 0.f) 286 | { 287 | tf = 0.f; 288 | } 289 | 290 | // assuming visible 291 | 292 | // Calculate Jacobian determinant and weight. 293 | const float maxJacobian = enableJacobianClamping ? jacobianClampThreshold : largeFloat; 294 | jacobian = RA2 * cosPhiB <= 0.f ? 0.f : clamp(RB2 * cosPhiA / (RA2 * cosPhiB), 0.f, maxJacobian); 295 | 296 | tf *= jacobian; 297 | } 298 | 299 | float wSum = max(0.f, temporalReservoir.avgWeight) * temporalReservoir.M * tf; 300 | print("tf", tf); 301 | 302 | float pNew = evalTargetFunction(radiance, worldNormal, worldPosition, initialSample.position, evalContext); 303 | float wi = initialSample.avgWeight <= 0.f ? 0.f : pNew * initialSample.avgWeight; 304 | 305 | // Update temporal reservoir. 306 | bool selectedNew = updateReservoir(wi, initialSample, sg, wSum, temporalReservoir); 307 | 308 | print("wSum", wSum); 309 | float avgWSum = wSum / temporalReservoir.M; 310 | pNew = evalTargetFunction(temporalReservoir.radiance, temporalReservoir.creationNormal, temporalReservoir.creationPoint, temporalReservoir.position, evalContext); 311 | temporalReservoir.avgWeight = pNew <= 0.f ? 0.f : avgWSum / pNew; 312 | print("avgWSum", avgWSum); 313 | print("pNew", pNew); 314 | print("temporalReservoir.avgWeight", temporalReservoir.avgWeight); 315 | temporalReservoir.M = clamp(temporalReservoir.M, 0, temporalMaxSamples); 316 | temporalReservoir.age++; 317 | temporalReservoir.creationPoint = worldPosition; 318 | temporalReservoir.creationNormal = worldNormal; 319 | 320 | writeReservoir(reservoirs, pixelIndex, temporalTargetIndex, frameDim.x * frameDim.y, temporalReservoir); 321 | 322 | if (kReSTIRMode == ReSTIRMode::TemporalOnly) 323 | { 324 | writeReservoir(reservoirs, pixelIndex, spatialTargetIndex, frameDim.x* frameDim.y, temporalReservoir); 325 | continue; 326 | } 327 | 328 | // Spatial Reuse. 329 | // Read spatial reservoir. 330 | 331 | GIReservoir spatialReservoir = readReservoir(prevReservoirs, prevIdx, spatialSourceIndex, frameDim.x * frameDim.y); 332 | 333 | spatialReservoir.M = max(0, spatialReservoir.M); 334 | if (!isPrevValid || spatialReservoir.age > maxSampleAge) 335 | { 336 | spatialReservoir.M = 0; 337 | } 338 | 339 | // Add near sample to s reservoir. 340 | uint prevIdx2; 341 | GIReservoir neighborReservoir = temporalReservoir; 342 | float wSumS = max(0.f, spatialReservoir.avgWeight) * spatialReservoir.M * evalTargetFunction(spatialReservoir.radiance, spatialReservoir.creationNormal, spatialReservoir.creationPoint, spatialReservoir.position, evalContext); 343 | 344 | // Determine maximum iteration based on current sample count. 345 | // If sample count is low, use more iterations to boost sample count. 346 | const float fastReuseRatio = 0.5f; 347 | const float fastReuseThreshold = spatialMaxSamples * fastReuseRatio; 348 | const int normalIteration = 3; 349 | const int fastReuseIteration = 10; 350 | 351 | int maxIteration = spatialReservoir.M > fastReuseThreshold ? normalIteration : fastReuseIteration; 352 | 353 | const float searchRadiusRatio = 0.1f; 354 | float searchRadius = frameDim.x * searchRadiusRatio; 355 | 356 | // Initialize reuse history. 357 | float3 positionList[10]; 358 | float3 normalList[10]; 359 | int MList[10]; 360 | int nReuse = 0; 361 | int reuseID = 0; 362 | positionList[nReuse] = worldPosition; 363 | normalList[nReuse] = worldNormal; 364 | MList[nReuse] = spatialReservoir.M; 365 | nReuse++; 366 | spatialReservoir.creationPoint = worldPosition; 367 | spatialReservoir.creationNormal = worldNormal; 368 | 369 | // Search and reuse neighbor samples. 370 | const uint startIndex = sampleNext1D(sg) * kNeighborOffsetCount; 371 | 372 | for (int i = 0; i < maxIteration; i++) 373 | { 374 | // Get search radius. 375 | const float radiusShrinkRatio = 0.5f; 376 | const float minSearchRadius = 10.f; 377 | searchRadius = max(searchRadius* radiusShrinkRatio, minSearchRadius); 378 | // Randomly sample a neighbor. 379 | float3 randOffset = sampleNext3D(sg); 380 | randOffset = randOffset * 2.f - 1.f; 381 | int2 neighborID = prevID + randOffset.xy * searchRadius; 382 | 383 | uint2 boundary = frameDim.xy - 1; 384 | neighborID.x = neighborID.x < 0 ? -neighborID.x : (neighborID.x > boundary.x ? 2 * boundary.x - neighborID.x : neighborID.x); 385 | neighborID.y = neighborID.y < 0 ? -neighborID.y : (neighborID.y > boundary.y ? 2 * boundary.y - neighborID.y : neighborID.y); 386 | 387 | // Check geometric similarity. 388 | float4 neighborNormalDepth = unpackNormalDepth(prevNormalDepth[neighborID]); 389 | if (!evalContext.isValidNeighbor(neighborNormalDepth, normalThreshold, depthThreshold)) 390 | continue; 391 | 392 | // Read neighbor's spatial reservoir. 393 | prevIdx2 = toLinearIndex(neighborID); 394 | 395 | bool bReuseSpatialSample = (kReSTIRMode == ReSTIRMode::TemporalAndUnbiasedSpatial ? i % 2 == 1 : 0); 396 | neighborReservoir = readReservoir(prevReservoirs, prevIdx2, bReuseSpatialSample ? spatialSourceIndex : temporalSourceIndex, frameDim.x * frameDim.y); 397 | 398 | // Discard black samples. 399 | if (neighborReservoir.M <= 0) 400 | { 401 | continue; 402 | } 403 | 404 | // Calculate target function. 405 | float3 offsetB = neighborReservoir.position - neighborReservoir.creationPoint; 406 | float3 offsetA = neighborReservoir.position - worldPosition; 407 | float pNewTN = evalTargetFunction(neighborReservoir.radiance, worldNormal, worldPosition, neighborReservoir.position, evalContext); 408 | // Discard back-face. 409 | if (dot(worldNormal, offsetA) <= 0.f) 410 | { 411 | pNewTN = 0.f; 412 | } 413 | 414 | float RB2 = dot(offsetB, offsetB); 415 | float RA2 = dot(offsetA, offsetA); 416 | offsetB = normalize(offsetB); 417 | offsetA = normalize(offsetA); 418 | float cosA = dot(worldNormal, offsetA); 419 | float cosB = dot(neighborReservoir.creationNormal, offsetB); 420 | float cosPhiA = -dot(offsetA, neighborReservoir.normal); 421 | float cosPhiB = -dot(offsetB, neighborReservoir.normal); 422 | if (cosB <= 0.f || cosPhiB <= 0.f) 423 | { 424 | continue; 425 | } 426 | if (cosA <= 0.f || cosPhiA <= 0.f || RA2 <= 0.f || RB2 <= 0.f) 427 | { 428 | pNewTN = 0.f; 429 | } 430 | 431 | bool isVisible = evalSegmentVisibility(computeRayOrigin(worldPosition, worldNormal), neighborReservoir.position); 432 | if (!isVisible) 433 | { 434 | pNewTN = 0.f; 435 | } 436 | 437 | // Calculate Jacobian determinant and weight. 438 | const float maxJacobian = enableJacobianClamping ? jacobianClampThreshold : largeFloat; 439 | float jacobian = RA2 * cosPhiB <= 0.f ? 0.f : clamp(RB2 * cosPhiA / (RA2 * cosPhiB), 0.f, maxJacobian); 440 | float wiTN = clamp(neighborReservoir.avgWeight * pNewTN * neighborReservoir.M * jacobian, 0.f, largeFloat); 441 | 442 | // Conditionally update spatial reservoir. 443 | bool isUpdated = updateReservoir(wiTN, neighborReservoir, sg, wSumS, spatialReservoir); 444 | if (isUpdated) reuseID = nReuse; 445 | 446 | // Update reuse history. 447 | positionList[nReuse] = neighborReservoir.creationPoint; 448 | normalList[nReuse] = neighborReservoir.creationNormal; 449 | MList[nReuse] = neighborReservoir.M; 450 | nReuse++; 451 | 452 | // Expand search radius. 453 | const float radiusExpandRatio = 3.f; 454 | searchRadius *= radiusExpandRatio; 455 | } 456 | 457 | // Calculate weight of spatial reuse. 458 | float m; 459 | if (kReSTIRMode == ReSTIRMode::TemporalAndBiasedSpatial) 460 | { 461 | m = spatialReservoir.M <= 0.f ? 0.f : 1.f / float(spatialReservoir.M); 462 | } 463 | else if (kReSTIRMode == ReSTIRMode::TemporalAndUnbiasedSpatial) 464 | { 465 | // Trace extra rays if unbiased spatial reuse is enabled. 466 | float totalWeight = 0.f; 467 | float chosenWeight = 0.f; 468 | int nValid = 0; 469 | int Z = 0; 470 | for (int i = 0; i < nReuse; i++) 471 | { 472 | bool isVisible = true; 473 | bool shouldTest = true; 474 | float3 directionVec = spatialReservoir.position - positionList[i]; 475 | if (dot(directionVec, normalList[i]) < 0.f) 476 | { 477 | shouldTest = false; 478 | isVisible = false; 479 | } 480 | if (shouldTest) 481 | { 482 | isVisible = evalSegmentVisibility(computeRayOrigin(positionList[i], normalList[i]), spatialReservoir.position); 483 | } 484 | // Discard new sample if it is occluded. 485 | if (isVisible) 486 | { 487 | if (kReSTIRMISWeight == 0) 488 | totalWeight += MList[i]; 489 | else 490 | { 491 | float misWeight = saturate(dot(normalList[i], normalize(directionVec))) * luminance(spatialReservoir.radiance); 492 | totalWeight += misWeight * MList[i]; 493 | if (reuseID == i) 494 | { 495 | chosenWeight = misWeight; 496 | } 497 | } 498 | nValid++; 499 | } 500 | else if (i == 0) 501 | { 502 | break; 503 | } 504 | } 505 | 506 | if (kReSTIRMISWeight == 0) m = totalWeight <= 0.f ? 0.f : 1.f / totalWeight; 507 | else m = totalWeight <= 0.f ? 0.f : chosenWeight / totalWeight; 508 | } 509 | 510 | pNew = evalTargetFunction(spatialReservoir.radiance, worldNormal, worldPosition, spatialReservoir.position, evalContext); 511 | float mWeight = pNew <= 0.f ? 0.f : 1.f / pNew * m; 512 | spatialReservoir.M = clamp(spatialReservoir.M, 0, spatialMaxSamples); 513 | float W = wSumS * mWeight; 514 | // TODO: add UI control for this 515 | const float maxSpatialWeight = enableSpatialWeightClamping ? spatialWeightClampThreshold : largeFloat; 516 | spatialReservoir.avgWeight = clamp(W, 0.f, maxSpatialWeight); 517 | spatialReservoir.age++; 518 | 519 | // Write spatial reservoir. 520 | writeReservoir(reservoirs, pixelIndex, spatialTargetIndex, frameDim.x* frameDim.y, spatialReservoir); 521 | } 522 | } 523 | }; 524 | 525 | ParameterBlock gGIResampling; 526 | //cbuffer CB 527 | //{ 528 | // GIResampling gGIResampling; 529 | //} 530 | 531 | [numthreads(16, 16, 1)] 532 | void main(uint3 dispatchThreadId : SV_DispatchThreadID) 533 | { 534 | printSetPixel(dispatchThreadId.xy); 535 | gGIResampling.execute(dispatchThreadId.xy); 536 | } 537 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/GIReservoir.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import Utils.Math.PackedFormats; 11 | 12 | struct PackedGIReservoir 13 | { 14 | uint4 creationGeometry; ///< Visible point's position and normal. 15 | uint4 hitGeometry; ///< Hit point's position and normal. 16 | uint4 lightInfo; ///< Reservoir information. 17 | }; 18 | 19 | struct GIReservoir 20 | { 21 | float3 creationPoint; ///< Visible point's position. 22 | float3 creationNormal; ///< Visible point's normal. 23 | float3 position; ///< Hit point's position. 24 | float3 normal; ///< Hit point's normal. 25 | float3 radiance; ///< Chosen sample's radiance. 26 | int M; ///< Input sample count. 27 | float avgWeight; ///< Average weight for chosen sample. 28 | uint age; ///< Number of frames the sample has survived. 29 | 30 | static GIReservoir createEmpty() 31 | { 32 | GIReservoir reservoir = {}; 33 | reservoir.creationPoint = float3(0.f); 34 | reservoir.creationNormal = float3(0.f, 0.f, 1.f); 35 | reservoir.position = float3(0.f, 0.f, 1.f); 36 | reservoir.normal = float3(0.f, 0.f, -1.f); 37 | reservoir.radiance = float3(0.f); 38 | reservoir.M = 0; 39 | reservoir.avgWeight = 1.f; 40 | reservoir.age = 0; 41 | return reservoir; 42 | } 43 | 44 | static GIReservoir createFromPacked(const PackedGIReservoir packed) 45 | { 46 | GIReservoir reservoir = {}; 47 | reservoir.unpack(packed); 48 | return reservoir; 49 | } 50 | 51 | PackedGIReservoir pack() 52 | { 53 | PackedGIReservoir packed; 54 | packed.creationGeometry.xyz = asuint(creationPoint); 55 | packed.creationGeometry.w = encodeNormal2x16(creationNormal); 56 | packed.hitGeometry.xyz = asuint(position); 57 | packed.hitGeometry.w = encodeNormal2x16(normal); 58 | packed.lightInfo.x = f32tof16(radiance.x) | (f32tof16(radiance.y) << 16); 59 | packed.lightInfo.y = f32tof16(radiance.z) | (M << 16); 60 | packed.lightInfo.z = asuint(avgWeight); 61 | packed.lightInfo.w = age; 62 | return packed; 63 | } 64 | 65 | [mutating] void unpack(const PackedGIReservoir packed) 66 | { 67 | creationPoint = asfloat(packed.creationGeometry.xyz); 68 | creationNormal = decodeNormal2x16(packed.creationGeometry.w); 69 | position = asfloat(packed.hitGeometry.xyz); 70 | normal = decodeNormal2x16(packed.hitGeometry.w); 71 | radiance.x = f16tof32(packed.lightInfo.x & 0xffff); 72 | radiance.y = f16tof32(packed.lightInfo.x >> 16); 73 | radiance.z = f16tof32(packed.lightInfo.y & 0xffff); 74 | M = (packed.lightInfo.y >> 16); 75 | avgWeight = asfloat(packed.lightInfo.z); 76 | age = packed.lightInfo.w; 77 | } 78 | }; 79 | 80 | GIReservoir readReservoir(StructuredBuffer reservoirBuffer, uint baseIndex, uint sampleIndex, uint elementCount) 81 | { 82 | uint index = baseIndex + sampleIndex * elementCount; 83 | return GIReservoir.createFromPacked(reservoirBuffer[index]); 84 | } 85 | 86 | GIReservoir readReservoir(RWStructuredBuffer reservoirBuffer, uint baseIndex, uint sampleIndex, uint elementCount) 87 | { 88 | uint index = baseIndex + sampleIndex * elementCount; 89 | return GIReservoir.createFromPacked(reservoirBuffer[index]); 90 | } 91 | 92 | void writeReservoir(RWStructuredBuffer reservoirBuffer, uint baseIndex, uint sampleIndex, uint elementCount, GIReservoir reservoir) 93 | { 94 | uint index = baseIndex + sampleIndex * elementCount; 95 | reservoirBuffer[index] = reservoir.pack(); 96 | } 97 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/Params.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | 11 | /** Enumeration of available debug outputs. 12 | Note: Keep in sync with definition in ScreenSpaceReSTIR.h 13 | */ 14 | enum class DebugOutput 15 | { 16 | Disabled, 17 | Position, 18 | Depth, 19 | Normal, 20 | FaceNormal, 21 | DiffuseWeight, 22 | SpecularWeight, 23 | SpecularRoughness, 24 | PackedNormal, 25 | PackedDepth, 26 | InitialWeight, 27 | TemporalReuse, 28 | SpatialReuse, 29 | FinalSampleDir, 30 | FinalSampleDistance, 31 | FinalSampleLi, 32 | }; 33 | 34 | enum class SpatialReusePattern 35 | #ifdef HOST_CODE 36 | : uint32_t 37 | #endif 38 | { 39 | Default = 0, // same as in ScreenSpaceReSTIR 40 | }; 41 | 42 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/ReflectTypes.cs.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | 11 | /** Dummy compute program for reflection of the structured buffer types. 12 | 13 | Falcor requires reflection data to be able to create a structured buffer, 14 | but the programs are not yet ready to be used when we create the buffers. 15 | Therefore we create this dummy program to make it easier. 16 | */ 17 | 18 | import SurfaceData; 19 | //import LocalEmissiveTriangle; 20 | import FinalSample; 21 | //import Lights; 22 | //import Reservoir; 23 | import GIReservoir; 24 | 25 | StructuredBuffer surfaceData; 26 | //StructuredBuffer emissiveTriangles; 27 | StructuredBuffer finalSamples; 28 | //StructuredBuffer lightTileData; 29 | //StructuredBuffer reservoirs; 30 | StructuredBuffer giReservoirs; 31 | 32 | void main() {} 33 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/ScreenSpaceReSTIR.h: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | #pragma once 11 | #include "Falcor.h" 12 | #include "Utils/Sampling/AliasTable.h" 13 | #include "Utils/Sampling/SampleGenerator.h" 14 | #include "Utils/Debug/PixelDebug.h" 15 | #include 16 | #include 17 | #include "Params.slang" 18 | 19 | namespace Falcor 20 | { 21 | /** Implementation of ReSTIR for direct and global illumination. 22 | 23 | The direct illumination part (ReSTIR DI) is based on 24 | "Spatiotemporal reservoir resampling for real-time ray tracing with dynamic direct lighting" 25 | by Benedikt Bitterli et al. from 2020. 26 | 27 | The global illumination part (ReSTIR GI) is based on 28 | "ReSTIR GI: Path Resampling for Real-Time Path Tracing" 29 | by Yaobin Ouyang et al. from 2021. 30 | 31 | Integrating this module into a renderer requires a few steps: 32 | 33 | - Host: Call ScreenSpaceReSTIR::beginFrame() on to begin a new frame. 34 | - Device: Populate surface data (GBuffer) using ScreenSpaceReSTIR::setSurfaceData()/setInvalidSurfaceData(). 35 | 36 | For ReSTIR DI: 37 | 38 | - Host: Call ScreenSpaceReSTIR::updateReSTIRDI() on to run the ReSTIR DI algorithm. 39 | - Device: Get final light samples using ScreenSpaceReSTIR::getFinalSample() and perform shading. 40 | 41 | For ReSTIR GI: 42 | 43 | - Device: Use a path tracer to generate initial samples, store them using ScreenSpaceReSTIR::setGIInitialSample(). 44 | - Host: Call ScreenSpaceReSTIR::updateReSTIRGI() to run the ReSTIR GI algorithm. 45 | - Device: Write a pass to get final samples using ScreenSpaceReSTIR::getGIFinalSample() and perform shading. 46 | 47 | Finally at the end of frame: 48 | 49 | - Host: Call ScreenSpaceReSTIR::endFrame() to end the frame. 50 | 51 | Also see the ScreenSpaceReSTIRPass render pass for a minimal example on how to use the sampler. 52 | */ 53 | class FALCOR_API ScreenSpaceReSTIR 54 | { 55 | public: 56 | using SharedPtr = std::shared_ptr; 57 | 58 | /** Enumeration of available debug outputs. 59 | Note: Keep in sync with definition in Params.slang 60 | */ 61 | enum class DebugOutput 62 | { 63 | Disabled, 64 | Position, 65 | Depth, 66 | Normal, 67 | FaceNormal, 68 | DiffuseWeight, 69 | SpecularWeight, 70 | SpecularRoughness, 71 | PackedNormal, 72 | PackedDepth, 73 | InitialWeight, 74 | TemporalReuse, 75 | SpatialReuse, 76 | FinalSampleDir, 77 | FinalSampleDistance, 78 | FinalSampleLi, 79 | }; 80 | 81 | 82 | enum class ReSTIRMode 83 | { 84 | InputOnly = 0, 85 | TemporalOnly = 1, 86 | TemporalAndBiasedSpatial = 2, 87 | TemporalAndUnbiasedSpatial = 3 88 | }; 89 | 90 | enum class TargetPDF 91 | { 92 | IncomingRadiance = 0, 93 | OutgoingRadiance = 1 94 | }; 95 | 96 | /** Configuration options. 97 | */ 98 | struct Options 99 | { 100 | //using SharedPtr = std::shared_ptr; 101 | //static SharedPtr create() { return SharedPtr(new Options()); }; 102 | //static SharedPtr create(const Options& other) { return SharedPtr(new Options(other)); }; 103 | 104 | //Options() { } 105 | //Options(const Options& other) { *this = other; } 106 | // Common Options for ReSTIR DI and GI. 107 | 108 | //bool useReSTIRDI = true; 109 | //bool useReSTIRGI = true; 110 | float normalThreshold = 0.5f; ///< Normal cosine threshold for reusing temporal samples or spatial neighbor samples. 111 | float depthThreshold = 0.1f; ///< Relative depth threshold for reusing temporal samples or spatial neighbor samples. 112 | 113 | // Options for ReSTIR DI only. 114 | 115 | // Light sampling options. 116 | //float envLightWeight = 1.f; ///< Relative weight for selecting the env map when sampling a light. 117 | //float emissiveLightWeight = 1.f; ///< Relative weight for selecting an emissive light when sampling a light. 118 | //float analyticLightWeight = 1.f; ///< Relative weight for selecting an analytical light when sampling a light. 119 | 120 | //bool useEmissiveTextureForSampling = true; ///< Use emissive texture for light sample evaluation. 121 | //bool useEmissiveTextureForShading = true; ///< Use emissive texture for shading. 122 | //bool useLocalEmissiveTriangles = false; ///< Use local emissive triangle data structure (for more efficient sampling/evaluation). 123 | 124 | // Light tile options. 125 | //uint32_t lightTileCount = 128; ///< Number of light tiles to compute. 126 | //uint32_t lightTileSize = 1024; ///< Number of lights per light tile. 127 | 128 | // Visibility options. 129 | bool useAlphaTest = true; ///< Use alpha testing on non-opaque triangles. 130 | //bool useInitialVisibility = true; ///< Check visibility on inital sample. 131 | //bool useFinalVisibility = true; ///< Check visibility on final sample. 132 | //bool reuseFinalVisibility = false; ///< Reuse final visibility temporally. 133 | 134 | // Initial resampling options. 135 | //uint32_t screenTileSize = 8; ///< Size of screen tile that samples from the same light tile. 136 | //uint32_t initialLightSampleCount = 32; ///< Number of initial light samples to resample per pixel. 137 | //uint32_t initialBRDFSampleCount = 1; ///< Number of initial BRDF samples to resample per pixel. 138 | //float brdfCutoff = 0.f; ///< Value in range [0,1] to determine how much to shorten BRDF rays. 139 | 140 | // Temporal resampling options. 141 | //bool useTemporalResampling = true; ///< Enable temporal resampling. 142 | //uint32_t maxHistoryLength = 20; ///< Maximum temporal history length. 143 | 144 | // Spatial resampling options. 145 | //bool useSpatialResampling = true; ///< Enable spatial resampling. 146 | //uint32_t spatialIterations = 1; ///< Number of spatial resampling iterations. 147 | //uint32_t spatialNeighborCount = 5; ///< Number of neighbor samples to resample per pixel and iteration. 148 | //uint32_t spatialGatherRadius = 30; ///< Radius to gather samples from. 149 | 150 | // General options. 151 | //bool usePairwiseMIS = true; ///< Use pairwise MIS when combining samples. 152 | //bool unbiased = true; ///< Use unbiased version of ReSTIR by querying extra visibility rays. 153 | 154 | DebugOutput debugOutput = DebugOutput::Disabled; 155 | 156 | bool enabled = true; // (I think) now controls both ReSTIR GI and DI 157 | 158 | // Options for ReSTIR GI only. 159 | 160 | ReSTIRMode reSTIRMode = ReSTIRMode::TemporalAndUnbiasedSpatial; ///< ReSTIR GI Mode. 161 | TargetPDF targetPdf = TargetPDF::OutgoingRadiance; ///< Target function mode. 162 | uint32_t reSTIRGITemporalMaxSamples = 30; ///< Maximum M value for temporal reuse stage. 163 | uint32_t reSTIRGISpatialMaxSamples = 100; ///< Maximum M value for spatial reuse stage. 164 | uint32_t reSTIRGIReservoirCount = 1; ///< Number of reservoirs per pixel. 165 | bool reSTIRGIUseReSTIRN = true; 166 | uint32_t reSTIRGIMaxSampleAge = 100; ///< Maximum frames that a sample can survive. 167 | float diffuseThreshold = 0.f;// 0.17f; ///< Pixels with diffuse component under this threshold will not use ReSTIR GI. 168 | float reSTIRGISpatialWeightClampThreshold = 10.f; 169 | bool reSTIRGIEnableSpatialWeightClamping = true; 170 | float reSTIRGIJacobianClampTreshold = 10.f; 171 | bool reSTIRGIEnableJacobianClamping = false; 172 | bool reSTIREnableTemporalJacobian = true; 173 | 174 | bool forceClearReservoirs = false; ///< Force clear temporal and spatial reservoirs. 175 | }; 176 | 177 | static_assert(std::is_trivially_copyable(), "Options needs to be trivially copyable"); 178 | 179 | /** Create a new instance of the ReSTIR sampler. 180 | \param[in] pScene Scene. 181 | \param[in] options Configuration options. 182 | */ 183 | static SharedPtr create(const Scene::SharedPtr& pScene, const Options& options, int numReSTIRInstances = 1, int ReSTIRInstanceID=0); 184 | 185 | /** Get a list of shader defines for using the ReSTIR sampler. 186 | \return Returns a list of defines. 187 | */ 188 | Program::DefineList getDefines() const; 189 | 190 | /** Bind the ReSTIR sampler to a given shader var. 191 | \param[in] var The shader variable to set the data into. 192 | */ 193 | void setShaderData(const ShaderVar& var) const; 194 | 195 | void setShaderDataRoot(const ShaderVar& rootVar) const; 196 | 197 | /** Render the GUI. 198 | \return True if options were changed, false otherwise. 199 | */ 200 | bool renderUI(Gui::Widgets& widget); 201 | 202 | /** Returns the current configuration. 203 | */ 204 | const Options& getOptions() const { return mOptions; } 205 | 206 | /** Set the configuration. 207 | */ 208 | //void setOptions(const Options& options); 209 | 210 | /** Begin a frame. 211 | Must be called once at the beginning of each frame. 212 | \param[in] pRenderContext Render context. 213 | \param[in] frameDim Current frame dimension. 214 | */ 215 | void beginFrame(RenderContext* pRenderContext, const uint2& frameDim); 216 | 217 | /** End a frame. 218 | Must be called one at the end of each frame. 219 | \param[in] pRenderContext Render context. 220 | */ 221 | void endFrame(RenderContext* pRenderContext); 222 | 223 | /** Update the ReSTIR sampler. 224 | This runs the ReSTIR DI algorithm and prepares a set of final samples to be queried afterwards. 225 | Must be called once between beginFrame() and endFrame(). 226 | \param[in] pRenderContext Render context. 227 | \param[in] pMotionVectors Motion vectors for temporal reprojection. 228 | */ 229 | //void updateReSTIRDI(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors); 230 | 231 | /** Update the ReSTIR sampler. 232 | This runs the ReSTIR GI algorithm. 233 | Must be called once between beginFrame() and endFrame(). 234 | \param[in] pRenderContext Render context. 235 | \param[in] pMotionVectors Motion vectors for temporal reprojection. 236 | */ 237 | void updateReSTIRGI(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors); 238 | 239 | /** Get the debug output texture. 240 | \return Returns the debug output texture. 241 | */ 242 | //const Texture::SharedPtr& getDebugOutputTexture() const { return mpDebugOutputTexture; } 243 | 244 | /** Get the pixel debug component. 245 | \return Returns the pixel debug component. 246 | */ 247 | const PixelDebug::SharedPtr& getPixelDebug() const { return mpPixelDebug; } 248 | 249 | /** Register script bindings. 250 | */ 251 | static void scriptBindings(pybind11::module& m); 252 | 253 | void enablePass(bool enabled); 254 | 255 | bool mRequestParentRecompile = true; 256 | 257 | void resetReservoirCount() { mRecompile = true; mFrameIndex = 0; }; 258 | 259 | void copyRecompileStateFromOtherInstance(ScreenSpaceReSTIR::SharedPtr other) 260 | { 261 | mRecompile = other->mRecompile; 262 | mRequestReallocate = other->mRequestReallocate; 263 | mResetTemporalReservoirs = other->mResetTemporalReservoirs; 264 | }; 265 | Options mOptions; ///< Configuration options. 266 | 267 | private: 268 | ScreenSpaceReSTIR(const Scene::SharedPtr& pScene, const Options& options, int numReSTIRInstances=1, int ReSTIRInstanceID=0); 269 | 270 | void prepareResources(RenderContext* pRenderContext); 271 | void prepareLighting(RenderContext* pRenderContext); 272 | 273 | void updatePrograms(); 274 | 275 | //void updateEmissiveTriangles(RenderContext* pRenderContext); 276 | //void generateLightTiles(RenderContext* pRenderContext); 277 | //void initialResampling(RenderContext* pRenderContext); 278 | //void temporalResampling(RenderContext* pRenderContext, const Texture::SharedPtr& pMotionVectors); 279 | //void spatialResampling(RenderContext* pRenderContext); 280 | //void evaluateFinalSamples(RenderContext* pRenderContext); 281 | 282 | void reSTIRGIClearPass(RenderContext* pRenderContext); 283 | 284 | Program::DefineList getLightsDefines() const; 285 | void setLightsShaderData(const ShaderVar& var) const; 286 | 287 | std::vector computeEnvLightLuminance(RenderContext* pRenderContext, const Texture::SharedPtr& texture); 288 | AliasTable::SharedPtr buildEnvLightAliasTable(uint32_t width, uint32_t height, const std::vector& luminances, std::mt19937& rng); 289 | AliasTable::SharedPtr buildEmissiveLightAliasTable(RenderContext* pRenderContext, const LightCollection::SharedPtr& lightCollection, std::mt19937& rng); 290 | AliasTable::SharedPtr buildAnalyticLightAliasTable(RenderContext* pRenderContext, const std::vector& lights, std::mt19937& rng); 291 | 292 | /** Create a 1D texture with random offsets within a unit circle around (0,0). 293 | The texture is RG8Snorm for compactness and has no mip maps. 294 | \param[in] sampleCount Number of samples in the offset texture. 295 | */ 296 | Texture::SharedPtr createNeighborOffsetTexture(uint32_t sampleCount); 297 | 298 | Scene::SharedPtr mpScene; ///< Scene. 299 | 300 | std::mt19937 mRng; ///< Random generator. 301 | 302 | PixelDebug::SharedPtr mpPixelDebug; ///< Pixel debug component. 303 | 304 | uint2 mFrameDim = uint2(0); ///< Current frame dimensions. 305 | uint32_t mFrameIndex = 0; ///< Current frame index. 306 | 307 | uint32_t mReSTIRInstanceIndex = 0; ///< The index of the ReSTIR instance, used as initial mFrameIndex 308 | uint32_t mNumReSTIRInstances = 1; ///< Number of ReSTIR instances that are executed together 309 | 310 | ComputePass::SharedPtr mpReflectTypes; ///< Pass for reflecting types. 311 | 312 | // ReSTIR DI passes. 313 | //ComputePass::SharedPtr mpUpdateEmissiveTriangles; ///< Pass for updating the local emissive triangle data. 314 | //ComputePass::SharedPtr mpGenerateLightTiles; ///< Pass for generating the light tiles. 315 | //ComputePass::SharedPtr mpInitialResampling; ///< Pass for initial resampling. 316 | //ComputePass::SharedPtr mpTemporalResampling; ///< Pass for temporal resampling. 317 | //ComputePass::SharedPtr mpSpatialResampling; ///< Pass for spatial resampling. 318 | //ComputePass::SharedPtr mpEvaluateFinalSamples; ///< Pass for evaluating the final samples. 319 | 320 | // ReSTIR GI passes. 321 | ComputePass::SharedPtr mpGIClearReservoirs; ///< Pass for clearing reservoirs. 322 | ComputePass::SharedPtr mpGIResampling; ///< Pass for spatio-temporal resampling. 323 | 324 | // ReSTIR DI resources. 325 | //Buffer::SharedPtr mpEnvLightLuminance; ///< Buffer with luminance values of the env map. 326 | //float mEnvLightLuminanceFactor; ///< Scalar luminance factor based on env map intensity and tint. 327 | //Buffer::SharedPtr mpEmissiveTriangles; ///< Buffer with emissive triangle data. 328 | 329 | //AliasTable::SharedPtr mpEnvLightAliasTable; ///< Alias table for sampling the env map. 330 | //AliasTable::SharedPtr mpEmissiveLightAliasTable; ///< Alias table for sampling emissive lights. 331 | //AliasTable::SharedPtr mpAnalyticLightAliasTable; ///< Alias table for sampling analytic lights. 332 | 333 | Buffer::SharedPtr mpSurfaceData; ///< Buffer with the current frame surface data (GBuffer). 334 | Buffer::SharedPtr mpPrevSurfaceData; ///< Buffer with the previous frame surface data (GBuffer). 335 | //Buffer::SharedPtr mpFinalSamples; ///< Buffer with the final samples. 336 | 337 | Texture::SharedPtr mpNormalDepthTexture; ///< Compact normal/depth texture used for fast neighbor pixel validation. 338 | Texture::SharedPtr mpPrevNormalDepthTexture; ///< Compact normal/depth texture used for fast neighbor pixel validation. 339 | //Texture::SharedPtr mpDebugOutputTexture; ///< Debug output texture. 340 | 341 | //Buffer::SharedPtr mpLightTileData; ///< Buffer with the light tiles (light samples). 342 | 343 | //Buffer::SharedPtr mpReservoirs; ///< Buffer containing the current reservoirs. 344 | //Buffer::SharedPtr mpPrevReservoirs; ///< Buffer containing the previous reservoirs. 345 | 346 | //Texture::SharedPtr mpNeighborOffsets; ///< 1D texture containing neighbor offsets within a unit circle. 347 | 348 | // ReSTIR GI resources. 349 | Buffer::SharedPtr mpGIInitialSamples; ///< 2D Buffer containing initial path tracing samples. 350 | Buffer::SharedPtr mpGIReservoirs[2]; ///< 2D Buffers containing GI reservoirs. 351 | glm::float3 mPrevCameraOrigin; ///< Last frame's camera origin 352 | glm::float4x4 mPrevViewProj; ///< Last frame's view projection matrix 353 | 354 | bool mRecompile = true; ///< Recompile programs on next frame if set to true. 355 | bool mRequestReallocate = false; 356 | bool mResetTemporalReservoirs = true; ///< Reset temporal reservoir buffer on next frame if set to true. 357 | 358 | int mCurRISPass = 0; 359 | int mTotalRISPasses = 0; 360 | 361 | //// Internal state 362 | //SampleGenerator::SharedPtr mpSampleGenerator; ///< GPU sample generator. 363 | 364 | //// Configuration 365 | //uint mMaxBounces = 3; ///< Max number of indirect bounces (0 = none). 366 | //bool mUseImportanceSampling = true; ///< Use importance sampling for materials. 367 | 368 | //// Ray tracing program. 369 | //struct 370 | //{ 371 | // RtProgram::SharedPtr pProgram; 372 | // RtBindingTable::SharedPtr pBindingTable; 373 | // RtProgramVars::SharedPtr pVars; 374 | //} mTracer; 375 | 376 | //struct 377 | //{ 378 | // float envLight = 0.f; 379 | // float emissiveLights = 0.f; 380 | // float analyticLights = 0.f; 381 | 382 | // /** Compute a discrete set of sample counts given the current selection probabilities. 383 | // */ 384 | // std::tuple getSampleCount(uint32_t totalCount) 385 | // { 386 | // uint32_t envCount = (uint32_t)std::floor(envLight * totalCount); 387 | // uint32_t emissiveCount = (uint32_t)std::floor(emissiveLights * totalCount); 388 | // uint32_t analyticCount = (uint32_t)std::floor(analyticLights * totalCount); 389 | // if (envCount > 0) envCount = totalCount - emissiveCount - analyticCount; 390 | // else if (emissiveCount > 0) emissiveCount = totalCount - envCount - analyticCount; 391 | // else if (analyticCount > 0) analyticCount = totalCount - envCount - emissiveCount; 392 | // return { envCount, emissiveCount, analyticCount }; 393 | // } 394 | //} 395 | //mLightSelectionProbabilities; 396 | }; 397 | } 398 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/ScreenSpaceReSTIR.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import Scene.Scene; 11 | import Scene.RaytracingInline; 12 | import Utils.Color.ColorHelpers; 13 | import SurfaceData; 14 | import FinalSample; 15 | import GIReservoir; 16 | 17 | //#ifndef SCREEN_SPACE_RESTIR_USE_DI 18 | //#define SCREEN_SPACE_RESTIR_USE_DI 0 19 | //#endif 20 | 21 | //#ifndef SCREEN_SPACE_RESTIR_USE_GI 22 | //#define SCREEN_SPACE_RESTIR_USE_GI 0 23 | //#endif 24 | 25 | #ifndef SCREEN_SPACE_RESTIR_GI_DIFFUSE_THRESHOLD 26 | #define SCREEN_SPACE_RESTIR_GI_DIFFUSE_THRESHOLD 0.17f 27 | #endif 28 | 29 | /** Public interface to the ReSTIR sampler. 30 | */ 31 | struct ScreenSpaceReSTIR 32 | { 33 | //static const bool kUseDI = SCREEN_SPACE_RESTIR_USE_DI; 34 | //static const bool kUseGI = SCREEN_SPACE_RESTIR_USE_GI; 35 | static const float kDiffuseThreshold = SCREEN_SPACE_RESTIR_GI_DIFFUSE_THRESHOLD; 36 | 37 | // Common resources. 38 | RWStructuredBuffer surfaceData; 39 | RWTexture2D normalDepth; 40 | 41 | // ReSTIR DI resources. 42 | //StructuredBuffer finalSamples; 43 | 44 | // ReSTIR GI resources. 45 | RWStructuredBuffer initialSamples; 46 | StructuredBuffer prevReservoirs; // Ping-pong buffers storing gather point samples 47 | StructuredBuffer reservoirs; 48 | 49 | // Common parameters. 50 | uint frameIndex; 51 | uint2 frameDim; 52 | uint giReservoirCount; 53 | uint _pad; 54 | 55 | /** Get linear index for one pixel. 56 | \param[in] pixel Pixel coordinate. 57 | \return Returns linear index. 58 | */ 59 | uint getPixelIndex(uint2 pixel) { return pixel.y * frameDim.x + pixel.x; } 60 | 61 | /** Determine whether a pixel should ignore ReSTIR GI. 62 | \param[in] roughness Roughness value. 63 | \param[in] diffuseWeight Diffuse component. 64 | \return Returns whether the pixel should ignore ReSTIR GI. 65 | */ 66 | static bool ignoreReSTIRGI(float roughness, float diffuseWeight, float specularWeight) 67 | { 68 | // Consider light coming from normal direction, use GGX model to calculate. 69 | // outgoing radiance through normal direction, e.g. N=L=V. 70 | roughness = max(roughness, 0.01); 71 | float diffuseComponent = diffuseWeight; 72 | float specularComponent = (specularWeight * specularWeight) / (4 * roughness * roughness); 73 | float totalLight = (diffuseComponent + specularComponent); 74 | return diffuseComponent < ScreenSpaceReSTIR::kDiffuseThreshold * totalLight; 75 | } 76 | 77 | /** Set the per-pixel surface data required for screen-space ReSTIR. 78 | \param[in] pixel Pixel coordinate. 79 | \param[in] pos Position in world-space. 80 | \param[in] depth Depth from camera. 81 | \param[in] normal Shading normal in world-space. 82 | \param[in] faceNormal Face normal in world-space. 83 | \param[in] diffuseWeight Diffuse lobe weight. 84 | \param[in] specularWeight Specular lobe weight. 85 | \param[in] specularRoughness Specular roughness (linear). 86 | */ 87 | void setSurfaceData(uint2 pixel, float3 pos, float depth, float3 normal, float3 faceNormal, float diffuseWeight, float specularWeight, float specularRoughness, uint lobes = 0) 88 | { 89 | // Store pixel data. 90 | SurfaceData sd; 91 | sd.pos = pos; 92 | sd.depth = depth; 93 | sd.normal = normal; 94 | sd.faceNormal = faceNormal; 95 | sd.diffuseWeight = diffuseWeight; 96 | sd.specularWeight = specularWeight; 97 | sd.specularRoughness = specularRoughness; 98 | sd.lobes = lobes; 99 | surfaceData[getPixelIndex(pixel)] = sd.pack(); 100 | 101 | // Store compact normal depth information for spatial reuse. 102 | normalDepth[pixel] = packNormalDepth(float4(normal, depth)); 103 | } 104 | 105 | //void setSurfaceData(const uint2 pixel, const float3 position, const float3 normal, const float3 diffuse, const float3 specular, const float specularRoughness, uint lobes = 0) 106 | //{ 107 | // if (any(pixel >= frameDim)) return; 108 | // 109 | // SurfaceData sd = {}; 110 | // sd.pos = position; 111 | // sd.normal = normal; 112 | // sd.depth = distance(gScene.camera.getPosition(), position); 113 | // 114 | // float diffuseWeight = luminance(diffuse); 115 | // float specularWeight = luminance(specular); 116 | // float sumWeights = diffuseWeight + specularWeight; 117 | // float diffuseProb = sumWeights < 1e-7f ? 1.f : (diffuseWeight / sumWeights); 118 | // 119 | // sd.diffuseWeight = diffuseWeight; 120 | // sd.specularWeight = specularWeight; 121 | // //sd.diffuseProb = diffuseProb; 122 | // sd.specularRoughness = specularRoughness; 123 | // sd.lobes = lobes; 124 | // 125 | // surfaceData[getPixelIndex(pixel)] = sd.pack(); 126 | // 127 | // // Store compact normal depth information for spatial reuse. 128 | // normalDepth[pixel] = packNormalDepth(float4(normal, sd.depth)); 129 | // 130 | // // Store surface data. 131 | // //gRTXDI.surfaceData[bufferIdx] = sd.pack(); 132 | //} 133 | 134 | /** Mark pixel as containing no valid shading data. 135 | \param[in] pixel Pixel coordinate. 136 | */ 137 | void setInvalidSurfaceData(uint2 pixel) 138 | { 139 | surfaceData[getPixelIndex(pixel)] = SurfaceData::createInvalid().pack(); 140 | } 141 | 142 | /** Get the final sample for the given pixel computed using ReSTIR DI. 143 | \param[in] pixel Pixel coordinate. 144 | \param[out] dir Direction to light in world-space. 145 | \param[out] distance Distance to light. 146 | \param[out] Li Weighted incident radiance. 147 | \return Returns true if a valid sample was returned. 148 | */ 149 | //bool getFinalSample(uint2 pixel, out float3 dir, out float distance, out float3 Li, bool useSecond = false) 150 | //{ 151 | // FinalSample finalSample = FinalSample::createFromPacked(finalSamples[getPixelIndex(pixel)]); 152 | // dir = finalSample.dir; 153 | // distance = finalSample.distance; 154 | // Li = finalSample.Li; 155 | // return distance > 0.f; 156 | //} 157 | 158 | /** Write initial sample for ReSTIR GI. 159 | \param[in] pixel Pixel coordinate. 160 | \param[in] creationPoint Position of visible point. 161 | \param[in] creationNormal Normal of visible point. 162 | \param[in] position Position of a secondary ray's hit point. 163 | \param[in] normal Normal of a secondary ray's hit point. 164 | \param[in] radiance Radiance of a secondary ray's hit point. 165 | */ 166 | void setGIInitialSample(uint2 pixel, float3 creationPoint, float3 creationNormal, float3 position, float3 normal, float3 radiance, float srcPDF, int reservoirIndexOffset=0) 167 | { 168 | GIReservoir initialSample; 169 | initialSample.creationPoint = creationPoint; 170 | initialSample.creationNormal = creationNormal; 171 | initialSample.position = position; 172 | initialSample.normal = normal; 173 | if (dot(initialSample.normal, initialSample.creationPoint - initialSample.position) < 0) 174 | { 175 | initialSample.normal *= -1; 176 | } 177 | if (dot(initialSample.creationNormal, initialSample.position - initialSample.creationPoint) < 0) 178 | { 179 | initialSample.creationNormal *= -1; 180 | } 181 | initialSample.radiance = radiance; 182 | initialSample.avgWeight = srcPDF == 0.f ? 0.f : 1.f / srcPDF;// 1; 183 | initialSample.M = 1; 184 | initialSample.age = 0; 185 | uint sampleIndexOffset = reservoirIndexOffset; 186 | uint pixelIndex = getPixelIndex(pixel); 187 | writeReservoir(initialSamples, pixelIndex, sampleIndexOffset, frameDim.x * frameDim.y, initialSample); 188 | } 189 | 190 | void setGIInitialSampleFromReservoir(uint2 pixel, GIReservoir giReservoir, float wSum, float pHat, int M) 191 | { 192 | giReservoir.avgWeight = pHat == 0.f ? 0.f : wSum / (M * pHat); 193 | giReservoir.M = 1; 194 | giReservoir.age = 0; 195 | uint sampleIndexOffset = 0;//1; 196 | uint pixelIndex = getPixelIndex(pixel); 197 | writeReservoir(initialSamples, pixelIndex, sampleIndexOffset, frameDim.x * frameDim.y, giReservoir); 198 | } 199 | 200 | void addGISample(float3 creationPoint, float3 creationNormal, float3 position, float3 normal, float3 radiance, float targetPDF, float srcPDF, float rnd, inout float wSum, inout float pHat, inout int M, inout GIReservoir giReservoir) 201 | { 202 | GIReservoir initialSample; 203 | initialSample.creationPoint = creationPoint; 204 | initialSample.creationNormal = creationNormal; 205 | initialSample.position = position; 206 | initialSample.normal = normal; 207 | if (dot(initialSample.normal, initialSample.creationPoint - initialSample.position) < 0) 208 | { 209 | initialSample.normal *= -1; 210 | } 211 | if (dot(initialSample.creationNormal, initialSample.position - initialSample.creationPoint) < 0) 212 | { 213 | initialSample.creationNormal *= -1; 214 | } 215 | initialSample.radiance = radiance; 216 | initialSample.avgWeight = srcPDF == 0.f ? 0.f : 1.f / srcPDF;// 1; 217 | 218 | float weight = targetPDF * initialSample.avgWeight; 219 | wSum += weight; 220 | M += 1; 221 | 222 | // Conditionally update reservoir. 223 | float random = rnd; 224 | bool isUpdate = random * wSum <= weight; 225 | if (isUpdate) 226 | { 227 | pHat = targetPDF; 228 | giReservoir.position = initialSample.position; 229 | giReservoir.normal = initialSample.normal; 230 | giReservoir.radiance = initialSample.radiance; 231 | giReservoir.creationNormal = initialSample.creationNormal; 232 | giReservoir.creationPoint = initialSample.creationPoint; 233 | } 234 | } 235 | 236 | 237 | 238 | /** Get the final sample for the given pixel computed using ReSTIR GI. 239 | \param[in] pixel Pixel coordinate. 240 | \param[out] creationPoint Position of visible point. 241 | \param[out] creationNormal Normal of visible point. 242 | \param[out] position Position of a secondary ray's hit point. 243 | \param[out] normal Normal of a secondary ray's hit point. 244 | \param[out] radiance Radiance of a secondary ray's hit point. 245 | \param[out] avgWeight Sample weight. 246 | */ 247 | void getGIFinalSample(uint2 pixel, uint reservoirID, out float3 creationPoint, out float3 creationNormal, out float3 position, out float3 normal, out float3 radiance, out float avgWeight) 248 | { 249 | uint pixelIndex = getPixelIndex(pixel); 250 | uint elementCount = frameDim.x * frameDim.y; 251 | uint sampleIndex = reservoirID * 2 + 1; 252 | GIReservoir reservoir = readReservoir(reservoirs, pixelIndex, sampleIndex, elementCount); 253 | creationPoint = reservoir.creationPoint; 254 | creationNormal = reservoir.creationNormal; 255 | position = reservoir.position; 256 | normal = reservoir.normal; 257 | radiance = reservoir.radiance; 258 | avgWeight = reservoir.avgWeight; 259 | } 260 | }; 261 | 262 | ParameterBlock gScreenSpaceReSTIR; 263 | //cbuffer CB 264 | //{ 265 | // ScreenSpaceReSTIR gScreenSpaceReSTIR; 266 | //} 267 | -------------------------------------------------------------------------------- /Rendering/ReSTIRGI/SurfaceData.slang: -------------------------------------------------------------------------------- 1 | /*************************************************************************** 2 | # Copyright (c) 2015-21, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | **************************************************************************/ 10 | import Utils.Math.PackedFormats; 11 | 12 | struct PackedSurfaceData 13 | { 14 | uint3 pos; 15 | uint depth; 16 | uint normal; 17 | uint faceNormal; 18 | uint weightsAndLobes; 19 | uint _pad; 20 | } 21 | 22 | /** Per-pixel surface data. 23 | */ 24 | struct SurfaceData 25 | { 26 | float3 pos; ///< Position in world-space. 27 | float depth; ///< Depth from camera (-1 if invalid data). 28 | float3 normal; ///< Shading normal in world-space. 29 | float3 faceNormal; ///< Face normal in world-space. 30 | float diffuseWeight; ///< Diffuse lobe weight. 31 | float specularWeight; ///< Specular lobe weight. 32 | float specularRoughness; ///< Specular lobe roughness (linear). 33 | uint lobes; ///< BSDF lobes 34 | 35 | static SurfaceData createFromPacked(const PackedSurfaceData packed) 36 | { 37 | SurfaceData surfaceData = {}; 38 | surfaceData.unpack(packed); 39 | return surfaceData; 40 | } 41 | 42 | static SurfaceData createInvalid() 43 | { 44 | SurfaceData surfaceData = {}; 45 | surfaceData.depth = -1.f; 46 | return surfaceData; 47 | } 48 | 49 | bool isValid() 50 | { 51 | return depth >= 0.f; 52 | } 53 | 54 | [mutating] void unpack(const PackedSurfaceData packed) 55 | { 56 | pos = asfloat(packed.pos); 57 | depth = asfloat(packed.depth); 58 | normal = decodeNormal2x16(packed.normal); 59 | faceNormal = decodeNormal2x16(packed.faceNormal); 60 | diffuseWeight = (packed.weightsAndLobes & 0xff) / float(0xff); 61 | specularWeight = ((packed.weightsAndLobes >> 8) & 0xff) / float(0xff); 62 | specularRoughness = ((packed.weightsAndLobes >> 16) & 0xff) / float(0xff); 63 | lobes = ((packed.weightsAndLobes >> 24) & 0xff); 64 | } 65 | 66 | PackedSurfaceData pack() 67 | { 68 | PackedSurfaceData packed = {}; 69 | packed.pos = asuint(pos); 70 | packed.depth = asuint(depth); 71 | packed.normal = encodeNormal2x16(normal); 72 | packed.faceNormal = encodeNormal2x16(faceNormal); 73 | packed.weightsAndLobes = saturate(diffuseWeight) * 0xff; 74 | packed.weightsAndLobes |= uint((saturate(specularWeight) * 0xff)) << 8; 75 | packed.weightsAndLobes |= uint((saturate(specularRoughness) * 0xff)) << 16; 76 | packed.weightsAndLobes |= (lobes << 24); 77 | return packed; 78 | } 79 | }; 80 | 81 | uint packNormalDepth(float4 normalDepth) 82 | { 83 | return (encodeNormal2x8(normalDepth.xyz) << 16) | (f32tof16(normalDepth.w) & 0xffff); 84 | } 85 | 86 | float4 unpackNormalDepth(uint packed) 87 | { 88 | return float4(decodeNormal2x8(packed >> 16), f16tof32(packed)); 89 | } 90 | 91 | /*************************************************************************** 92 | # Copyright (c) 2015-22, NVIDIA CORPORATION. All rights reserved. 93 | # 94 | # Redistribution and use in source and binary forms, with or without 95 | # modification, are permitted provided that the following conditions 96 | # are met: 97 | # * Redistributions of source code must retain the above copyright 98 | # notice, this list of conditions and the following disclaimer. 99 | # * Redistributions in binary form must reproduce the above copyright 100 | # notice, this list of conditions and the following disclaimer in the 101 | # documentation and/or other materials provided with the distribution. 102 | # * Neither the name of NVIDIA CORPORATION nor the names of its 103 | # contributors may be used to endorse or promote products derived 104 | # from this software without specific prior written permission. 105 | # 106 | # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY 107 | # EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 108 | # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 109 | # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 110 | # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 111 | # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 112 | # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 113 | # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY 114 | # OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 115 | # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 116 | # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 117 | **************************************************************************/ 118 | //import Utils.Math.PackedFormats; 119 | //import Utils.Math.FormatConversion; 120 | 121 | //__exported import PackedTypes; 122 | 123 | /** Stores data about primary hit points. 124 | To reduce register pressure, some of the fields are stored in packed format 125 | and decoded as being accessed through Slang properties. 126 | */ 127 | //struct SurfaceData 128 | //{ 129 | // // The following fields are stored in PackedSurfaceData. 130 | // 131 | // float3 position; ///< Position in world-space. 132 | // float viewDepth; ///< Distance from viewer to surface (negative for invalid surface). 133 | // uint packedNormal; ///< Packed surface normal in world-space (octahedral mapping). 134 | // uint packedWeights; ///< Packed diffuse/specular reflectance and roughness. 135 | // 136 | // // The following fields are *NOT* stored in PackedSurfaceData. 137 | // 138 | // float3 viewDir; ///< Direction to the viewer in world-space. 139 | // 140 | // // True if surface data is valid. 141 | // property bool valid 142 | // { 143 | // get { return viewDepth >= 0.f; } 144 | // } 145 | // 146 | // // Shading normal. 147 | // property float3 normal 148 | // { 149 | // get { return decodeNormal2x16(packedNormal); } 150 | // set { packedNormal = encodeNormal2x16(newValue); } 151 | // } 152 | // 153 | // // Diffuse reflectance. 154 | // property float diffuse 155 | // { 156 | // get { return unpackUnorm8(packedWeights); } 157 | // set { packedWeights = (packedWeights & ~0xff) | packUnorm8(newValue); } 158 | // } 159 | // 160 | // // Specular reflectance. 161 | // property float specular 162 | // { 163 | // get { return unpackUnorm8(packedWeights >> 8); } 164 | // set { packedWeights = (packedWeights & ~0xff00) | (packUnorm8(newValue) << 8); } 165 | // } 166 | // 167 | // // Specular roughness. 168 | // property float roughness 169 | // { 170 | // get { return unpackUnorm8(packedWeights >> 16); } 171 | // set { packedWeights = (packedWeights & ~0xff0000) | (packUnorm8(newValue) << 16); } 172 | // } 173 | // 174 | // // Diffuse lobe probability. 175 | // property float diffuseProb 176 | // { 177 | // get { return unpackUnorm8(packedWeights >> 24); } 178 | // set { packedWeights = (packedWeights & ~0xff000000) | (packUnorm8(newValue) << 24); } 179 | // } 180 | // 181 | // /** Initialize an invalid surface. 182 | // */ 183 | // __init() 184 | // { 185 | // this = {}; 186 | // viewDepth = -1.f; 187 | // } 188 | // 189 | // /** Initialize a surface. 190 | // \param[in] packed Packed surface data. 191 | // \param[in] viewDir View direction. 192 | // */ 193 | // __init(const PackedSurfaceData packed, const float3 viewDir) 194 | // { 195 | // position = asfloat(packed.position); 196 | // viewDepth = asfloat(packed.viewDepth); 197 | // packedNormal = packed.normal; 198 | // packedWeights = packed.weights; 199 | // this.viewDir = viewDir; 200 | // } 201 | // 202 | // /** Pack surface data. 203 | // */ 204 | // PackedSurfaceData pack() 205 | // { 206 | // PackedSurfaceData packed = {}; 207 | // packed.position = asuint(position); 208 | // packed.viewDepth = asuint(viewDepth); 209 | // packed.normal = packedNormal; 210 | // packed.weights = packedWeights; 211 | // return packed; 212 | // } 213 | //}; 214 | --------------------------------------------------------------------------------