├── .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 |
--------------------------------------------------------------------------------