├── .gitattributes ├── .gitmodules ├── LICENSE ├── README.md ├── RenderPasses └── Surfel │ ├── CMakeLists.txt │ ├── HashUtils.slang │ ├── Random.slang │ ├── Surfel.cpp │ ├── SurfelGBuffer │ ├── SurfelGBuffer.3d.slang │ ├── SurfelGBuffer.cpp │ └── SurfelGBuffer.h │ ├── SurfelGI │ ├── MultiscaleMeanEstimator.slang │ ├── OverlayMode.slang │ ├── StaticParams.slang │ ├── SurfelEvaluationPass.cs.slang │ ├── SurfelGI.cpp │ ├── SurfelGI.h │ ├── SurfelGenerationPass.cs.slang │ ├── SurfelIntegratePass.cs.slang │ ├── SurfelPreparePass.cs.slang │ ├── SurfelRayTrace.rt.slang │ ├── SurfelTypes.slang │ ├── SurfelUpdatePass.cs.slang │ └── SurfelUtils.slang │ ├── SurfelGIRenderPass │ ├── SurfelGIRenderPass.cpp │ ├── SurfelGIRenderPass.cs.slang │ └── SurfelGIRenderPass.h │ └── SurfelVBuffer │ ├── SurfelVBuffer.cpp │ ├── SurfelVBuffer.h │ └── SurfelVBuffer.rt.slang ├── docs ├── images │ └── teaser.png └── index.md └── scripts └── BasicSurfelGI.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.slang linguist-language=HLSL -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Falcor"] 2 | path = Falcor 3 | url = git@github.com:NVIDIAGameWorks/Falcor.git 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 W298 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![teaser](docs/images/teaser.png) 2 | 3 | # SurfelGI 4 | 5 | Real-Time dynamic global illumination based on surfel. This project is based on GIBS presented at SIGGRAPH 2021. Implemented with Falcor framework. 6 | 7 | - [Documentation](https://w298.github.io/SurfelGI/) 8 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_plugin(Surfel) 2 | 3 | target_sources(Surfel PRIVATE 4 | Surfel.cpp 5 | 6 | HashUtils.slang 7 | Random.slang 8 | 9 | SurfelGBuffer/SurfelGBuffer.cpp 10 | SurfelGBuffer/SurfelGBuffer.h 11 | SurfelGBuffer/SurfelGBuffer.3d.slang 12 | 13 | SurfelVBuffer/SurfelVBuffer.cpp 14 | SurfelVBuffer/SurfelVBuffer.h 15 | SurfelVBuffer/SurfelVBuffer.rt.slang 16 | 17 | SurfelGIRenderPass/SurfelGIRenderPass.cpp 18 | SurfelGIRenderPass/SurfelGIRenderPass.h 19 | SurfelGIRenderPass/SurfelGIRenderPass.cs.slang 20 | 21 | SurfelGI/StaticParams.slang 22 | SurfelGI/OverlayMode.slang 23 | SurfelGI/SurfelGI.cpp 24 | SurfelGI/SurfelGI.h 25 | SurfelGI/SurfelTypes.slang 26 | SurfelGI/SurfelUtils.slang 27 | SurfelGI/SurfelPreparePass.cs.slang 28 | SurfelGI/SurfelUpdatePass.cs.slang 29 | SurfelGI/SurfelRayTrace.rt.slang 30 | SurfelGI/SurfelGenerationPass.cs.slang 31 | SurfelGI/SurfelIntegratePass.cs.slang 32 | SurfelGI/SurfelEvaluationPass.cs.slang 33 | SurfelGI/MultiscaleMeanEstimator.slang 34 | ) 35 | 36 | target_copy_shaders(Surfel RenderPasses/Surfel) 37 | 38 | target_source_group(Surfel "RenderPasses") 39 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/HashUtils.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /** 4 | * 32-bit (non-cryptographic) hash function by Robert Jenkins. 5 | * This is a perfect hash function (no collisions). 6 | * See https://gist.github.com/badboy/6267743. 7 | */ 8 | uint jenkinsHash(uint a) 9 | { 10 | a = (a + 0x7ed55d16) + (a << 12); 11 | a = (a ^ 0xc761c23c) ^ (a >> 19); 12 | a = (a + 0x165667b1) + (a << 5); 13 | a = (a + 0xd3a2646c) ^ (a << 9); 14 | a = (a + 0xfd7046c5) + (a << 3); 15 | a = (a ^ 0xb55a4f09) ^ (a >> 16); 16 | return a; 17 | } 18 | 19 | float3 pseudocolor(uint value) 20 | { 21 | uint h = jenkinsHash(value); 22 | return (uint3(h, h >> 8, h >> 16) & 0xff) / 255.f; 23 | } 24 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/Random.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | struct RNG 4 | { 5 | uint2 s; 6 | 7 | uint rotl(uint x, uint k) 8 | { 9 | return (x << k) | (x >> (32 - k)); 10 | } 11 | 12 | [mutating] 13 | uint next() 14 | { 15 | uint result = s.x * 0x9e3779bb; 16 | 17 | s.y ^= s.x; 18 | s.x = rotl(s.x, 26) ^ s.y ^ (s.y << 9); 19 | s.y = rotl(s.y, 13); 20 | 21 | return result; 22 | } 23 | 24 | uint hash(uint seed) 25 | { 26 | seed = (seed ^ 61) ^ (seed >> 16); 27 | seed *= 9; 28 | seed = seed ^ (seed >> 4); 29 | seed *= 0x27d4eb2d; 30 | seed = seed ^ (seed >> 15); 31 | return seed; 32 | } 33 | 34 | [mutating] 35 | void init(uint2 id, uint frameIndex) 36 | { 37 | id += uint2(frameIndex, frameIndex); 38 | uint s0 = (id.x << 16) | id.y; 39 | uint s1 = frameIndex; 40 | s.x = hash(s0); 41 | s.y = hash(s1); 42 | next(); 43 | } 44 | 45 | [mutating] 46 | float next_float() 47 | { 48 | uint u = 0x3f800000 | (next() >> 9); 49 | return asfloat(u) - 1.0; 50 | } 51 | 52 | [mutating] 53 | uint next_uint(uint nmax) 54 | { 55 | float f = next_float(); 56 | return uint(floor(f * nmax)); 57 | } 58 | 59 | [mutating] 60 | float2 next_float2() 61 | { 62 | return float2(next_float(), next_float()); 63 | } 64 | 65 | [mutating] 66 | float3 next_float3() 67 | { 68 | return float3(next_float(), next_float(), next_float()); 69 | } 70 | }; 71 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/Surfel.cpp: -------------------------------------------------------------------------------- 1 | #include "RenderGraph/RenderPass.h" 2 | #include "SurfelGBuffer/SurfelGBuffer.h" 3 | #include "SurfelVBuffer/SurfelVBuffer.h" 4 | #include "SurfelGIRenderPass/SurfelGIRenderPass.h" 5 | #include "SurfelGI/SurfelGI.h" 6 | 7 | extern "C" FALCOR_API_EXPORT void registerPlugin(Falcor::PluginRegistry& registry) 8 | { 9 | registry.registerClass(); 10 | registry.registerClass(); 11 | registry.registerClass(); 12 | registry.registerClass(); 13 | } 14 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGBuffer/SurfelGBuffer.3d.slang: -------------------------------------------------------------------------------- 1 | import RenderPasses.Surfel.HashUtils; 2 | 3 | import Scene.Raster; 4 | 5 | struct GBufferVSOut 6 | { 7 | float2 texC : TEXCRD; 8 | nointerpolation GeometryInstanceID instanceID : INSTANCE_ID; 9 | nointerpolation uint materialID : MATERIAL_ID; 10 | float4 posH : SV_POSITION; 11 | } 12 | 13 | struct GBufferPSOut 14 | { 15 | uint4 packedHitInfo : SV_TARGET0; 16 | float4 instanceIDVisual : SV_TARGET1; 17 | }; 18 | 19 | GBufferVSOut vsMain(VSIn vsIn) 20 | { 21 | GBufferVSOut vsOut; 22 | const GeometryInstanceID instanceID = { vsIn.instanceID }; 23 | 24 | float4x4 worldMat = gScene.getWorldMatrix(instanceID); 25 | float3 posW = mul(worldMat, float4(vsIn.pos, 1.f)).xyz; 26 | vsOut.posH = mul(gScene.camera.getViewProj(), float4(posW, 1.f)); 27 | 28 | vsOut.texC = vsIn.texC; 29 | vsOut.instanceID = instanceID; 30 | vsOut.materialID = gScene.getMaterialID(instanceID); 31 | 32 | return vsOut; 33 | } 34 | 35 | GBufferPSOut psMain(GBufferVSOut vsOut, uint triangleIndex: SV_PrimitiveID, float3 barycentrics: SV_Barycentrics) 36 | { 37 | // Alpha test. 38 | VertexData v = {}; 39 | v.texC = vsOut.texC; 40 | float lod = 0.f; 41 | 42 | if (gScene.materials.alphaTest(v, vsOut.materialID, lod)) 43 | discard; 44 | 45 | // Store. 46 | TriangleHit triangleHit; 47 | triangleHit.instanceID = vsOut.instanceID; 48 | triangleHit.primitiveIndex = triangleIndex; 49 | triangleHit.barycentrics = barycentrics.yz; 50 | 51 | GBufferPSOut psOut = { 52 | triangleHit.pack(), 53 | float4(pseudocolor(vsOut.instanceID.index), 1) 54 | }; 55 | 56 | return psOut; 57 | } 58 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGBuffer/SurfelGBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "SurfelGBuffer.h" 2 | 3 | const ChannelList SurfelGBuffer::kChannels = { 4 | {"packedHitInfo", "gPackedHitInfo", "Packed Hit Info", true, ResourceFormat::RGBA32Uint}, 5 | {"instanceIDVisual", "gInstanceIDVisual", "Instance ID Visualization", true, ResourceFormat::RGBA32Float}, 6 | }; 7 | 8 | SurfelGBuffer::SurfelGBuffer(ref pDevice, const Properties& props) : RenderPass(pDevice) 9 | { 10 | // Check for required features. 11 | if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_2)) 12 | FALCOR_THROW("requires Shader Model 6.2 support."); 13 | if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) 14 | FALCOR_THROW("requires pixel shader barycentrics support."); 15 | if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RasterizerOrderedViews)) 16 | FALCOR_THROW("requires rasterizer ordered views (ROVs) support."); 17 | 18 | // Init graphics state. 19 | mpState = GraphicsState::create(mpDevice); 20 | 21 | // Create FBO. 22 | mpFbo = Fbo::create(mpDevice); 23 | } 24 | 25 | RenderPassReflection SurfelGBuffer::reflect(const CompileData& compileData) 26 | { 27 | RenderPassReflection reflector; 28 | 29 | // Output depth 30 | reflector.addOutput("depth", "depth buffer") 31 | .format(ResourceFormat::D32Float) 32 | .bindFlags(ResourceBindFlags::DepthStencil); 33 | 34 | // Output channels 35 | addRenderPassOutputs(reflector, kChannels, ResourceBindFlags::RenderTarget | ResourceBindFlags::UnorderedAccess); 36 | 37 | return reflector; 38 | } 39 | 40 | void SurfelGBuffer::execute(RenderContext* pRenderContext, const RenderData& renderData) 41 | { 42 | const auto& pDepth = renderData.getTexture("depth"); 43 | 44 | if (mpProgram) 45 | { 46 | mpFbo->attachDepthStencilTarget(pDepth); 47 | 48 | for (uint32_t i = 0; i < kChannels.size(); ++i) 49 | mpFbo->attachColorTarget(renderData.getTexture(kChannels[i].name), i); 50 | 51 | pRenderContext->clearDsv(pDepth->getDSV().get(), 1.f, 0); 52 | pRenderContext->clearFbo(mpFbo.get(), float4(0), 1.f, 0, FboAttachmentType::Color); 53 | 54 | mpState->setFbo(mpFbo); 55 | mpScene->rasterize(pRenderContext, mpState.get(), mpVars.get()); 56 | } 57 | } 58 | 59 | void SurfelGBuffer::setScene(RenderContext* pRenderContext, const ref& pScene) 60 | { 61 | mpScene = pScene; 62 | 63 | if (mpScene) 64 | { 65 | ProgramDesc desc; 66 | desc.addShaderModules(mpScene->getShaderModules()); 67 | desc.addShaderLibrary("RenderPasses/Surfel/SurfelGBuffer/SurfelGBuffer.3d.slang") 68 | .vsEntry("vsMain") 69 | .psEntry("psMain"); 70 | desc.addTypeConformances(mpScene->getTypeConformances()); 71 | 72 | mpProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); 73 | mpState->setProgram(mpProgram); 74 | 75 | mpVars = ProgramVars::create(mpDevice, mpProgram.get()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGBuffer/SurfelGBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Falcor.h" 3 | #include "RenderGraph/RenderPass.h" 4 | #include "RenderGraph/RenderPassHelpers.h" 5 | 6 | using namespace Falcor; 7 | 8 | class SurfelGBuffer : public RenderPass 9 | { 10 | public: 11 | FALCOR_PLUGIN_CLASS(SurfelGBuffer, "SurfelGBuffer", "Surfel GBuffer"); 12 | 13 | static ref create(ref pDevice, const Properties& props) 14 | { 15 | return make_ref(pDevice, props); 16 | } 17 | 18 | SurfelGBuffer(ref pDevice, const Properties& props); 19 | 20 | virtual Properties getProperties() const override { return {}; } 21 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 22 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override {} 23 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 24 | virtual void renderUI(Gui::Widgets& widget) override {} 25 | virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; 26 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 27 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 28 | 29 | private: 30 | ref mpScene; 31 | ref mpState; 32 | ref mpProgram; 33 | ref mpVars; 34 | ref mpFbo; 35 | 36 | static const ChannelList kChannels; 37 | }; 38 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/MultiscaleMeanEstimator.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Multi-Scale Mean Estimator 4 | // https://github.com/Apress/ray-tracing-gems/blob/master/Ch_25_Hybrid_Rendering_for_Real-Time_Ray_Tracing/MultiscaleMeanEstimator.hlsl 5 | 6 | struct MSMEData 7 | { 8 | float3 mean; 9 | float3 shortMean; 10 | float vbbr; 11 | float3 variance; 12 | float inconsistency; 13 | 14 | #ifdef HOST_CODE 15 | #else 16 | __init() 17 | { 18 | this.mean = float3(0.f); 19 | this.shortMean = float3(0.f); 20 | this.vbbr = 0.f; 21 | this.variance = float3(0.f); 22 | this.inconsistency = 1.f; 23 | } 24 | #endif 25 | }; 26 | 27 | #ifdef HOST_CODE 28 | #else 29 | 30 | float3 MSME(float3 y, inout MSMEData data, float shortWindowBlend = 0.08f) 31 | { 32 | float3 mean = data.mean; 33 | float3 shortMean = data.shortMean; 34 | float vbbr = data.vbbr; 35 | float3 variance = data.variance; 36 | float inconsistency = data.inconsistency; 37 | 38 | // Suppress fireflies. 39 | { 40 | float3 dev = sqrt(max(1e-5, variance)); 41 | float3 highThreshold = 0.1 + shortMean + dev * 8; 42 | float3 overflow = max(0, y - highThreshold); 43 | y -= overflow; 44 | } 45 | 46 | float3 delta = y - shortMean; 47 | shortMean = lerp(shortMean, y, shortWindowBlend); 48 | float3 delta2 = y - shortMean; 49 | 50 | // This should be a longer window than shortWindowBlend to avoid bias 51 | // from the variance getting smaller when the short-term mean does. 52 | float varianceBlend = shortWindowBlend * 0.5; 53 | variance = lerp(variance, delta * delta2, varianceBlend); 54 | float3 dev = sqrt(max(1e-5, variance)); 55 | 56 | float3 shortDiff = mean - shortMean; 57 | 58 | float relativeDiff = dot(float3(0.299, 0.587, 0.114), abs(shortDiff) / max(1e-5, dev)); 59 | inconsistency = lerp(inconsistency, relativeDiff, 0.08); 60 | 61 | float varianceBasedBlendReduction = clamp(dot(float3(0.299, 0.587, 0.114), 0.5 * shortMean / max(1e-5, dev)), 1.0 / 32, 1); 62 | 63 | float3 catchUpBlend = clamp(smoothstep(0, 1, relativeDiff * max(0.02, inconsistency - 0.2)), 1.0 / 256, 1); 64 | catchUpBlend *= vbbr; 65 | 66 | vbbr = lerp(vbbr, varianceBasedBlendReduction, 0.1); 67 | mean = lerp(mean, y, saturate(catchUpBlend)); 68 | 69 | // Output 70 | data.mean = mean; 71 | data.shortMean = shortMean; 72 | data.vbbr = vbbr; 73 | data.variance = variance; 74 | data.inconsistency = inconsistency; 75 | 76 | return mean; 77 | } 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/OverlayMode.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Utils/HostDeviceShared.slangh" 3 | 4 | BEGIN_NAMESPACE_FALCOR 5 | 6 | enum class OverlayMode : int 7 | { 8 | IndirectLighting = 0, 9 | Variance = 1, 10 | RayCount = 2, 11 | RefCount = 3, 12 | Life = 4, 13 | Coverage = 5, 14 | }; 15 | 16 | FALCOR_ENUM_INFO(OverlayMode, { 17 | { OverlayMode::IndirectLighting, "IndirectLighting" }, 18 | { OverlayMode::Variance, "Variance" }, 19 | { OverlayMode::RayCount, "RayCount" }, 20 | { OverlayMode::RefCount, "RefCount" }, 21 | { OverlayMode::Life, "Life" }, 22 | { OverlayMode::Coverage, "Coverage" }, 23 | }); 24 | FALCOR_ENUM_REGISTER(OverlayMode); 25 | 26 | END_NAMESPACE_FALCOR 27 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/StaticParams.slang: -------------------------------------------------------------------------------- 1 | static const uint kSurfelTargetArea = SURFEL_TARGET_AREA; 2 | static const float kCellUnit = CELL_UNIT; 3 | static const uint kCellDimension = CELL_DIM; 4 | static const uint kCellCount = CELL_COUNT; 5 | static const uint kPerCellSurfelLimit = PER_CELL_SURFEL_LIMIT; 6 | static const uint kMaxSurfelForStep = MAX_SURFEL_FOR_STEP; 7 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelEvaluationPass.cs.slang: -------------------------------------------------------------------------------- 1 | import Scene.Scene; 2 | import RenderPasses.Surfel.Random; 3 | import RenderPasses.Surfel.HashUtils; 4 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 5 | import RenderPasses.Surfel.SurfelGI.SurfelUtils; 6 | import RenderPasses.Surfel.SurfelGI.StaticParams; 7 | 8 | cbuffer CB 9 | { 10 | uint gFrameIndex; 11 | float gPlacementThreshold; 12 | float gRemovalThreshold; 13 | uint gBlendingDelay; 14 | uint gOverlayMode; 15 | float gVarianceSensitivity; 16 | } 17 | 18 | RWStructuredBuffer gSurfelBuffer; 19 | RWStructuredBuffer gCellInfoBuffer; 20 | RWStructuredBuffer gCellToSurfelBuffer; 21 | RWStructuredBuffer gSurfelRecycleInfoBuffer; 22 | 23 | RWByteAddressBuffer gSurfelRefCounter; 24 | 25 | Texture2D gPackedHitInfo; 26 | Texture2D gSurfelDepth; 27 | RWTexture2D gOutput; 28 | 29 | SamplerState gSurfelDepthSampler; 30 | 31 | [numthreads(16, 16, 1)] 32 | void csMain( 33 | uint3 dispatchThreadId: SV_DispatchThreadID, 34 | uint groupIndex: SV_GroupIndex, 35 | uint3 groupThreadID: SV_GroupThreadID, 36 | uint3 groupdId: SV_GroupID 37 | ) 38 | { 39 | uint2 pixelPos = dispatchThreadId.xy; 40 | 41 | RNG randomState; 42 | randomState.init(pixelPos, gFrameIndex); 43 | 44 | HitInfo hitInfo = HitInfo(gPackedHitInfo[pixelPos]); 45 | if (!hitInfo.isValid()) 46 | return; 47 | 48 | TriangleHit triangleHit = hitInfo.getTriangleHit(); 49 | VertexData v = gScene.getVertexData(triangleHit); 50 | float4 curPosH = mul(gScene.camera.data.viewProjMatNoJitter, float4(v.posW, 1.f)); 51 | float depth = curPosH.z / curPosH.w; 52 | 53 | int3 cellPos = getCellPos(v.posW, gScene.camera.getPosition(), kCellUnit); 54 | if (!isCellValid(cellPos)) 55 | return; 56 | 57 | uint flattenIndex = getFlattenCellIndex(cellPos); 58 | CellInfo cellInfo = gCellInfoBuffer[flattenIndex]; 59 | 60 | float4 indirectLighting = float4(0.f); 61 | float coverage = 0.f; 62 | float varianceEx = 0.f; 63 | float rayCountEx = 0.f; 64 | uint refCount = 0; 65 | uint life = 0; 66 | 67 | float maxVariance = 0.f; 68 | 69 | for (uint i = 0; i < cellInfo.surfelCount; ++i) 70 | { 71 | uint surfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + i]; 72 | Surfel surfel = gSurfelBuffer[surfelIndex]; 73 | 74 | float3 bias = v.posW - surfel.position; 75 | float dist2 = dot(bias, bias); 76 | 77 | if (dist2 < surfel.radius * surfel.radius) 78 | { 79 | float3 normal = normalize(surfel.normal); 80 | 81 | float dotN = dot(v.normalW, normal); 82 | if (dotN > 0) 83 | { 84 | float dist = sqrt(dist2); 85 | float contribution = 1.f; 86 | 87 | contribution *= saturate(dotN); 88 | contribution *= saturate(1 - dist / surfel.radius); 89 | contribution = smoothstep(0, 1, contribution); 90 | 91 | coverage += contribution; 92 | 93 | #ifdef USE_SURFEL_DEPTH 94 | { 95 | float2 uv = getSurfelDepthUV(surfelIndex, bias / dist, normal); 96 | float2 surfelDepth = gSurfelDepth.SampleLevel(gSurfelDepthSampler, uv, 0u); 97 | 98 | float mean = surfelDepth.x; 99 | float sqrMean = surfelDepth.y; 100 | 101 | if (dist > mean) 102 | { 103 | float variance = sqrMean - pow(mean, 2); 104 | contribution *= variance / (variance + pow(dist - mean, 2)); 105 | } 106 | } 107 | #else // USE_SURFEL_DEPTH 108 | #endif // USE_SURFEL_DEPTH 109 | 110 | const SurfelRecycleInfo surfelRecycleInfo = gSurfelRecycleInfoBuffer[surfelIndex]; 111 | 112 | // Delay blending if not sufficient sample is accumulated. 113 | // Because samples are updated per frame, so do not use sample count directly. 114 | indirectLighting += float4(surfel.radiance, 1.f) * contribution * smoothstep(0, gBlendingDelay, surfelRecycleInfo.frame); 115 | 116 | varianceEx += length(surfel.msmeData.variance) * contribution; 117 | rayCountEx += surfel.rayCount * contribution; 118 | 119 | refCount = max(refCount, gSurfelRefCounter.Load(surfelIndex)); 120 | life = max(life, surfelRecycleInfo.life); 121 | 122 | maxVariance = max(maxVariance, length(surfel.msmeData.variance)); 123 | } 124 | } 125 | } 126 | 127 | if (indirectLighting.w > 0) 128 | { 129 | indirectLighting.xyz /= indirectLighting.w; 130 | indirectLighting.w = saturate(indirectLighting.w); 131 | 132 | varianceEx /= indirectLighting.w; 133 | rayCountEx /= indirectLighting.w; 134 | 135 | // Write texture by overlay mode. 136 | if (gOverlayMode == 0) 137 | { 138 | gOutput[pixelPos] = indirectLighting; 139 | } 140 | else if (gOverlayMode == 1) 141 | { 142 | gOutput[pixelPos] = float4(stepColor(maxVariance * gVarianceSensitivity, 0.8f, 0.5f), 1); 143 | } 144 | else if (gOverlayMode == 2) 145 | { 146 | gOutput[pixelPos] = float4(stepColor(rayCountEx, 48.f, 32.f), 1); 147 | } 148 | else if (gOverlayMode == 3) 149 | { 150 | gOutput[pixelPos] = float4(lerpColor(refCount / 256.f), 1); 151 | } 152 | else if (gOverlayMode == 4) 153 | { 154 | gOutput[pixelPos] = float4(step(life, 0u), step(1u, life), 0, 1); 155 | } 156 | else if (gOverlayMode == 5) 157 | { 158 | gOutput[pixelPos] = float4(lerpColor(smoothstep(gPlacementThreshold, gRemovalThreshold, coverage)), 1); 159 | } 160 | } 161 | } 162 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelGI.cpp: -------------------------------------------------------------------------------- 1 | #include "SurfelGI.h" 2 | #include "Utils/Math/FalcorMath.h" 3 | #include "SurfelTypes.slang" 4 | 5 | namespace 6 | { 7 | 8 | float plotFunc(void* data, int i) 9 | { 10 | return static_cast(data)[i]; 11 | } 12 | 13 | const std::string kPackedHitInfoTextureName = "packedHitInfo"; 14 | const std::string kOutputTextureName = "output"; 15 | const std::string kIrradianceMapTextureName = "irradiance map"; 16 | const std::string kSurfelDepthTextureName = "surfel depth"; 17 | 18 | const std::string kSurfelBufferVarName = "gSurfelBuffer"; 19 | const std::string kSurfelGeometryBufferVarName = "gSurfelGeometryBuffer"; 20 | const std::string kSurfelValidIndexBufferVarName = "gSurfelValidIndexBuffer"; 21 | const std::string kSurfelDirtyIndexBufferVarName = "gSurfelDirtyIndexBuffer"; 22 | const std::string kSurfelFreeIndexBufferVarName = "gSurfelFreeIndexBuffer"; 23 | const std::string kCellInfoBufferVarName = "gCellInfoBuffer"; 24 | const std::string kCellToSurfelBufferVarName = "gCellToSurfelBuffer"; 25 | const std::string kSurfelRayResultBufferVarName = "gSurfelRayResultBuffer"; 26 | const std::string kSurfelRecycleInfoBufferVarName = "gSurfelRecycleInfoBuffer"; 27 | const std::string kSurfelReservationBufferVarName = "gSurfelReservationBuffer"; 28 | const std::string kSurfelRefCounterVarName = "gSurfelRefCounter"; 29 | const std::string kSurfelCounterVarName = "gSurfelCounter"; 30 | 31 | } // namespace 32 | 33 | SurfelGI::SurfelGI(ref pDevice, const Properties& props) : RenderPass(pDevice) 34 | { 35 | mpDevice = pDevice; 36 | 37 | if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) 38 | FALCOR_THROW("SurfelGI requires Shader Model 6.5 support."); 39 | if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RaytracingTier1_1)) 40 | FALCOR_THROW("SurfelGI requires Raytracing Tier 1.1 support."); 41 | 42 | mpFence = mpDevice->createFence(); 43 | mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); 44 | 45 | // Create sampler. 46 | Sampler::Desc samplerDesc; 47 | samplerDesc.setFilterMode(TextureFilteringMode::Linear, TextureFilteringMode::Linear, TextureFilteringMode::Linear); 48 | samplerDesc.setAddressingMode(TextureAddressingMode::Clamp, TextureAddressingMode::Clamp, TextureAddressingMode::Clamp); 49 | mpSurfelDepthSampler = mpDevice->createSampler(samplerDesc); 50 | } 51 | 52 | RenderPassReflection SurfelGI::reflect(const CompileData& compileData) 53 | { 54 | RenderPassReflection reflector; 55 | 56 | reflectInput(reflector, compileData.defaultTexDims); 57 | reflectOutput(reflector, compileData.defaultTexDims); 58 | 59 | if (math::any(mFrameDim != compileData.defaultTexDims)) 60 | { 61 | mIsFrameDimChanged = true; 62 | mFrameDim = compileData.defaultTexDims; 63 | } 64 | 65 | return reflector; 66 | } 67 | 68 | void SurfelGI::execute(RenderContext* pRenderContext, const RenderData& renderData) 69 | { 70 | if (!mpScene) 71 | return; 72 | 73 | if (mRecompile) 74 | { 75 | createPasses(); 76 | createResolutionIndependentResources(); 77 | 78 | mRecompile = false; 79 | } 80 | 81 | if (mIsFrameDimChanged) 82 | { 83 | createResolutionDependentResources(); 84 | mIsFrameDimChanged = false; 85 | } 86 | 87 | bindResources(renderData); 88 | 89 | mFOVy = focalLengthToFovY(mpScene->getCamera()->getFocalLength(), mpScene->getCamera()->getFrameHeight()); 90 | mCamPos = mpScene->getCamera()->getPosition(); 91 | 92 | // Request the light collection if emissive lights are enabled. 93 | if (mpScene->getRenderSettings().useEmissiveLights) 94 | mpScene->getLightCollection(pRenderContext); 95 | 96 | { 97 | FALCOR_PROFILE(pRenderContext, "Prepare Pass"); 98 | 99 | mpPreparePass->execute(pRenderContext, uint3(1)); 100 | pRenderContext->copyResource(mpSurfelDirtyIndexBuffer.get(), mpSurfelValidIndexBuffer.get()); 101 | } 102 | 103 | { 104 | FALCOR_PROFILE(pRenderContext, "Update Pass (Collect Cell Info Pass)"); 105 | 106 | auto var = mpCollectCellInfoPass->getRootVar(); 107 | 108 | mpScene->setRaytracingShaderData(pRenderContext, var); 109 | 110 | var["CB"]["gCameraPos"] = mCamPos; 111 | var["CB"]["gResolution"] = mFrameDim; 112 | var["CB"]["gFOVy"] = mFOVy; 113 | var["CB"]["gLockSurfel"] = mLockSurfel; 114 | var["CB"]["gVarianceSensitivity"] = mRuntimeParams.varianceSensitivity; 115 | var["CB"]["gMinRayCount"] = mRuntimeParams.minRayCount; 116 | var["CB"]["gMaxRayCount"] = mRuntimeParams.maxRayCount; 117 | 118 | mpCollectCellInfoPass->execute(pRenderContext, uint3(kTotalSurfelLimit, 1, 1)); 119 | } 120 | 121 | { 122 | FALCOR_PROFILE(pRenderContext, "Update Pass (Accumulate Cell Info Pass)"); 123 | 124 | auto var = mpAccumulateCellInfoPass->getRootVar(); 125 | 126 | var["CB"]["gCameraPos"] = mCamPos; 127 | 128 | mpAccumulateCellInfoPass->execute(pRenderContext, uint3(mStaticParams.cellCount, 1, 1)); 129 | } 130 | 131 | { 132 | FALCOR_PROFILE(pRenderContext, "Update Pass (Update Cell To Surfel buffer Pass)"); 133 | 134 | auto var = mpUpdateCellToSurfelBuffer->getRootVar(); 135 | 136 | var["CB"]["gCameraPos"] = mCamPos; 137 | 138 | mpUpdateCellToSurfelBuffer->execute(pRenderContext, uint3(kTotalSurfelLimit, 1, 1)); 139 | } 140 | 141 | if (mLockSurfel) 142 | { 143 | FALCOR_PROFILE(pRenderContext, "Surfel Evaluation Pass"); 144 | 145 | auto var = mpSurfelEvaluationPass->getRootVar(); 146 | 147 | mpScene->setRaytracingShaderData(pRenderContext, var); 148 | 149 | var["CB"]["gFrameIndex"] = mFrameIndex; 150 | var["CB"]["gPlacementThreshold"] = mRuntimeParams.placementThreshold; 151 | var["CB"]["gRemovalThreshold"] = mRuntimeParams.removalThreshold; 152 | var["CB"]["gBlendingDelay"] = mRuntimeParams.blendingDelay; 153 | var["CB"]["gOverlayMode"] = (uint)mRuntimeParams.overlayMode; 154 | var["CB"]["gVarianceSensitivity"] = mRuntimeParams.varianceSensitivity; 155 | 156 | pRenderContext->clearUAV(mpOutputTexture->getUAV().get(), float4(0)); 157 | mpSurfelEvaluationPass->execute(pRenderContext, uint3(mFrameDim, 1)); 158 | } 159 | else 160 | { 161 | { 162 | FALCOR_PROFILE(pRenderContext, "Surfel RayTrace Pass"); 163 | 164 | auto var = mRtPass.pVars->getRootVar(); 165 | var["CB"]["gFrameIndex"] = mFrameIndex; 166 | var["CB"]["gRayStep"] = mRuntimeParams.rayStep; 167 | var["CB"]["gMaxStep"] = mRuntimeParams.maxStep; 168 | 169 | mpScene->raytrace(pRenderContext, mRtPass.pProgram.get(), mRtPass.pVars, uint3(kRayBudget, 1, 1)); 170 | } 171 | 172 | if (mFrameIndex <= mMaxFrameIndex) 173 | { 174 | FALCOR_PROFILE(pRenderContext, "Surfel Integrate Pass"); 175 | 176 | auto var = mpSurfelIntegratePass->getRootVar(); 177 | var["CB"]["gShortMeanWindow"] = mRuntimeParams.shortMeanWindow; 178 | var["CB"]["gCameraPos"] = mCamPos; 179 | var["CB"]["gVarianceSensitivity"] = mRuntimeParams.varianceSensitivity; 180 | 181 | mpSurfelIntegratePass->execute(pRenderContext, uint3(kTotalSurfelLimit, 1, 1)); 182 | } 183 | 184 | { 185 | FALCOR_PROFILE(pRenderContext, "Surfel Generation Pass"); 186 | 187 | auto var = mpSurfelGenerationPass->getRootVar(); 188 | 189 | mpScene->setRaytracingShaderData(pRenderContext, var); 190 | 191 | var["CB"]["gResolution"] = mFrameDim; 192 | var["CB"]["gFOVy"] = mFOVy; 193 | var["CB"]["gFrameIndex"] = mFrameIndex; 194 | var["CB"]["gChanceMultiply"] = mRuntimeParams.chanceMultiply; 195 | var["CB"]["gChancePower"] = mRuntimeParams.chancePower; 196 | var["CB"]["gPlacementThreshold"] = mRuntimeParams.placementThreshold; 197 | var["CB"]["gRemovalThreshold"] = mRuntimeParams.removalThreshold; 198 | var["CB"]["gOverlayMode"] = (uint)mRuntimeParams.overlayMode; 199 | var["CB"]["gBlendingDelay"] = mRuntimeParams.blendingDelay; 200 | var["CB"]["gVarianceSensitivity"] = mRuntimeParams.varianceSensitivity; 201 | 202 | pRenderContext->clearUAV(mpOutputTexture->getUAV().get(), float4(0)); 203 | mpSurfelGenerationPass->execute(pRenderContext, uint3(mFrameDim, 1)); 204 | } 205 | } 206 | 207 | { 208 | FALCOR_PROFILE(pRenderContext, "Read Back"); 209 | 210 | pRenderContext->copyResource(mpReadBackBuffer.get(), mpSurfelCounter.get()); 211 | 212 | if (mResetSurfelBuffer) 213 | { 214 | pRenderContext->copyResource(mpSurfelBuffer.get(), mpEmptySurfelBuffer.get()); 215 | 216 | pRenderContext->clearUAV(mpIrradianceMapTexture->getUAV().get(), float4(0)); 217 | pRenderContext->clearUAV(mpSurfelDepthTexture->getUAV().get(), float4(0)); 218 | 219 | mResetSurfelBuffer = false; 220 | mFrameIndex = 0; 221 | } 222 | 223 | pRenderContext->submit(false); 224 | pRenderContext->signal(mpFence.get()); 225 | 226 | mReadBackValid = true; 227 | } 228 | 229 | mFrameIndex++; 230 | } 231 | 232 | void SurfelGI::renderUI(Gui::Widgets& widget) 233 | { 234 | widget.text("Frame index"); 235 | widget.text(std::to_string(mFrameIndex), true); 236 | 237 | widget.dummy("#spacer0", {1, 10}); 238 | 239 | if (mReadBackValid) 240 | { 241 | const uint validSurfelCount = mpReadBackBuffer->getElement(0); 242 | const uint requestedRayCount = mpReadBackBuffer->getElement(4); 243 | const uint filledCellCount = mpReadBackBuffer->getElement(3); 244 | 245 | std::rotate(mSurfelCount.begin(), mSurfelCount.begin() + 1, mSurfelCount.end()); 246 | mSurfelCount[mSurfelCount.size() - 1] = (float)validSurfelCount / kTotalSurfelLimit; 247 | 248 | widget.graph("", plotFunc, mSurfelCount.data(), mSurfelCount.size(), 0, 0, FLT_MAX, 0, 25u); 249 | 250 | widget.text("Presented surfel"); 251 | widget.text(std::to_string(validSurfelCount) + " / " + std::to_string(kTotalSurfelLimit), true); 252 | widget.text("(" + std::to_string(validSurfelCount * 100.0f / kTotalSurfelLimit) + " %)", true); 253 | 254 | std::rotate(mRayBudget.begin(), mRayBudget.begin() + 1, mRayBudget.end()); 255 | mRayBudget[mRayBudget.size() - 1] = (float)requestedRayCount / kRayBudget; 256 | 257 | widget.graph("", plotFunc, mRayBudget.data(), mRayBudget.size(), 0, 0, FLT_MAX, 0, 25u); 258 | 259 | widget.text("Ray budget"); 260 | widget.text(std::to_string(requestedRayCount) + " / " + std::to_string(kRayBudget), true); 261 | widget.text("(" + std::to_string(requestedRayCount * 100.0f / kRayBudget) + " %)", true); 262 | 263 | widget.text("Surfel shortage"); 264 | widget.text(std::to_string(mpReadBackBuffer->getElement(2)), true); 265 | widget.tooltip("Positive if there is enough memory for the new surfel, negative otherwise."); 266 | 267 | widget.text("Miss ray bounce"); 268 | widget.text(std::to_string(mpReadBackBuffer->getElement(5)), true); 269 | widget.tooltip("The number of rays that failed to find surfel and move on to the next step."); 270 | 271 | widget.text("Visible distance"); 272 | widget.text(std::to_string(mStaticParams.cellDim * mStaticParams.cellUnit), true); 273 | widget.tooltip( 274 | "The maximum distance at which global illumination is drawn. This is affected by the dimensions and size " 275 | "of the cell." 276 | ); 277 | } 278 | 279 | widget.dummy("#spacer0", {1, 10}); 280 | 281 | { 282 | bool editted = false; 283 | 284 | widget.text("Adjust render scale"); 285 | widget.tooltip( 286 | "Adjust render scale, which affects cell unit and camera speed. Try adjusting this value if you think your " 287 | "scene is too large or too small so the rendering isn't working properly." 288 | ); 289 | 290 | if (widget.var("", mRenderScale, 1e-3f, 1e+3f, 2.f)) 291 | { 292 | editted = true; 293 | } 294 | else if (widget.button("/4")) 295 | { 296 | editted = true; 297 | mRenderScale /= 4; 298 | } 299 | else if (widget.button("/2", true)) 300 | { 301 | editted = true; 302 | mRenderScale /= 2; 303 | } 304 | else if (widget.button("x2", true)) 305 | { 306 | editted = true; 307 | mRenderScale *= 2; 308 | } 309 | else if (widget.button("x4", true)) 310 | { 311 | editted = true; 312 | mRenderScale *= 4; 313 | } 314 | 315 | if (editted) 316 | { 317 | mRenderScale = std::max(1e-3f, std::min(1e+3f, mRenderScale)); 318 | 319 | if (mpScene) 320 | mpScene->setCameraSpeed(mRenderScale); 321 | 322 | mTempStaticParams.cellUnit = 0.05f * mRenderScale; 323 | resetAndRecompile(); 324 | } 325 | } 326 | 327 | widget.dummy("#spacer0", {1, 10}); 328 | 329 | if (widget.button(!mLockSurfel ? "Lock Surfel" : "Unlock Surfel")) 330 | mLockSurfel = !mLockSurfel; 331 | widget.tooltip("Lock / Unlock surfel generation and update operation."); 332 | 333 | if (widget.button("Reset Surfel", true)) 334 | mResetSurfelBuffer = true; 335 | widget.tooltip("Clears all spawned surfels in the scene."); 336 | 337 | widget.dropdown("Overlay mode", mRuntimeParams.overlayMode); 338 | widget.tooltip("Decide what to render."); 339 | 340 | widget.dummy("#spacer0", {1, 10}); 341 | 342 | if (auto group = widget.group("Static Params (Needs re-compile)")) 343 | { 344 | widget.tooltip("Following parameters needs re-compile. Please press below button after adjusting values."); 345 | 346 | if (widget.button("Recompile")) 347 | resetAndRecompile(); 348 | 349 | if (auto g = group.group("Surfel Generation", true)) 350 | { 351 | g.slider("Target area size", mTempStaticParams.surfelTargetArea, 200u, 80000u); 352 | g.var("Cell unit", mTempStaticParams.cellUnit, 0.005f, 100.f); 353 | 354 | if (g.var("Cell dimension", mTempStaticParams.cellDim, 25u, 1000u, 25u)) 355 | mTempStaticParams.cellCount = 356 | mTempStaticParams.cellDim * mTempStaticParams.cellDim * mTempStaticParams.cellDim; 357 | 358 | g.slider("Per cell surfel limit", mTempStaticParams.perCellSurfelLimit, 2u, 1024u); 359 | } 360 | 361 | if (auto g = group.group("Ray Tracing", true)) 362 | { 363 | g.checkbox("Use surfel radiance", mTempStaticParams.useSurfelRadinace); 364 | g.checkbox("Limit surfel search", mTempStaticParams.limitSurfelSearch); 365 | 366 | if (mTempStaticParams.limitSurfelSearch) 367 | g.slider("Max surfel for step", mTempStaticParams.maxSurfelForStep, 1u, 100u); 368 | 369 | g.checkbox("Use ray guiding", mTempStaticParams.useRayGuiding); 370 | } 371 | 372 | if (auto g = group.group("Integrate", true)) 373 | { 374 | g.checkbox("Use surfel depth", mTempStaticParams.useSurfelDepth); 375 | g.checkbox("Use irradiance sharing", mTempStaticParams.useIrradianceSharing); 376 | } 377 | } 378 | 379 | if (auto group = widget.group("Runtime Params")) 380 | { 381 | if (auto g = group.group("Surfel Generation", true)) 382 | { 383 | g.slider("Chance multiply", mRuntimeParams.chanceMultiply, 0.f, 1.f); 384 | g.slider("Chance power", mRuntimeParams.chancePower, 0u, 8u); 385 | 386 | if (g.slider("Threshold gap", mRuntimeParams.thresholdGap, 0.f, 4.f)) 387 | mRuntimeParams.removalThreshold = mRuntimeParams.placementThreshold + mRuntimeParams.thresholdGap; 388 | if (g.slider("Placement threshold", mRuntimeParams.placementThreshold, 0.f, 10.0f)) 389 | mRuntimeParams.removalThreshold = mRuntimeParams.placementThreshold + mRuntimeParams.thresholdGap; 390 | else if (g.slider("Removal threshold", mRuntimeParams.removalThreshold, 0.f, 20.0f)) 391 | mRuntimeParams.placementThreshold = mRuntimeParams.removalThreshold - mRuntimeParams.thresholdGap; 392 | 393 | g.slider("Blending delay", mRuntimeParams.blendingDelay, 0u, 2048u); 394 | } 395 | 396 | if (auto g = group.group("Ray Tracing", true)) 397 | { 398 | g.slider("Variance Sensitivity", mRuntimeParams.varianceSensitivity, 0.1f, 100.f); 399 | g.slider("Min Ray Count", mRuntimeParams.minRayCount, 0u, mRuntimeParams.maxRayCount); 400 | g.slider("Max Ray Count", mRuntimeParams.maxRayCount, mRuntimeParams.minRayCount, 256u); 401 | g.slider("Ray step", mRuntimeParams.rayStep, 0u, mRuntimeParams.maxStep); 402 | g.slider("Max step", mRuntimeParams.maxStep, mRuntimeParams.rayStep, 100u); 403 | } 404 | 405 | if (auto g = group.group("Integrate", true)) 406 | { 407 | g.slider("Short mean window", mRuntimeParams.shortMeanWindow, 0.01f, 0.5f); 408 | } 409 | } 410 | } 411 | 412 | void SurfelGI::setScene(RenderContext* pRenderContext, const ref& pScene) 413 | { 414 | mpScene = pScene; 415 | if (!mpScene) 416 | return; 417 | 418 | // Overwrite material property for rendering only diffuse. 419 | // It will be removed when specular (glossy) GI is implemented. 420 | for (const auto& mat : mpScene->getMaterials()) 421 | { 422 | mat->toBasicMaterial()->setSpecularTexture(nullptr); 423 | mat->toBasicMaterial()->setSpecularParams(float4(0, 0, 0, 0)); 424 | } 425 | 426 | mFrameIndex = 0; 427 | mMaxFrameIndex = 1000000; 428 | mFrameDim = uint2(0, 0); 429 | mRenderScale = 1.f; 430 | mIsFrameDimChanged = true; 431 | mReadBackValid = false; 432 | mLockSurfel = false; 433 | mResetSurfelBuffer = false; 434 | mRecompile = false; 435 | mSurfelCount = std::vector(1000, 0.f); 436 | mRayBudget = std::vector(1000, 0.f); 437 | 438 | createPasses(); 439 | createResolutionIndependentResources(); 440 | } 441 | 442 | bool SurfelGI::onKeyEvent(const KeyboardEvent& keyEvent) 443 | { 444 | if (keyEvent.key == Input::Key::L) 445 | { 446 | mResetSurfelBuffer = true; 447 | return true; 448 | } 449 | 450 | return false; 451 | } 452 | 453 | void SurfelGI::reflectInput(RenderPassReflection& reflector, uint2 resolution) 454 | { 455 | reflector.addInput(kPackedHitInfoTextureName, "packed hit info texture") 456 | .format(ResourceFormat::RGBA32Uint) 457 | .bindFlags(ResourceBindFlags::ShaderResource); 458 | } 459 | 460 | void SurfelGI::reflectOutput(RenderPassReflection& reflector, uint2 resolution) 461 | { 462 | reflector.addOutput(kOutputTextureName, "output texture") 463 | .format(ResourceFormat::RGBA32Float) 464 | .bindFlags(ResourceBindFlags::UnorderedAccess); 465 | 466 | reflector.addOutput(kIrradianceMapTextureName, "irradiance map texture") 467 | .format(ResourceFormat::R32Float) 468 | .bindFlags(ResourceBindFlags::UnorderedAccess) 469 | .texture2D(kIrradianceMapRes.x, kIrradianceMapRes.y); 470 | 471 | reflector.addOutput(kSurfelDepthTextureName, "surfel depth texture") 472 | .format(ResourceFormat::RG32Float) 473 | .bindFlags(ResourceBindFlags::UnorderedAccess | ResourceBindFlags::ShaderResource) 474 | .texture2D(kSurfelDepthTextureRes.x, kSurfelDepthTextureRes.y); 475 | } 476 | 477 | void SurfelGI::resetAndRecompile() 478 | { 479 | mStaticParams = mTempStaticParams; 480 | 481 | // Reset render passes. 482 | mpSurfelEvaluationPass = nullptr; 483 | mpPreparePass = nullptr; 484 | mpCollectCellInfoPass = nullptr; 485 | mpAccumulateCellInfoPass = nullptr; 486 | mpUpdateCellToSurfelBuffer = nullptr; 487 | mpSurfelGenerationPass = nullptr; 488 | mpSurfelIntegratePass = nullptr; 489 | mRtPass.pProgram = nullptr; 490 | mRtPass.pBindingTable = nullptr; 491 | mRtPass.pVars = nullptr; 492 | 493 | // Reset resources. 494 | mpSurfelBuffer = nullptr; 495 | mpSurfelGeometryBuffer = nullptr; 496 | mpSurfelValidIndexBuffer = nullptr; 497 | mpSurfelDirtyIndexBuffer = nullptr; 498 | mpSurfelFreeIndexBuffer = nullptr; 499 | mpCellInfoBuffer = nullptr; 500 | mpCellToSurfelBuffer = nullptr; 501 | mpSurfelRayResultBuffer = nullptr; 502 | mpSurfelRecycleInfoBuffer = nullptr; 503 | mpSurfelReservationBuffer = nullptr; 504 | mpSurfelRefCounter = nullptr; 505 | mpSurfelCounter = nullptr; 506 | mpEmptySurfelBuffer = nullptr; 507 | mpReadBackBuffer = nullptr; 508 | 509 | // #TODO Should reset texture reousrces also? 510 | 511 | // Reset variables. 512 | mFrameIndex = 0; 513 | mIsFrameDimChanged = true; 514 | mReadBackValid = false; 515 | mLockSurfel = false; 516 | 517 | mRecompile = true; 518 | } 519 | 520 | void SurfelGI::createPasses() 521 | { 522 | auto defines = mStaticParams.getDefines(*this); 523 | defines.add(mpScene->getSceneDefines()); 524 | 525 | // Evalulation Pass 526 | mpSurfelEvaluationPass = 527 | ComputePass::create(mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelEvaluationPass.cs.slang", "csMain", defines); 528 | 529 | // Prepare Pass 530 | mpPreparePass = 531 | ComputePass::create(mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelPreparePass.cs.slang", "csMain", defines); 532 | 533 | // Update Pass (Collect Cell Info Pass) 534 | mpCollectCellInfoPass = ComputePass::create( 535 | mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelUpdatePass.cs.slang", "collectCellInfo", defines 536 | ); 537 | 538 | // Update Pass (Accumulate Cell Info Pass) 539 | mpAccumulateCellInfoPass = ComputePass::create( 540 | mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelUpdatePass.cs.slang", "accumulateCellInfo", defines 541 | ); 542 | 543 | // Update Pass (Update Cell To Surfel buffer Pass) 544 | mpUpdateCellToSurfelBuffer = ComputePass::create( 545 | mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelUpdatePass.cs.slang", "updateCellToSurfelBuffer", defines 546 | ); 547 | 548 | // Surfel RayTrace Pass 549 | { 550 | ProgramDesc desc; 551 | desc.addShaderModules(mpScene->getShaderModules()); 552 | desc.addShaderLibrary("RenderPasses/Surfel/SurfelGI/SurfelRayTrace.rt.slang"); 553 | desc.setMaxPayloadSize(72u); 554 | desc.setMaxAttributeSize(mpScene->getRaytracingMaxAttributeSize()); 555 | desc.setMaxTraceRecursionDepth(2u); 556 | 557 | mRtPass.pBindingTable = RtBindingTable::create(2, 2, mpScene->getGeometryCount()); 558 | mRtPass.pBindingTable->setRayGen(desc.addRayGen("rayGen")); 559 | mRtPass.pBindingTable->setMiss(0, desc.addMiss("scatterMiss")); 560 | mRtPass.pBindingTable->setMiss(1, desc.addMiss("shadowMiss")); 561 | 562 | mRtPass.pBindingTable->setHitGroup( 563 | 0, 564 | mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), 565 | desc.addHitGroup("scatterCloseHit", "scatterAnyHit") 566 | ); 567 | 568 | mRtPass.pBindingTable->setHitGroup( 569 | 1, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("", "shadowAnyhit") 570 | ); 571 | 572 | mRtPass.pProgram = Program::create(mpDevice, desc, defines); 573 | mRtPass.pProgram->addDefines(mpSampleGenerator->getDefines()); 574 | mRtPass.pProgram->setTypeConformances(mpScene->getTypeConformances()); 575 | 576 | mRtPass.pVars = RtProgramVars::create(mpDevice, mRtPass.pProgram, mRtPass.pBindingTable); 577 | mpSampleGenerator->bindShaderData(mRtPass.pVars->getRootVar()); 578 | } 579 | 580 | // Surfel Generation Pass 581 | mpSurfelGenerationPass = 582 | ComputePass::create(mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelGenerationPass.cs.slang", "csMain", defines); 583 | 584 | // Surfel Integrate Pass 585 | mpSurfelIntegratePass = 586 | ComputePass::create(mpDevice, "RenderPasses/Surfel/SurfelGI/SurfelIntegratePass.cs.slang", "csMain", defines); 587 | } 588 | 589 | void SurfelGI::createResolutionIndependentResources() 590 | { 591 | mpSurfelBuffer = mpDevice->createStructuredBuffer( 592 | sizeof(Surfel), kTotalSurfelLimit, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false 593 | ); 594 | 595 | mpSurfelGeometryBuffer = mpDevice->createStructuredBuffer( 596 | sizeof(uint4), kTotalSurfelLimit, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false 597 | ); 598 | 599 | mpSurfelValidIndexBuffer = mpDevice->createStructuredBuffer( 600 | sizeof(uint), kTotalSurfelLimit, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false 601 | ); 602 | 603 | mpSurfelDirtyIndexBuffer = mpDevice->createStructuredBuffer( 604 | sizeof(uint), kTotalSurfelLimit, ResourceBindFlags::ShaderResource, MemoryType::DeviceLocal, nullptr, false 605 | ); 606 | 607 | { 608 | uint* freeIndexBuffer = new uint[kTotalSurfelLimit]; 609 | std::iota(freeIndexBuffer, freeIndexBuffer + kTotalSurfelLimit, 0); 610 | 611 | mpSurfelFreeIndexBuffer = mpDevice->createStructuredBuffer( 612 | sizeof(uint), 613 | kTotalSurfelLimit, 614 | ResourceBindFlags::UnorderedAccess, 615 | MemoryType::DeviceLocal, 616 | freeIndexBuffer, 617 | false 618 | ); 619 | 620 | delete[] freeIndexBuffer; 621 | } 622 | 623 | mpCellInfoBuffer = mpDevice->createStructuredBuffer( 624 | sizeof(CellInfo), 625 | mStaticParams.cellCount, 626 | ResourceBindFlags::UnorderedAccess, 627 | MemoryType::DeviceLocal, 628 | nullptr, 629 | false 630 | ); 631 | 632 | mpCellToSurfelBuffer = mpDevice->createStructuredBuffer( 633 | sizeof(uint), 634 | kTotalSurfelLimit * 125, 635 | ResourceBindFlags::UnorderedAccess, 636 | MemoryType::DeviceLocal, 637 | nullptr, 638 | false 639 | ); 640 | 641 | mpSurfelRayResultBuffer = mpDevice->createStructuredBuffer( 642 | sizeof(SurfelRayResult), kRayBudget, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false 643 | ); 644 | 645 | mpSurfelRecycleInfoBuffer = mpDevice->createStructuredBuffer( 646 | sizeof(SurfelRecycleInfo), 647 | kTotalSurfelLimit, 648 | ResourceBindFlags::UnorderedAccess, 649 | MemoryType::DeviceLocal, 650 | nullptr, 651 | false 652 | ); 653 | 654 | mpSurfelReservationBuffer = mpDevice->createBuffer( 655 | sizeof(uint) * mStaticParams.cellCount, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr 656 | ); 657 | 658 | mpSurfelRefCounter = mpDevice->createBuffer( 659 | sizeof(uint) * kTotalSurfelLimit, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr 660 | ); 661 | 662 | mpSurfelCounter = mpDevice->createBuffer( 663 | sizeof(uint) * _countof(kInitialStatus), 664 | ResourceBindFlags::UnorderedAccess, 665 | MemoryType::DeviceLocal, 666 | kInitialStatus 667 | ); 668 | 669 | mpReadBackBuffer = mpDevice->createBuffer( 670 | sizeof(uint) * _countof(kInitialStatus), ResourceBindFlags::None, MemoryType::ReadBack, nullptr 671 | ); 672 | 673 | mpEmptySurfelBuffer = mpDevice->createStructuredBuffer( 674 | sizeof(Surfel), kTotalSurfelLimit, ResourceBindFlags::UnorderedAccess, MemoryType::DeviceLocal, nullptr, false 675 | ); 676 | } 677 | 678 | void SurfelGI::createResolutionDependentResources() {} 679 | 680 | void SurfelGI::bindResources(const RenderData& renderData) 681 | { 682 | const auto& pPackedHitInfoTexture = renderData.getTexture(kPackedHitInfoTextureName); 683 | mpOutputTexture = renderData.getTexture(kOutputTextureName); 684 | mpIrradianceMapTexture = renderData.getTexture(kIrradianceMapTextureName); 685 | mpSurfelDepthTexture = renderData.getTexture(kSurfelDepthTextureName); 686 | 687 | // Evaluation Pass 688 | { 689 | auto var = mpSurfelEvaluationPass->getRootVar(); 690 | 691 | var[kSurfelBufferVarName] = mpSurfelBuffer; 692 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 693 | var[kCellToSurfelBufferVarName] = mpCellToSurfelBuffer; 694 | var[kSurfelRecycleInfoBufferVarName] = mpSurfelRecycleInfoBuffer; 695 | 696 | var[kSurfelRefCounterVarName] = mpSurfelRefCounter; 697 | 698 | var["gPackedHitInfo"] = pPackedHitInfoTexture; 699 | var["gSurfelDepth"] = mpSurfelDepthTexture; 700 | var["gOutput"] = mpOutputTexture; 701 | 702 | var["gSurfelDepthSampler"] = mpSurfelDepthSampler; 703 | } 704 | 705 | // Prepare Pass 706 | { 707 | auto var = mpPreparePass->getRootVar(); 708 | var[kSurfelCounterVarName] = mpSurfelCounter; 709 | } 710 | 711 | // Update Pass (Collect Cell Info Pass) 712 | { 713 | auto var = mpCollectCellInfoPass->getRootVar(); 714 | 715 | var[kSurfelBufferVarName] = mpSurfelBuffer; 716 | var[kSurfelGeometryBufferVarName] = mpSurfelGeometryBuffer; 717 | var[kSurfelDirtyIndexBufferVarName] = mpSurfelDirtyIndexBuffer; 718 | var[kSurfelValidIndexBufferVarName] = mpSurfelValidIndexBuffer; 719 | var[kSurfelFreeIndexBufferVarName] = mpSurfelFreeIndexBuffer; 720 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 721 | var[kSurfelRayResultBufferVarName] = mpSurfelRayResultBuffer; 722 | var[kSurfelRecycleInfoBufferVarName] = mpSurfelRecycleInfoBuffer; 723 | 724 | var[kSurfelRefCounterVarName] = mpSurfelRefCounter; 725 | var[kSurfelCounterVarName] = mpSurfelCounter; 726 | } 727 | 728 | // Update Pass (Accumulate Cell Info Pass) 729 | { 730 | auto var = mpAccumulateCellInfoPass->getRootVar(); 731 | 732 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 733 | var[kSurfelCounterVarName] = mpSurfelCounter; 734 | 735 | var[kSurfelReservationBufferVarName] = mpSurfelReservationBuffer; 736 | } 737 | 738 | // Update Pass (Update Cell To Surfel buffer Pass) 739 | { 740 | auto var = mpUpdateCellToSurfelBuffer->getRootVar(); 741 | 742 | var[kSurfelBufferVarName] = mpSurfelBuffer; 743 | var[kSurfelValidIndexBufferVarName] = mpSurfelValidIndexBuffer; 744 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 745 | var[kCellToSurfelBufferVarName] = mpCellToSurfelBuffer; 746 | 747 | var[kSurfelCounterVarName] = mpSurfelCounter; 748 | } 749 | 750 | // Surfel RayTrace Pass 751 | { 752 | auto var = mRtPass.pVars->getRootVar(); 753 | 754 | var[kSurfelBufferVarName] = mpSurfelBuffer; 755 | var[kSurfelGeometryBufferVarName] = mpSurfelGeometryBuffer; 756 | var[kSurfelFreeIndexBufferVarName] = mpSurfelFreeIndexBuffer; 757 | var[kSurfelValidIndexBufferVarName] = mpSurfelValidIndexBuffer; 758 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 759 | var[kCellToSurfelBufferVarName] = mpCellToSurfelBuffer; 760 | var[kSurfelRayResultBufferVarName] = mpSurfelRayResultBuffer; 761 | var[kSurfelRecycleInfoBufferVarName] = mpSurfelRecycleInfoBuffer; 762 | 763 | var[kSurfelReservationBufferVarName] = mpSurfelReservationBuffer; 764 | var[kSurfelRefCounterVarName] = mpSurfelRefCounter; 765 | var[kSurfelCounterVarName] = mpSurfelCounter; 766 | 767 | var["gSurfelDepth"] = mpSurfelDepthTexture; 768 | var["gIrradianceMap"] = mpIrradianceMapTexture; 769 | 770 | var["gSurfelDepthSampler"] = mpSurfelDepthSampler; 771 | } 772 | 773 | // Surfel Generation Pass 774 | { 775 | auto var = mpSurfelGenerationPass->getRootVar(); 776 | 777 | var[kSurfelBufferVarName] = mpSurfelBuffer; 778 | var[kSurfelGeometryBufferVarName] = mpSurfelGeometryBuffer; 779 | var[kSurfelFreeIndexBufferVarName] = mpSurfelFreeIndexBuffer; 780 | var[kSurfelValidIndexBufferVarName] = mpSurfelValidIndexBuffer; 781 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 782 | var[kCellToSurfelBufferVarName] = mpCellToSurfelBuffer; 783 | var[kSurfelRecycleInfoBufferVarName] = mpSurfelRecycleInfoBuffer; 784 | 785 | var[kSurfelRefCounterVarName] = mpSurfelRefCounter; 786 | var[kSurfelCounterVarName] = mpSurfelCounter; 787 | 788 | var["gPackedHitInfo"] = pPackedHitInfoTexture; 789 | var["gSurfelDepth"] = mpSurfelDepthTexture; 790 | var["gOutput"] = mpOutputTexture; 791 | 792 | var["gSurfelDepthSampler"] = mpSurfelDepthSampler; 793 | } 794 | 795 | // Surfel Integrate Pass 796 | { 797 | auto var = mpSurfelIntegratePass->getRootVar(); 798 | 799 | var[kSurfelBufferVarName] = mpSurfelBuffer; 800 | var[kSurfelValidIndexBufferVarName] = mpSurfelValidIndexBuffer; 801 | var[kCellInfoBufferVarName] = mpCellInfoBuffer; 802 | var[kCellToSurfelBufferVarName] = mpCellToSurfelBuffer; 803 | var[kSurfelRayResultBufferVarName] = mpSurfelRayResultBuffer; 804 | 805 | var[kSurfelCounterVarName] = mpSurfelCounter; 806 | 807 | var["gSurfelDepth"] = mpSurfelDepthTexture; 808 | var["gSurfelDepthRW"] = mpSurfelDepthTexture; 809 | var["gIrradianceMap"] = mpIrradianceMapTexture; 810 | 811 | var["gSurfelDepthSampler"] = mpSurfelDepthSampler; 812 | } 813 | } 814 | 815 | Falcor::DefineList SurfelGI::StaticParams::getDefines(const SurfelGI& owner) const 816 | { 817 | DefineList defines; 818 | 819 | defines.add("SURFEL_TARGET_AREA", std::to_string(surfelTargetArea)); 820 | defines.add("CELL_UNIT", std::to_string(cellUnit)); 821 | defines.add("CELL_DIM", std::to_string(cellDim)); 822 | defines.add("CELL_COUNT", std::to_string(cellCount)); 823 | defines.add("PER_CELL_SURFEL_LIMIT", std::to_string(perCellSurfelLimit)); 824 | 825 | if (useSurfelRadinace) 826 | defines.add("USE_SURFEL_RADIANCE"); 827 | 828 | if (limitSurfelSearch) 829 | defines.add("LIMIT_SURFEL_SEARCH"); 830 | 831 | defines.add("MAX_SURFEL_FOR_STEP", std::to_string(maxSurfelForStep)); 832 | 833 | if (useRayGuiding) 834 | defines.add("USE_RAY_GUIDING"); 835 | 836 | if (useSurfelDepth) 837 | defines.add("USE_SURFEL_DEPTH"); 838 | 839 | if (useIrradianceSharing) 840 | defines.add("USE_IRRADIANCE_SHARING"); 841 | 842 | return defines; 843 | } 844 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelGI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Falcor.h" 3 | #include "RenderGraph/RenderPass.h" 4 | #include "RenderGraph/RenderPassHelpers.h" 5 | #include "OverlayMode.slang" 6 | 7 | using namespace Falcor; 8 | 9 | class SurfelGI : public RenderPass 10 | { 11 | public: 12 | FALCOR_PLUGIN_CLASS(SurfelGI, "SurfelGI", "Surfel GI Pass"); 13 | 14 | static ref create(ref pDevice, const Properties& props) 15 | { 16 | return make_ref(pDevice, props); 17 | } 18 | 19 | SurfelGI(ref pDevice, const Properties& props); 20 | 21 | virtual void setProperties(const Properties& props) override {} 22 | virtual Properties getProperties() const override { return {}; } 23 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 24 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override {} 25 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 26 | virtual void renderUI(Gui::Widgets& widget) override; 27 | virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; 28 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 29 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override; 30 | 31 | private: 32 | void reflectInput(RenderPassReflection& reflector, uint2 resolution); 33 | void reflectOutput(RenderPassReflection& reflector, uint2 resolution); 34 | void resetAndRecompile(); 35 | void createPasses(); 36 | void createResolutionIndependentResources(); 37 | void createResolutionDependentResources(); 38 | void bindResources(const RenderData& renderData); 39 | 40 | struct RuntimeParams 41 | { 42 | // Surfel generation. 43 | float chanceMultiply = 0.3f; 44 | uint chancePower = 1; 45 | float placementThreshold = 2.f; 46 | float removalThreshold = 4.f; 47 | float thresholdGap = 2.f; 48 | uint blendingDelay = 240; 49 | Falcor::OverlayMode overlayMode = Falcor::OverlayMode::IndirectLighting; 50 | 51 | // Ray tracing. 52 | float varianceSensitivity = 40.f; 53 | uint minRayCount = 4u; 54 | uint maxRayCount = 64u; 55 | uint rayStep = 3; 56 | uint maxStep = 6; 57 | 58 | // Integrate. 59 | float shortMeanWindow = 0.03f; 60 | }; 61 | 62 | struct StaticParams 63 | { 64 | uint surfelTargetArea = 40000; 65 | float cellUnit = 0.05f; 66 | uint cellDim = 250u; 67 | uint cellCount = cellDim * cellDim * cellDim; 68 | uint perCellSurfelLimit = 1024u; 69 | 70 | bool useSurfelRadinace = true; 71 | bool limitSurfelSearch = false; 72 | uint maxSurfelForStep = 10; 73 | bool useRayGuiding = false; 74 | bool useSurfelDepth = true; 75 | bool useIrradianceSharing = true; 76 | 77 | DefineList getDefines(const SurfelGI& owner) const; 78 | }; 79 | 80 | RuntimeParams mRuntimeParams; 81 | StaticParams mStaticParams; 82 | StaticParams mTempStaticParams; 83 | 84 | uint mFrameIndex; 85 | uint mMaxFrameIndex; 86 | uint2 mFrameDim; 87 | float mFOVy; 88 | float3 mCamPos; 89 | float mRenderScale; 90 | 91 | bool mIsFrameDimChanged; 92 | bool mReadBackValid; 93 | bool mLockSurfel; 94 | bool mResetSurfelBuffer; 95 | bool mRecompile; 96 | 97 | std::vector mSurfelCount; 98 | std::vector mRayBudget; 99 | 100 | ref mpScene; 101 | ref mpFence; 102 | ref mpSampleGenerator; 103 | 104 | ref mpSurfelEvaluationPass; 105 | 106 | ref mpPreparePass; 107 | ref mpCollectCellInfoPass; 108 | ref mpAccumulateCellInfoPass; 109 | ref mpUpdateCellToSurfelBuffer; 110 | ref mpSurfelGenerationPass; 111 | ref mpSurfelIntegratePass; 112 | 113 | struct 114 | { 115 | ref pProgram; 116 | ref pBindingTable; 117 | ref pVars; 118 | } mRtPass; 119 | 120 | ref mpOutputTexture; 121 | ref mpIrradianceMapTexture; 122 | ref mpSurfelDepthTexture; 123 | 124 | ref mpSurfelBuffer; 125 | ref mpSurfelGeometryBuffer; 126 | ref mpSurfelValidIndexBuffer; 127 | ref mpSurfelDirtyIndexBuffer; 128 | ref mpSurfelFreeIndexBuffer; 129 | ref mpCellInfoBuffer; 130 | ref mpCellToSurfelBuffer; 131 | ref mpSurfelRayResultBuffer; 132 | ref mpSurfelRecycleInfoBuffer; 133 | 134 | ref mpSurfelReservationBuffer; 135 | ref mpSurfelRefCounter; 136 | ref mpSurfelCounter; 137 | 138 | ref mpEmptySurfelBuffer; 139 | ref mpReadBackBuffer; 140 | 141 | ref mpSurfelDepthSampler; 142 | }; 143 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelGenerationPass.cs.slang: -------------------------------------------------------------------------------- 1 | import Scene.Scene; 2 | import RenderPasses.Surfel.Random; 3 | import RenderPasses.Surfel.HashUtils; 4 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 5 | import RenderPasses.Surfel.SurfelGI.SurfelUtils; 6 | import RenderPasses.Surfel.SurfelGI.StaticParams; 7 | 8 | cbuffer CB 9 | { 10 | uint2 gResolution; 11 | float gFOVy; 12 | uint gFrameIndex; 13 | float gChanceMultiply; 14 | uint gChancePower; 15 | float gPlacementThreshold; 16 | float gRemovalThreshold; 17 | uint gBlendingDelay; 18 | uint gOverlayMode; 19 | float gVarianceSensitivity; 20 | } 21 | 22 | RWStructuredBuffer gSurfelBuffer; 23 | RWStructuredBuffer gSurfelGeometryBuffer; 24 | RWStructuredBuffer gSurfelFreeIndexBuffer; 25 | RWStructuredBuffer gSurfelValidIndexBuffer; 26 | RWStructuredBuffer gCellInfoBuffer; 27 | RWStructuredBuffer gCellToSurfelBuffer; 28 | RWStructuredBuffer gSurfelRecycleInfoBuffer; 29 | 30 | RWByteAddressBuffer gSurfelRefCounter; 31 | RWByteAddressBuffer gSurfelCounter; 32 | 33 | Texture2D gPackedHitInfo; 34 | Texture2D gSurfelDepth; 35 | RWTexture2D gOutput; 36 | 37 | SamplerState gSurfelDepthSampler; 38 | 39 | groupshared uint groupShareMinCoverage; 40 | groupshared uint groupShareMaxContribution; 41 | 42 | [numthreads(16, 16, 1)] 43 | void csMain( 44 | uint3 dispatchThreadId: SV_DispatchThreadID, 45 | uint groupIndex: SV_GroupIndex, 46 | uint3 groupThreadID: SV_GroupThreadID, 47 | uint3 groupdId: SV_GroupID 48 | ) 49 | { 50 | // Initialize group shared values. 51 | if (groupIndex == 0) 52 | { 53 | groupShareMinCoverage = ~0; 54 | groupShareMaxContribution = 0; 55 | } 56 | 57 | GroupMemoryBarrierWithGroupSync(); 58 | 59 | if (dispatchThreadId.x >= gResolution.x || dispatchThreadId.y >= gResolution.y) 60 | return; 61 | 62 | uint2 tilePos = groupdId.xy; 63 | uint2 pixelPos = dispatchThreadId.xy; 64 | 65 | RNG randomState; 66 | randomState.init(pixelPos, gFrameIndex); 67 | 68 | HitInfo hitInfo = HitInfo(gPackedHitInfo[pixelPos]); 69 | if (!hitInfo.isValid()) 70 | return; 71 | 72 | TriangleHit triangleHit = hitInfo.getTriangleHit(); 73 | VertexData v = gScene.getVertexData(triangleHit); 74 | float4 curPosH = mul(gScene.camera.data.viewProjMatNoJitter, float4(v.posW, 1.f)); 75 | float depth = curPosH.z / curPosH.w; 76 | 77 | int3 cellPos = getCellPos(v.posW, gScene.camera.getPosition(), kCellUnit); 78 | if (!isCellValid(cellPos)) 79 | return; 80 | 81 | uint flattenIndex = getFlattenCellIndex(cellPos); 82 | CellInfo cellInfo = gCellInfoBuffer[flattenIndex]; 83 | 84 | // Evaluate min coverage value and pixel position. 85 | // Also evaluate max contribution and surfel index (for handling over-coverage). 86 | // Also evalute weighted color output (indrect lighting). 87 | float4 indirectLighting = float4(0.f); 88 | { 89 | float coverage = 0.f; 90 | float varianceEx = 0.f; 91 | float rayCountEx = 0.f; 92 | uint refCount = 0; 93 | uint life = 0; 94 | 95 | float maxVariance = 0.f; 96 | float maxContribution = 0.f; 97 | uint maxContributionSurfelIndex = randomState.next_uint(cellInfo.surfelCount); 98 | 99 | for (uint i = 0; i < cellInfo.surfelCount; ++i) 100 | { 101 | uint surfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + i]; 102 | Surfel surfel = gSurfelBuffer[surfelIndex]; 103 | 104 | float3 bias = v.posW - surfel.position; 105 | float dist2 = dot(bias, bias); 106 | 107 | if (dist2 < surfel.radius * surfel.radius) 108 | { 109 | float3 normal = normalize(surfel.normal); 110 | 111 | float dotN = dot(v.normalW, normal); 112 | if (dotN > 0) 113 | { 114 | const SurfelRecycleInfo surfelRecycleInfo = gSurfelRecycleInfoBuffer[surfelIndex]; 115 | bool isSleeping = surfelRecycleInfo.status & 0x0001; 116 | bool lastSeen = surfelRecycleInfo.status & 0x0002; 117 | 118 | if (!isSleeping) 119 | { 120 | float dist = sqrt(dist2); 121 | float contribution = 1.f; 122 | 123 | contribution *= saturate(dotN); 124 | contribution *= saturate(1 - dist / surfel.radius); 125 | contribution = smoothstep(0, 1, contribution); 126 | 127 | coverage += contribution; 128 | 129 | #ifdef USE_SURFEL_DEPTH 130 | { 131 | float2 uv = getSurfelDepthUV(surfelIndex, bias / dist, normal); 132 | float2 surfelDepth = gSurfelDepth.SampleLevel(gSurfelDepthSampler, uv, 0u); 133 | 134 | float mean = surfelDepth.x; 135 | float sqrMean = surfelDepth.y; 136 | 137 | if (dist > mean) 138 | { 139 | float variance = sqrMean - pow(mean, 2); 140 | contribution *= variance / (variance + pow(dist - mean, 2)); 141 | } 142 | } 143 | #else // USE_SURFEL_DEPTH 144 | #endif // USE_SURFEL_DEPTH 145 | 146 | // Delay blending if not sufficient sample is accumulated. 147 | // Because samples are updated per frame, so do not use sample count directly. 148 | indirectLighting += float4(surfel.radiance, 1.f) * contribution * smoothstep(0, gBlendingDelay, surfelRecycleInfo.frame); 149 | 150 | varianceEx += length(surfel.msmeData.variance) * contribution; 151 | rayCountEx += surfel.rayCount * contribution; 152 | 153 | refCount = max(refCount, gSurfelRefCounter.Load(surfelIndex)); 154 | life = max(life, surfelRecycleInfo.life); 155 | 156 | if (maxContribution < contribution) 157 | { 158 | maxContribution = contribution; 159 | maxContributionSurfelIndex = i; 160 | } 161 | 162 | maxVariance = max(maxVariance, length(surfel.msmeData.variance)); 163 | } 164 | 165 | if (!lastSeen) 166 | gSurfelRecycleInfoBuffer[surfelIndex].status |= 0x0002; 167 | } 168 | } 169 | } 170 | 171 | if (indirectLighting.w > 0) 172 | { 173 | indirectLighting.xyz /= indirectLighting.w; 174 | indirectLighting.w = saturate(indirectLighting.w); 175 | 176 | varianceEx /= indirectLighting.w; 177 | rayCountEx /= indirectLighting.w; 178 | 179 | // Write texture by overlay mode. 180 | if (gOverlayMode == 0) 181 | { 182 | gOutput[pixelPos] = indirectLighting; 183 | } 184 | else if (gOverlayMode == 1) 185 | { 186 | gOutput[pixelPos] = float4(stepColor(maxVariance * gVarianceSensitivity, 0.8f, 0.5f), 1); 187 | } 188 | else if (gOverlayMode == 2) 189 | { 190 | gOutput[pixelPos] = float4(stepColor(rayCountEx, 48.f, 32.f), 1); 191 | } 192 | else if (gOverlayMode == 3) 193 | { 194 | gOutput[pixelPos] = float4(lerpColor(refCount / 256.f), 1); 195 | } 196 | else if (gOverlayMode == 4) 197 | { 198 | gOutput[pixelPos] = float4(step(life, 0u), step(1u, life), 0, 1); 199 | } 200 | else if (gOverlayMode == 5) 201 | { 202 | gOutput[pixelPos] = float4(lerpColor(smoothstep(gPlacementThreshold, gRemovalThreshold, coverage)), 1); 203 | } 204 | } 205 | 206 | uint coverageData = 0; 207 | coverageData |= ((f32tof16(coverage) & 0x0000FFFF) << 16); 208 | coverageData |= ((randomState.next_uint(255) & 0x000000FF) << 8); 209 | coverageData |= ((groupThreadID.x & 0x0000000F) << 4); 210 | coverageData |= ((groupThreadID.y & 0x0000000F) << 0); 211 | 212 | InterlockedMin(groupShareMinCoverage, coverageData); 213 | 214 | uint contributionData = 0; 215 | contributionData |= ((f32tof16(maxContribution) & 0x0000FFFF) << 16); 216 | contributionData |= ((maxContributionSurfelIndex & 0x0000FFFF) << 0); 217 | 218 | InterlockedMax(groupShareMaxContribution, contributionData); 219 | 220 | GroupMemoryBarrierWithGroupSync(); 221 | } 222 | 223 | uint coverageData = groupShareMinCoverage; 224 | float coverage = f16tof32((coverageData & 0xFFFF0000) >> 16); 225 | uint x = (coverageData & 0x000000F0) >> 4; 226 | uint y = (coverageData & 0x0000000F) >> 0; 227 | 228 | if (cellInfo.surfelCount < kPerCellSurfelLimit) 229 | { 230 | if (groupThreadID.x == x && groupThreadID.y == y) 231 | { 232 | // If seat for surfel in current cell avaliable and coverage is under threshold, 233 | // genearte new surfel probabilistically. 234 | if (coverage <= gPlacementThreshold) 235 | { 236 | const float chance = pow(depth, gChancePower); 237 | if (randomState.next_float() < chance * gChanceMultiply) 238 | { 239 | int freeSurfelCount; 240 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::FreeSurfel, -1, freeSurfelCount); 241 | 242 | if (0 < freeSurfelCount && freeSurfelCount <= kTotalSurfelLimit) 243 | { 244 | uint validSurfelCount; 245 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::ValidSurfel, 1, validSurfelCount); 246 | 247 | if (validSurfelCount < kTotalSurfelLimit) 248 | { 249 | uint newIndex = gSurfelFreeIndexBuffer[freeSurfelCount - 1]; 250 | 251 | float varRadius = calcSurfelRadius( 252 | distance(gScene.camera.getPosition(), v.posW), 253 | gFOVy, 254 | gResolution, 255 | kSurfelTargetArea, 256 | kCellUnit 257 | ); 258 | 259 | Surfel newSurfel = Surfel(v.posW, v.normalW, varRadius); 260 | 261 | newSurfel.radiance = indirectLighting.xyz; 262 | newSurfel.msmeData.mean = indirectLighting.xyz; 263 | newSurfel.msmeData.shortMean = indirectLighting.xyz; 264 | 265 | gSurfelValidIndexBuffer[validSurfelCount] = newIndex; 266 | gSurfelBuffer[newIndex] = newSurfel; 267 | gSurfelRecycleInfoBuffer[newIndex] = { kMaxLife, 0u, 0u }; 268 | gSurfelGeometryBuffer[newIndex] = hitInfo.data; 269 | gSurfelRefCounter.Store(newIndex, 0); 270 | } 271 | } 272 | } 273 | } 274 | } 275 | } 276 | 277 | if (cellInfo.surfelCount > 0) 278 | { 279 | if (groupThreadID.x == x && groupThreadID.y == y) 280 | { 281 | // If coverage is upper removal threshold, 282 | // remove surfel that most contribute to coverage probabilistically. 283 | if (coverage > gRemovalThreshold) 284 | { 285 | const float chance = pow(depth, gChancePower); 286 | if (randomState.next_float() < chance * gChanceMultiply) 287 | { 288 | uint contributionData = groupShareMaxContribution; 289 | float maxContribution = f16tof32((contributionData & 0xFFFF0000) >> 16); 290 | uint maxContributionSurfelIndex = (contributionData & 0x0000FFFF) >> 0; 291 | 292 | uint toDestroySurfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + maxContributionSurfelIndex]; 293 | Surfel toDestroySurfel = gSurfelBuffer[toDestroySurfelIndex]; 294 | 295 | toDestroySurfel.radius = 0; 296 | gSurfelBuffer[toDestroySurfelIndex] = toDestroySurfel; 297 | } 298 | } 299 | } 300 | } 301 | } 302 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelIntegratePass.cs.slang: -------------------------------------------------------------------------------- 1 | #include "Utils/Math/MathConstants.slangh" 2 | 3 | import Scene.Scene; 4 | import Utils.Color.ColorHelpers; 5 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 6 | import RenderPasses.Surfel.SurfelGI.SurfelUtils; 7 | import RenderPasses.Surfel.SurfelGI.StaticParams; 8 | import RenderPasses.Surfel.SurfelGI.MultiscaleMeanEstimator; 9 | 10 | cbuffer CB 11 | { 12 | float gShortMeanWindow; 13 | float3 gCameraPos; 14 | float gVarianceSensitivity; 15 | } 16 | 17 | RWStructuredBuffer gSurfelBuffer; 18 | RWStructuredBuffer gSurfelValidIndexBuffer; 19 | RWStructuredBuffer gCellInfoBuffer; 20 | RWStructuredBuffer gCellToSurfelBuffer; 21 | RWStructuredBuffer gSurfelRayResultBuffer; 22 | 23 | RWByteAddressBuffer gSurfelCounter; 24 | 25 | Texture2D gSurfelDepth; 26 | RWTexture2D gSurfelDepthRW; 27 | RWTexture2D gIrradianceMap; 28 | 29 | SamplerState gSurfelDepthSampler; 30 | 31 | // Will be moved at Update Pass later for optimzation. 32 | [numthreads(32, 1, 1)] 33 | void csMain(uint3 dispatchThreadId: SV_DispatchThreadID) 34 | { 35 | uint validSurfelCount = gSurfelCounter.Load((int)SurfelCounterOffset::ValidSurfel); 36 | if (dispatchThreadId.x >= validSurfelCount) 37 | return; 38 | 39 | uint surfelIndex = gSurfelValidIndexBuffer[dispatchThreadId.x]; 40 | Surfel surfel = gSurfelBuffer[surfelIndex]; 41 | 42 | // If no ray is allocated, exit. 43 | if (surfel.rayCount == 0) 44 | return; 45 | 46 | // Irrdiance map left top coordinate. 47 | uint2 irrMapLT = uint2( 48 | surfelIndex % (kIrradianceMapRes.x / kIrradianceMapUnit.x), 49 | surfelIndex / (kIrradianceMapRes.x / kIrradianceMapUnit.y) 50 | ); 51 | irrMapLT *= kIrradianceMapUnit; 52 | 53 | // Surfel depth texture left top coordinate. 54 | uint2 surfelDepthLT = uint2( 55 | surfelIndex % (kSurfelDepthTextureRes.x / kSurfelDepthTextureUnit.x), 56 | surfelIndex / (kSurfelDepthTextureRes.x / kSurfelDepthTextureUnit.y) 57 | ); 58 | surfelDepthLT *= kSurfelDepthTextureUnit; 59 | 60 | float3 surfelRadiance = float3(0.f); 61 | for (uint rayIndex = 0; rayIndex < surfel.rayCount; ++rayIndex) 62 | { 63 | const SurfelRayResult rayResult = gSurfelRayResultBuffer[surfel.rayOffset + rayIndex]; 64 | 65 | float3 Lr = rayResult.radiance; 66 | float3 dirLocal = rayResult.dirLocal; 67 | float3 dirWorld = rayResult.dirWorld; 68 | float pdf = rayResult.pdf; 69 | float surfelDepth = (rayResult.firstRayLength > 0) ? clamp(rayResult.firstRayLength, 0, surfel.radius) : surfel.radius; 70 | 71 | // Encode ray direction. 72 | float2 uv = octEncode(normalize(dirLocal)); 73 | 74 | #ifdef USE_RAY_GUIDING 75 | 76 | // Write luminance at irradiance map. 77 | uint2 irrMapOffset = kIrradianceMapHalfUnit + int2( 78 | sign(uv.x) * round(abs(uv.x * kIrradianceMapHalfUnit.x)), 79 | sign(uv.y) * round(abs(uv.y * kIrradianceMapHalfUnit.y)) 80 | ); 81 | uint2 irrMapCoord = irrMapLT + irrMapOffset; 82 | 83 | // Write radiance of ray at texture for ray-guiding. 84 | // Mininum boundary is 1e-3f, because pdf should not be 0 for importance sampling. 85 | float k = 1e-2f; 86 | float delta = luminance(Lr) - gIrradianceMap[irrMapCoord]; 87 | gIrradianceMap[irrMapCoord] = max(1e-3f, gIrradianceMap[irrMapCoord] + k * delta); 88 | 89 | #else // USE_RAY_GUIDING 90 | #endif // USE_RAY_GUIDING 91 | 92 | #ifdef USE_SURFEL_DEPTH 93 | 94 | // Write surfel depth. 95 | uint2 surfelDepthTextureOffset = kSurfelDepthTextureHalfUnit + int2( 96 | sign(uv.x) * round(abs(uv.x * (kSurfelDepthTextureHalfUnit.x - 1))), 97 | sign(uv.y) * round(abs(uv.y * (kSurfelDepthTextureHalfUnit.y - 1))) 98 | ); 99 | uint2 surfelDepthTextureCoord = surfelDepthLT + surfelDepthTextureOffset; 100 | 101 | float2 signedUV = ((int2)surfelDepthTextureOffset - kSurfelDepthTextureHalfUnit) / (kSurfelDepthTextureHalfUnit - int2(1, 1)); 102 | float3 texelDir = octDecode(signedUV); 103 | float depthWeight = saturate(dot(dirLocal, texelDir)); 104 | 105 | float k2 = 1e-2f; 106 | float2 delta2 = float2(surfelDepth, pow(surfelDepth, 2)) - gSurfelDepthRW[surfelDepthTextureCoord]; 107 | gSurfelDepthRW[surfelDepthTextureCoord] = gSurfelDepthRW[surfelDepthTextureCoord] + k2 * delta2 * depthWeight; 108 | 109 | #else // USE_SURFEL_DEPTH 110 | #endif // USE_SURFEL_DEPTH 111 | 112 | // Calculate incident radiance by using importance sampling. 113 | surfelRadiance += Lr * dot(dirWorld, surfel.normal) * ((1.f / (16 * M_PI)) / max(1e-12f, pdf)); 114 | } 115 | 116 | // Average radiance. 117 | surfelRadiance /= surfel.rayCount; 118 | 119 | #ifdef USE_RAY_GUIDING 120 | 121 | // Sum luminances and check if irradiance map of surfel has hole (not sampled yet). 122 | float sumLuminance = 0.f; 123 | bool hasHole = false; 124 | 125 | for (uint y = 0; y < kIrradianceMapUnit.y; ++y) 126 | { 127 | for (uint x = 0; x < kIrradianceMapUnit.x; ++x) 128 | { 129 | const float l = gIrradianceMap[irrMapLT + uint2(x, y)]; 130 | sumLuminance += l; 131 | if (!hasHole && step(l, 1e-12f)) 132 | hasHole = true; 133 | } 134 | } 135 | 136 | surfel.sumLuminance = sumLuminance; 137 | surfel.hasHole = hasHole; 138 | 139 | #else // USE_RAY_GUIDING 140 | #endif // USE_RAY_GUIDING 141 | 142 | #ifdef USE_SURFEL_DEPTH 143 | 144 | // Write border. 145 | for (uint x = 1; x < kSurfelDepthTextureUnit.x - 1; ++x) 146 | { 147 | gSurfelDepthRW[surfelDepthLT + uint2(x, 0)] = gSurfelDepthRW[surfelDepthLT + uint2(x, 1)]; 148 | gSurfelDepthRW[surfelDepthLT + uint2(x, kSurfelDepthTextureUnit.y - 1)] = 149 | gSurfelDepthRW[surfelDepthLT + uint2(x, kSurfelDepthTextureUnit.y - 2)]; 150 | } 151 | for (uint y = 1; y < kSurfelDepthTextureUnit.y - 1; ++y) 152 | { 153 | gSurfelDepthRW[surfelDepthLT + uint2(0, y)] = gSurfelDepthRW[surfelDepthLT + uint2(1, y)]; 154 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 1, y)] = 155 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 2, y)]; 156 | } 157 | 158 | // Write corner. 159 | gSurfelDepthRW[surfelDepthLT + uint2(0, 0)] = ( 160 | gSurfelDepthRW[surfelDepthLT + uint2(0, 1)] + 161 | gSurfelDepthRW[surfelDepthLT + uint2(1, 0)] 162 | ) / 2.f; 163 | 164 | gSurfelDepthRW[surfelDepthLT + uint2(0, kSurfelDepthTextureUnit.y - 1)] = ( 165 | gSurfelDepthRW[surfelDepthLT + uint2(0, kSurfelDepthTextureUnit.y - 2)] + 166 | gSurfelDepthRW[surfelDepthLT + uint2(1, kSurfelDepthTextureUnit.y - 1)] 167 | ) / 2.f; 168 | 169 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 1, kSurfelDepthTextureUnit.y - 1)] = ( 170 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 1, kSurfelDepthTextureUnit.y - 2)] + 171 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 2, kSurfelDepthTextureUnit.y - 1)] 172 | ) / 2.f; 173 | 174 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 1, 0)] = ( 175 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 1, 1)] + 176 | gSurfelDepthRW[surfelDepthLT + uint2(kSurfelDepthTextureUnit.x - 2, 0)] 177 | ) / 2.f; 178 | 179 | #else // USE_SURFEL_DEPTH 180 | #endif // USE_SURFEL_DEPTH 181 | 182 | #ifdef USE_IRRADIANCE_SHARING 183 | 184 | float4 sharedRadiance = float4(0.f); 185 | int3 cellPos = getCellPos(surfel.position, gCameraPos, kCellUnit); 186 | if (isCellValid(cellPos)) 187 | { 188 | const float3 centerPos = surfel.position; 189 | const float3 centerNormal = surfel.normal; 190 | 191 | uint flattenIndex = getFlattenCellIndex(cellPos); 192 | CellInfo cellInfo = gCellInfoBuffer[flattenIndex]; 193 | 194 | for (uint i = 0; i < cellInfo.surfelCount; ++i) 195 | { 196 | uint neiSurfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + i]; 197 | Surfel neiSurfel = gSurfelBuffer[neiSurfelIndex]; 198 | 199 | float3 bias = centerPos - neiSurfel.position; 200 | float dist2 = dot(bias, bias); 201 | float affectRadius = kCellUnit * sqrt(2); 202 | 203 | if (dist2 < pow(affectRadius, 2)) 204 | { 205 | float dotN = dot(neiSurfel.normal, centerNormal); 206 | if (dotN > 0) 207 | { 208 | float dist = sqrt(dist2); 209 | float contribution = 1.f; 210 | 211 | contribution *= saturate(dotN); 212 | contribution *= saturate(1 - dist / affectRadius); 213 | contribution = smoothstep(0, 1, contribution); 214 | 215 | #ifdef USE_SURFEL_DEPTH 216 | { 217 | float2 uv = getSurfelDepthUV(surfelIndex, bias / dist, centerNormal); 218 | float2 surfelDepth = gSurfelDepth.SampleLevel(gSurfelDepthSampler, uv, 0u); 219 | 220 | float mean = surfelDepth.x; 221 | float sqrMean = surfelDepth.y; 222 | 223 | if (dist > mean) 224 | { 225 | float variance = sqrMean - pow(mean, 2); 226 | contribution *= variance / (variance + pow(dist - mean, 2)); 227 | } 228 | } 229 | #else // USE_SURFEL_DEPTH 230 | #endif // USE_SURFEL_DEPTH 231 | 232 | sharedRadiance += float4(neiSurfel.radiance, 1.f) * contribution; 233 | } 234 | } 235 | } 236 | 237 | if (sharedRadiance.w > 0) 238 | { 239 | sharedRadiance.xyz /= sharedRadiance.w; 240 | surfelRadiance = lerp( 241 | surfelRadiance, 242 | sharedRadiance.xyz, 243 | saturate(length(surfel.msmeData.variance) * gVarianceSensitivity) 244 | ); 245 | } 246 | } 247 | 248 | #else // USE_IRRADIANCE_SHARING 249 | #endif // USE_IRRADIANCE_SHARING 250 | 251 | // Update surfel radiance using Multiscale Mean Estimator. 252 | float3 mean = MSME(surfelRadiance, surfel.msmeData, gShortMeanWindow); 253 | surfel.radiance = mean; 254 | 255 | // Write back to buffer. 256 | gSurfelBuffer[surfelIndex] = surfel; 257 | } 258 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelPreparePass.cs.slang: -------------------------------------------------------------------------------- 1 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 2 | 3 | RWByteAddressBuffer gSurfelCounter; 4 | 5 | [numthreads(1, 1, 1)] 6 | void csMain(uint3 dispatchThreadId: SV_DispatchThreadID) 7 | { 8 | const uint dirtySurfelCount = clamp(gSurfelCounter.Load((int)SurfelCounterOffset::ValidSurfel), 0, kTotalSurfelLimit); 9 | 10 | gSurfelCounter.Store((int)SurfelCounterOffset::ValidSurfel, 0); 11 | gSurfelCounter.Store((int)SurfelCounterOffset::DirtySurfel, dirtySurfelCount); 12 | gSurfelCounter.Store((int)SurfelCounterOffset::FreeSurfel, clamp(asint(gSurfelCounter.Load((int)SurfelCounterOffset::FreeSurfel)), 0, (int)kTotalSurfelLimit)); 13 | gSurfelCounter.Store((int)SurfelCounterOffset::Cell, 0); 14 | gSurfelCounter.Store((int)SurfelCounterOffset::RequestedRay, 0); 15 | gSurfelCounter.Store((int)SurfelCounterOffset::MissBounce, 0); 16 | } 17 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelRayTrace.rt.slang: -------------------------------------------------------------------------------- 1 | #include "Utils/Math/MathConstants.slangh" 2 | 3 | import Scene.Raytracing; 4 | import Utils.Sampling.SampleGenerator; 5 | import Rendering.Lights.LightHelpers; 6 | import Utils.Geometry.GeometryHelpers; 7 | import Utils.Color.ColorHelpers; 8 | import Utils.Math.MathHelpers; 9 | import RenderPasses.Surfel.Random; 10 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 11 | import RenderPasses.Surfel.SurfelGI.SurfelUtils; 12 | import RenderPasses.Surfel.SurfelGI.StaticParams; 13 | 14 | /** 15 | Raytracing shader for surfel GI. 16 | 17 | First, ray steps until the RayStep value is reached. 18 | At last step (= RayStep), try to finalize path using surfel radiance. 19 | It is possible to skip forward bounces by using surfel radiance. 20 | But if surfel radiance is invalid, goto next step, even if it is last step. 21 | By doing this, it is possible to trace areas where surfel does not exist. 22 | But if ray exceed MaxStep, terminate path, no matter what. 23 | Because it is possible to stuck in infinite loop. 24 | */ 25 | 26 | cbuffer CB 27 | { 28 | uint gFrameIndex; ///< Frame index. 29 | uint gRayStep; ///< How many steps does ray go. 30 | uint gMaxStep; ///< Global maxium step count. No ray step can exceed this value. 31 | } 32 | 33 | // [status] 34 | // 0x0001 : isSleeping 35 | // 0x0002 : terminated 36 | struct ScatterPayload 37 | { 38 | float3 radiance; 39 | float3 thp; 40 | float3 origin; 41 | float3 direction; 42 | float firstRayLength; 43 | uint16_t currStep; 44 | uint16_t status; 45 | 46 | SampleGenerator sg; 47 | 48 | __init(SampleGenerator sg, bool isSleeping) 49 | { 50 | this.radiance = float3(0, 0, 0); 51 | this.thp = float3(1, 1, 1); 52 | this.origin = float3(0, 0, 0); 53 | this.direction = float3(0, 0, 0); 54 | this.firstRayLength = 0.f; 55 | this.currStep = 1u; // Be aware. Start from 1. 56 | this.status = isSleeping ? 0x0001 : 0x0000; 57 | } 58 | } 59 | 60 | struct ShadowPayload 61 | { 62 | bool visible; 63 | } 64 | 65 | RWStructuredBuffer gSurfelBuffer; 66 | RWStructuredBuffer gSurfelGeometryBuffer; 67 | RWStructuredBuffer gSurfelFreeIndexBuffer; 68 | RWStructuredBuffer gSurfelValidIndexBuffer; 69 | RWStructuredBuffer gCellInfoBuffer; 70 | RWStructuredBuffer gCellToSurfelBuffer; 71 | RWStructuredBuffer gSurfelRayResultBuffer; 72 | RWStructuredBuffer gSurfelRecycleInfoBuffer; 73 | 74 | RWByteAddressBuffer gSurfelReservationBuffer; 75 | RWByteAddressBuffer gSurfelRefCounter; 76 | RWByteAddressBuffer gSurfelCounter; 77 | 78 | Texture2D gSurfelDepth; 79 | RWTexture2D gIrradianceMap; 80 | 81 | SamplerState gSurfelDepthSampler; 82 | 83 | bool traceShadowRay(float3 origin, float3 dir, float distance) 84 | { 85 | RayDesc ray; 86 | ray.Origin = origin; 87 | ray.Direction = dir; 88 | ray.TMin = 0.f; 89 | ray.TMax = distance; 90 | 91 | ShadowPayload shadowPayload; 92 | shadowPayload.visible = false; 93 | TraceRay( 94 | gScene.rtAccel, 95 | RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 96 | 0xff, 97 | 1, /* hitIdx */ 98 | rayTypeCount, 99 | 1, /* missIdx */ 100 | ray, 101 | shadowPayload 102 | ); 103 | 104 | return shadowPayload.visible; 105 | } 106 | 107 | void traceScatterRay(inout ScatterPayload scatterPayload) 108 | { 109 | RayDesc ray; 110 | ray.Origin = scatterPayload.origin; 111 | ray.TMin = 0.f; 112 | ray.TMax = FLT_MAX; 113 | ray.Direction = scatterPayload.direction; 114 | 115 | TraceRay( 116 | gScene.rtAccel, 117 | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES | RAY_FLAG_FORCE_OPAQUE | RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 118 | 0xff, 119 | 0, /* hitIdx */ 120 | rayTypeCount, 121 | 0, /* missIdx */ 122 | ray, 123 | scatterPayload 124 | ); 125 | } 126 | 127 | // [Sample ONE light sources] and divide by pdf. 128 | float3 evalAnalyticLight(const ShadingData sd, const IMaterialInstance mi, inout SampleGenerator sg) 129 | { 130 | const uint lightCount = gScene.getLightCount(); 131 | if (lightCount == 0) 132 | return float3(0.f); 133 | 134 | RNG rng; 135 | rng.init(DispatchRaysIndex().xx, gFrameIndex); 136 | 137 | const uint lightIndex = rng.next_uint(lightCount - 1); 138 | float invPdf = lightCount; // Probability is all same, so pdf is 1/N. 139 | 140 | AnalyticLightSample ls; 141 | if (!sampleLight(sd.posW, gScene.getLight(lightIndex), sg, ls)) 142 | return float3(0.f); 143 | 144 | const uint lobeTypes = mi.getLobeTypes(sd); 145 | const bool hasReflection = lobeTypes & uint(LobeType::Reflection); 146 | const bool hasTransmission = lobeTypes & uint(LobeType::Transmission); 147 | float NdotL = dot(sd.getOrientedFaceNormal(), ls.dir); 148 | if ((NdotL <= kMinCosTheta && !hasTransmission) || (NdotL >= -kMinCosTheta && !hasReflection)) 149 | return float3(0.f); 150 | 151 | // Trace shadow ray to check light source is visible or not. 152 | const float3 origin = computeRayOrigin(sd.posW, dot(sd.faceN, ls.dir) >= 0.f ? sd.faceN : -sd.faceN); 153 | if (!traceShadowRay(origin, ls.dir, ls.distance)) 154 | return float3(0.f); 155 | 156 | return mi.eval(sd, ls.dir, sg) * ls.Li * invPdf; 157 | } 158 | 159 | // Omit multiple bouncing by using radiance of surfel. 160 | // Return true if surfel radiance is valid, and successfully applied. 161 | // Return false if surfel is invalid, so need to goto next step. 162 | bool finalize(inout ScatterPayload scatterPayload, const VertexData v, const TriangleHit triangleHit) 163 | { 164 | #ifdef USE_SURFEL_RADIANCE 165 | 166 | float4 Lr = float4(0.f); 167 | 168 | int3 cellPos = getCellPos(v.posW, gScene.camera.getPosition(), kCellUnit); 169 | if (!isCellValid(cellPos)) 170 | { 171 | // Surfel radiance is invalid. 172 | // Need to goto next step, so, do not terminate path. 173 | return false; 174 | } 175 | 176 | uint flattenIndex = getFlattenCellIndex(cellPos); 177 | CellInfo cellInfo = gCellInfoBuffer[flattenIndex]; 178 | 179 | // If surfel count in cell is too much, or some probability, 180 | // do not search surfel. 181 | if (cellInfo.surfelCount > 64 || sampleNext1D(scatterPayload.sg) < 0.2f) 182 | { 183 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::MissBounce, 1); 184 | return false; 185 | } 186 | 187 | float sleepingCoverage = 0.f; 188 | float maxContribution = 0.f; 189 | int maxContributionSleepingSurfelIndex = -1; 190 | 191 | #ifdef LIMIT_SURFEL_SEARCH 192 | 193 | const uint selectionCount = min(kMaxSurfelForStep, cellInfo.surfelCount); 194 | for (uint i = 0; i < selectionCount; ++i) 195 | { 196 | uint randomSelection = sampleNext1D(scatterPayload.sg) * (cellInfo.surfelCount - 1); 197 | uint surfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + randomSelection]; 198 | Surfel surfel = gSurfelBuffer[surfelIndex]; 199 | SurfelRecycleInfo info = gSurfelRecycleInfoBuffer[surfelIndex]; 200 | bool isSleeping = info.status & 0x0001; 201 | 202 | float3 bias = v.posW - surfel.position; 203 | float dist2 = dot(bias, bias); 204 | 205 | if (dist2 < surfel.radius * surfel.radius) 206 | { 207 | float3 normal = normalize(surfel.normal); 208 | 209 | float dotN = dot(v.normalW, normal); 210 | if (dotN > 0) 211 | { 212 | float dist = sqrt(dist2); 213 | float contribution = 1.f; 214 | 215 | contribution *= saturate(dotN); 216 | contribution *= saturate(1 - dist / surfel.radius); 217 | contribution = smoothstep(0, 1, contribution); 218 | 219 | if (gUseSurfelDepth) 220 | { 221 | float2 uv = getSurfelDepthUV(surfelIndex, bias / dist, normal); 222 | float2 surfelDepth = gSurfelDepth.SampleLevel(gSurfelDepthSampler, uv, 0u); 223 | 224 | float mean = surfelDepth.x; 225 | float sqrMean = surfelDepth.y; 226 | 227 | if (dist > mean) 228 | { 229 | float variance = sqrMean - pow(mean, 2); 230 | contribution *= variance / (variance + pow(dist - mean, 2)); 231 | } 232 | } 233 | 234 | Lr += float4(surfel.radiance, 1.f) * contribution; 235 | 236 | if (isSleeping) 237 | { 238 | sleepingCoverage += contribution; 239 | if (maxContribution < contribution) 240 | { 241 | maxContribution = contribution; 242 | maxContributionSleepingSurfelIndex = surfelIndex; 243 | } 244 | } 245 | 246 | gSurfelRefCounter.InterlockedAdd(surfelIndex, 1); 247 | } 248 | } 249 | } 250 | 251 | #else // LIMIT_SURFEL_SEARCH 252 | 253 | for (uint i = 0; i < cellInfo.surfelCount; ++i) 254 | { 255 | uint surfelIndex = gCellToSurfelBuffer[cellInfo.cellToSurfelBufferOffset + i]; 256 | Surfel surfel = gSurfelBuffer[surfelIndex]; 257 | SurfelRecycleInfo info = gSurfelRecycleInfoBuffer[surfelIndex]; 258 | bool isSleeping = info.status & 0x0001; 259 | 260 | float3 bias = v.posW - surfel.position; 261 | float dist2 = dot(bias, bias); 262 | 263 | if (dist2 < surfel.radius * surfel.radius) 264 | { 265 | float3 normal = normalize(surfel.normal); 266 | 267 | float dotN = dot(v.normalW, normal); 268 | if (dotN > 0) 269 | { 270 | float dist = sqrt(dist2); 271 | float contribution = 1.f; 272 | 273 | contribution *= saturate(dotN); 274 | contribution *= saturate(1 - dist / surfel.radius); 275 | contribution = smoothstep(0, 1, contribution); 276 | 277 | #ifdef USE_SURFEL_DEPTH 278 | { 279 | float2 uv = getSurfelDepthUV(surfelIndex, bias / dist, normal); 280 | float2 surfelDepth = gSurfelDepth.SampleLevel(gSurfelDepthSampler, uv, 0u); 281 | 282 | float mean = surfelDepth.x; 283 | float sqrMean = surfelDepth.y; 284 | 285 | if (dist > mean) 286 | { 287 | float variance = sqrMean - pow(mean, 2); 288 | contribution *= variance / (variance + pow(dist - mean, 2)); 289 | } 290 | } 291 | #else // USE_SURFEL_DEPTH 292 | #endif // USE_SURFEL_DEPTH 293 | 294 | Lr += float4(surfel.radiance, 1.f) * contribution; 295 | 296 | if (isSleeping) 297 | { 298 | sleepingCoverage += contribution; 299 | if (maxContribution < contribution) 300 | { 301 | maxContribution = contribution; 302 | maxContributionSleepingSurfelIndex = surfelIndex; 303 | } 304 | } 305 | 306 | gSurfelRefCounter.InterlockedAdd(surfelIndex, 1); 307 | } 308 | } 309 | } 310 | 311 | #endif // LIMIT_SURFEL_SEARCH 312 | 313 | // No surfel found. 314 | // Need to goto next step, so, do not terminate path. 315 | if (Lr.w <= 0.f) 316 | { 317 | // Sleeping surfel should be spawned at low surfel count area. 318 | if (cellInfo.surfelCount < 8) 319 | { 320 | uint reservedCount; 321 | gSurfelReservationBuffer.InterlockedAdd(flattenIndex, 1u, reservedCount); 322 | 323 | // Limit surfel spawn per cell for preventing over-spawnning. 324 | if (reservedCount < 8) 325 | { 326 | int freeSurfelCount; 327 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::FreeSurfel, -1, freeSurfelCount); 328 | 329 | if (0 < freeSurfelCount && freeSurfelCount <= kTotalSurfelLimit) 330 | { 331 | uint validSurfelCount; 332 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::ValidSurfel, 1, validSurfelCount); 333 | 334 | if (validSurfelCount < kTotalSurfelLimit) 335 | { 336 | uint newIndex = gSurfelFreeIndexBuffer[freeSurfelCount - 1]; 337 | Surfel newSurfel = Surfel(v.posW, v.normalW, 1e-6f); 338 | 339 | gSurfelValidIndexBuffer[validSurfelCount] = newIndex; 340 | gSurfelBuffer[newIndex] = newSurfel; 341 | gSurfelRecycleInfoBuffer[newIndex] = { 1u, 0u, true }; 342 | gSurfelGeometryBuffer[newIndex] = triangleHit.pack(); 343 | gSurfelRefCounter.Store(newIndex, 1); 344 | } 345 | } 346 | } 347 | } 348 | 349 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::MissBounce, 1); 350 | return false; 351 | } 352 | 353 | // If sleeping surfels are over-coveraged, then destroy max contribution sleeping surfel. 354 | if (sleepingCoverage >= 4.0f && maxContributionSleepingSurfelIndex != -1) 355 | { 356 | Surfel toDestroySleepingSurfel = gSurfelBuffer[maxContributionSleepingSurfelIndex]; 357 | toDestroySleepingSurfel.radius = 0; 358 | gSurfelBuffer[maxContributionSleepingSurfelIndex] = toDestroySleepingSurfel; 359 | } 360 | 361 | // Surfel radiance is valid, so use it. 362 | Lr.xyz /= Lr.w; 363 | scatterPayload.radiance += scatterPayload.thp * Lr.xyz; 364 | 365 | #else // USE_SURFEL_RADIANCE 366 | #endif // USE_SURFEL_RADIANCE 367 | 368 | return true; 369 | } 370 | 371 | void handleHit(TriangleHit triangleHit, inout ScatterPayload scatterPayload) 372 | { 373 | VertexData v = gScene.getVertexData(triangleHit); 374 | uint materialID = gScene.getMaterialID(triangleHit.instanceID); 375 | let lod = ExplicitLodTextureSampler(0.f); 376 | ShadingData sd = gScene.materials.prepareShadingData(v, materialID, -scatterPayload.direction, lod); 377 | let mi = gScene.materials.getMaterialInstance(sd, lod); 378 | 379 | if (scatterPayload.currStep == 1u) 380 | scatterPayload.firstRayLength = distance(scatterPayload.origin, v.posW); 381 | 382 | scatterPayload.radiance += scatterPayload.thp * mi.getProperties(sd).emission; 383 | 384 | // Only use diffuse lobe. 385 | sd.mtl.setActiveLobes((uint)LobeType::Diffuse); 386 | 387 | // Calculate exitant radiance using light sources. 388 | // It do f(wi, wo) * dot(wo, n) * Li 389 | float3 Lr = evalAnalyticLight(sd, mi, scatterPayload.sg); 390 | scatterPayload.radiance += scatterPayload.thp * Lr; 391 | 392 | // Prepare next ray. 393 | // If failed to sample, terminate path. 394 | BSDFSample sample; 395 | if (mi.sample(sd, scatterPayload.sg, sample, true)) 396 | { 397 | scatterPayload.origin = sd.computeRayOrigin(); 398 | scatterPayload.direction = sample.wo; 399 | scatterPayload.thp *= sample.weight; 400 | } 401 | else 402 | { 403 | scatterPayload.status |= 0x0002; 404 | return; 405 | } 406 | 407 | // Finialize path if path length reached to last step. 408 | // Sleeping surfels have double ray step, because sleeping surfel focus on exploration. 409 | const uint rayStep = (scatterPayload.status & 0x0001) ? gRayStep * 2u : gRayStep; 410 | if (scatterPayload.currStep < rayStep) 411 | { 412 | // Russian roulette. 413 | const float rrValue = luminance(scatterPayload.thp); 414 | const float prob = max(0.f, 1.f - rrValue); 415 | 416 | if (sampleNext1D(scatterPayload.sg) >= prob) 417 | { 418 | scatterPayload.thp /= max(1e-12f, 1.f - prob); 419 | 420 | // Check throughput is valid. 421 | // If valid, goto next step, or terminate path. 422 | bool thpValid = any(scatterPayload.thp > 0.f); 423 | if (thpValid) 424 | scatterPayload.currStep++; 425 | else 426 | scatterPayload.status |= 0x0002; 427 | 428 | return; 429 | } 430 | } 431 | 432 | // If path length reached to last step, 433 | // or russian roulette failed, try to finalize path. 434 | // If failed to finalize, goto next step. 435 | if (finalize(scatterPayload, v, triangleHit)) 436 | scatterPayload.status |= 0x0002; 437 | else 438 | scatterPayload.currStep++; 439 | } 440 | 441 | // Miss Shaders 442 | 443 | [shader("miss")] 444 | void scatterMiss(inout ScatterPayload scatterPayload) 445 | { 446 | // Evaluate environment map. 447 | float3 Le = gScene.envMap.eval(-scatterPayload.direction); 448 | scatterPayload.radiance += scatterPayload.thp * Le; 449 | scatterPayload.status |= 0x0002; 450 | } 451 | 452 | [shader("miss")] 453 | void shadowMiss(inout ShadowPayload shadowPayload) 454 | { 455 | // If shadow ray miss, it means light is visible. 456 | shadowPayload.visible = true; 457 | } 458 | 459 | // Hit Shaders 460 | 461 | [shader("anyhit")] 462 | void scatterAnyHit(inout ScatterPayload scatterPayload, BuiltInTriangleIntersectionAttributes attribs) 463 | { 464 | // Alpha test. 465 | GeometryInstanceID instanceID = getGeometryInstanceID(); 466 | VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); 467 | const uint materialID = gScene.getMaterialID(instanceID); 468 | if (gScene.materials.alphaTest(v, materialID, 0.f)) 469 | IgnoreHit(); 470 | } 471 | 472 | [shader("closesthit")] 473 | void scatterCloseHit(inout ScatterPayload scatterPayload, BuiltInTriangleIntersectionAttributes attribs) 474 | { 475 | TriangleHit triangleHit; 476 | triangleHit.instanceID = getGeometryInstanceID(); 477 | triangleHit.primitiveIndex = PrimitiveIndex(); 478 | triangleHit.barycentrics = attribs.barycentrics; 479 | 480 | handleHit(triangleHit, scatterPayload); 481 | } 482 | 483 | [shader("anyhit")] 484 | void shadowAnyhit(inout ShadowPayload shadowPayload, BuiltInTriangleIntersectionAttributes attribs) 485 | { 486 | // Alpha test. 487 | GeometryInstanceID instanceID = getGeometryInstanceID(); 488 | VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); 489 | const uint materialID = gScene.getMaterialID(instanceID); 490 | if (gScene.materials.alphaTest(v, materialID, 0.f)) 491 | IgnoreHit(); 492 | } 493 | 494 | // Ray Generation 495 | 496 | [shader("raygeneration")] 497 | void rayGen() 498 | { 499 | uint totalRayCount = gSurfelCounter.Load((int)SurfelCounterOffset::RequestedRay); 500 | if (DispatchRaysIndex().x >= totalRayCount) 501 | return; 502 | 503 | uint rayIndex = DispatchRaysIndex().x; 504 | SurfelRayResult surfelRayResult = gSurfelRayResultBuffer[rayIndex]; 505 | const uint surfelIndex = surfelRayResult.surfelIndex; 506 | const Surfel surfel = gSurfelBuffer[surfelIndex]; 507 | const SurfelRecycleInfo surfelRecycleInfo = gSurfelRecycleInfoBuffer[surfelIndex]; 508 | const bool isSleeping = surfelRecycleInfo.status & 0x0001; 509 | 510 | RNG rng; 511 | rng.init(uint2(rayIndex, rayIndex), gFrameIndex); 512 | 513 | SampleGenerator sg = SampleGenerator(uint2(rayIndex, rayIndex) + uint2(gFrameIndex), gFrameIndex); 514 | 515 | // Initialize scatter payload. 516 | ScatterPayload scatterPayload = ScatterPayload(sg, isSleeping); 517 | scatterPayload.origin = surfel.position; 518 | 519 | float3 dirLocal; 520 | float pdf; 521 | 522 | uint2 irrMapBase = uint2( 523 | surfelIndex % (kIrradianceMapRes.x / kIrradianceMapUnit.x), 524 | surfelIndex / (kIrradianceMapRes.x / kIrradianceMapUnit.y) 525 | ); 526 | irrMapBase *= kIrradianceMapUnit; 527 | 528 | #ifdef USE_RAY_GUIDING 529 | 530 | // If hole dosen't exist, and ray count of surfel is bigger than 16, apply ray-guiding. 531 | if (!surfel.hasHole && step(16u, surfel.rayCount)) 532 | { 533 | // Approximate inverse CDF for sampling. 534 | float randomCeil = rng.next_float() * surfel.sumLuminance; 535 | float cummulative = 0.f; 536 | 537 | int2 coord; 538 | for (uint y = 0; y < kIrradianceMapUnit.y; ++y) 539 | { 540 | for (uint x = 0; x < kIrradianceMapUnit.x; ++x) 541 | { 542 | cummulative += gIrradianceMap[irrMapBase + uint2(x, y)]; 543 | if (cummulative >= randomCeil) 544 | { 545 | coord = int2(x, y); 546 | pdf = gIrradianceMap[irrMapBase + uint2(x, y)] / surfel.sumLuminance; 547 | break; 548 | } 549 | } 550 | 551 | if (cummulative >= randomCeil) 552 | break; 553 | } 554 | 555 | float2 uv = float2(coord - kIrradianceMapHalfUnit) / float2(kIrradianceMapHalfUnit); 556 | dirLocal = octDecode(uv); 557 | 558 | // Add small noise to direction. 559 | float3 noise = normalize(hemispherepoint_uniform(rng.next_float(), rng.next_float())); 560 | dirLocal = normalize(dirLocal + 0.1f * noise); 561 | } 562 | else 563 | { 564 | dirLocal = normalize(hemispherepoint_uniform(rng.next_float(), rng.next_float())); 565 | pdf = 1.f / (16 * M_PI); 566 | } 567 | 568 | #else // USE_RAY_GUIDING 569 | 570 | dirLocal = normalize(hemispherepoint_uniform(rng.next_float(), rng.next_float())); 571 | pdf = 1.f / (16 * M_PI); 572 | 573 | #endif // USE_RAY_GUIDING 574 | 575 | float3 dirWorld = normalize(mul(dirLocal, get_tangentspace(surfel.normal))); 576 | 577 | scatterPayload.direction = dirWorld; 578 | 579 | // Store direction and pdf to result first, because payload direction will be changed. 580 | surfelRayResult.dirLocal = dirLocal; 581 | surfelRayResult.dirWorld = dirWorld; 582 | surfelRayResult.pdf = pdf; 583 | 584 | // Trace ray. 585 | // Sleeping surfels have double max step, because sleeping surfel focus on exploration. 586 | const uint maxStep = isSleeping ? gMaxStep * 2u : gMaxStep; 587 | while (!(scatterPayload.status & 0x0002) && scatterPayload.currStep <= maxStep) 588 | traceScatterRay(scatterPayload); 589 | 590 | // Store first ray length for estimating surfel depth function. 591 | surfelRayResult.firstRayLength = scatterPayload.firstRayLength; 592 | 593 | // Store final radiance to result. 594 | surfelRayResult.radiance = scatterPayload.radiance; 595 | 596 | // Write back to buffer. 597 | gSurfelRayResultBuffer[rayIndex] = surfelRayResult; 598 | } 599 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelTypes.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Utils/HostDeviceShared.slangh" 3 | #include "MultiscaleMeanEstimator.slang" 4 | 5 | BEGIN_NAMESPACE_FALCOR 6 | 7 | enum class SurfelCounterOffset : int 8 | { 9 | ValidSurfel = 0, 10 | DirtySurfel = 4, 11 | FreeSurfel = 8, 12 | Cell = 12, 13 | RequestedRay = 16, 14 | MissBounce = 20 15 | }; 16 | 17 | static const uint2 kTileSize = uint2(16, 16); 18 | static const uint kTotalSurfelLimit = 150000; 19 | static const uint kRayBudget = 150000 * 64; 20 | static const uint kRefCountThreshold = 32u; 21 | static const uint kMaxLife = 240u; 22 | static const uint kSleepingMaxLife = kMaxLife / 4; 23 | static const uint kInitialStatus[] = { 0, 0, kTotalSurfelLimit, 0, 0, 0 }; 24 | 25 | static const uint2 kIrradianceMapRes = uint2(3840, 2160); 26 | static const uint2 kIrradianceMapUnit = uint2(7, 7); 27 | static const uint2 kIrradianceMapHalfUnit = kIrradianceMapUnit / 2u; 28 | 29 | static const uint2 kSurfelDepthTextureRes = uint2(3840, 2160); 30 | static const uint2 kSurfelDepthTextureUnit = uint2(7, 7); 31 | static const uint2 kSurfelDepthTextureHalfUnit = kSurfelDepthTextureUnit / 2u; 32 | 33 | struct Surfel 34 | { 35 | float3 position; 36 | float3 normal; 37 | float radius; 38 | float3 radiance; 39 | float sumLuminance; 40 | bool hasHole; 41 | MSMEData msmeData; 42 | uint rayOffset; 43 | uint rayCount; 44 | 45 | #ifdef HOST_CODE 46 | #else 47 | __init(float3 position, float3 normal, float radius) 48 | { 49 | this.position = position; 50 | this.normal = normal; 51 | this.radius = radius; 52 | this.radiance = float3(0.f); 53 | this.sumLuminance = 0.f; 54 | this.hasHole = false; 55 | this.msmeData = MSMEData(); 56 | this.rayOffset = 0; 57 | this.rayCount = 0; 58 | } 59 | #endif 60 | }; 61 | 62 | struct CellInfo 63 | { 64 | uint surfelCount; 65 | uint cellToSurfelBufferOffset; 66 | }; 67 | 68 | struct SurfelRayResult 69 | { 70 | float3 dirLocal; 71 | float3 dirWorld; 72 | float pdf; 73 | float firstRayLength; 74 | float3 radiance; 75 | uint surfelIndex; 76 | }; 77 | 78 | // [status] 79 | // 0x0001 : isSleeping 80 | // 0x0002 : lastSeen 81 | struct SurfelRecycleInfo 82 | { 83 | uint16_t life; 84 | uint16_t frame; 85 | uint16_t status; 86 | }; 87 | 88 | END_NAMESPACE_FALCOR 89 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelUpdatePass.cs.slang: -------------------------------------------------------------------------------- 1 | import Scene.Scene; 2 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 3 | import RenderPasses.Surfel.SurfelGI.SurfelUtils; 4 | import RenderPasses.Surfel.SurfelGI.StaticParams; 5 | 6 | cbuffer CB 7 | { 8 | float3 gCameraPos; 9 | uint2 gResolution; 10 | float gFOVy; 11 | bool gLockSurfel; 12 | float gVarianceSensitivity; 13 | uint gMinRayCount; 14 | uint gMaxRayCount; 15 | } 16 | 17 | RWStructuredBuffer gSurfelBuffer; 18 | RWStructuredBuffer gSurfelGeometryBuffer; 19 | StructuredBuffer gSurfelDirtyIndexBuffer; 20 | RWStructuredBuffer gSurfelValidIndexBuffer; 21 | RWStructuredBuffer gSurfelFreeIndexBuffer; 22 | RWStructuredBuffer gCellInfoBuffer; 23 | RWStructuredBuffer gCellToSurfelBuffer; 24 | RWStructuredBuffer gSurfelRayResultBuffer; 25 | RWStructuredBuffer gSurfelRecycleInfoBuffer; 26 | 27 | RWByteAddressBuffer gSurfelReservationBuffer; 28 | RWByteAddressBuffer gSurfelRefCounter; 29 | RWByteAddressBuffer gSurfelCounter; 30 | 31 | // Calculate how much surfels are located at cell. 32 | [numthreads(32, 1, 1)] 33 | void collectCellInfo(uint3 dispatchThreadId: SV_DispatchThreadID) 34 | { 35 | uint dirtySurfelCount = gSurfelCounter.Load((int)SurfelCounterOffset::DirtySurfel); 36 | if (dispatchThreadId.x >= dirtySurfelCount) 37 | return; 38 | 39 | uint surfelIndex = gSurfelDirtyIndexBuffer[dispatchThreadId.x]; 40 | Surfel surfel = gSurfelBuffer[surfelIndex]; 41 | 42 | float surfelRadius = surfel.radius; 43 | SurfelRecycleInfo surfelRecycleInfo = gSurfelRecycleInfoBuffer[surfelIndex]; 44 | bool isSleeping = surfelRecycleInfo.status & 0x0001; 45 | bool lastSeen = surfelRecycleInfo.status & 0x0002; 46 | 47 | // Increase frame count and reduce life. 48 | surfelRecycleInfo.life = max(asint(surfelRecycleInfo.life) - 1, 0); 49 | surfelRecycleInfo.frame = clamp(asint(surfelRecycleInfo.frame) + 1, 0, 65535); 50 | 51 | // Check surfel is seen at last frame. 52 | // If so, reset life. 53 | // If so, and surfel is sleeping, awake. 54 | if (lastSeen) 55 | { 56 | surfelRecycleInfo.life = kMaxLife; 57 | if (isSleeping) 58 | isSleeping = false; 59 | } 60 | 61 | const bool hasEnoughRefCount = gSurfelRefCounter.Load(surfelIndex) > kRefCountThreshold; 62 | 63 | // Check sleeping surfel is still vaild or not. 64 | // If valid, reset life. 65 | if (isSleeping && hasEnoughRefCount) 66 | surfelRecycleInfo.life = kSleepingMaxLife; 67 | 68 | // Radius of surfel <= 0 means that the surfel should be destroyed. 69 | // Filter invalid surfels by checking radius and life. 70 | if (surfelRadius > 0 && surfelRecycleInfo.life > 0) 71 | { 72 | // Copy surfel index. 73 | uint validSurfelCount; 74 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::ValidSurfel, 1, validSurfelCount); 75 | gSurfelValidIndexBuffer[validSurfelCount] = surfelIndex; 76 | 77 | // Get vertex data using geometry info. 78 | TriangleHit hit = TriangleHit(gSurfelGeometryBuffer[surfelIndex]); 79 | VertexData data = gScene.getVertexData(hit); 80 | 81 | if (!gLockSurfel) 82 | { 83 | // Update surfel position, normal. 84 | surfel.position = data.posW; 85 | surfel.normal = data.normalW; 86 | 87 | // If surfel is sleeping, increase target area. 88 | surfel.radius = calcSurfelRadius( 89 | distance(gScene.camera.getPosition(), surfel.position), 90 | gFOVy, 91 | gResolution, 92 | kSurfelTargetArea * (isSleeping ? 16.f : 1.f), 93 | kCellUnit 94 | ); 95 | 96 | // Limit lower bound of surfel radius when sleeping. 97 | if (isSleeping) 98 | surfel.radius = max(surfel.radius, kCellUnit * 0.5f); 99 | } 100 | 101 | // Calculate number of surfels located at cell. 102 | int3 cellPos = getCellPos(surfel.position, gCameraPos, kCellUnit); 103 | for (uint i = 0; i < 125; ++i) 104 | { 105 | int3 neighborPos = cellPos + neighborOffset[i]; 106 | if (isSurfelIntersectCell(surfel, neighborPos, gCameraPos, kCellUnit)) 107 | { 108 | uint flattenIndex = getFlattenCellIndex(neighborPos); 109 | InterlockedAdd(gCellInfoBuffer[flattenIndex].surfelCount, 1); 110 | } 111 | } 112 | 113 | if (!gLockSurfel) 114 | { 115 | // Ray allocation by using MSME variance. 116 | // If surfel is sleeping surfel, reduce ray count. 117 | uint lower = isSleeping ? (gMinRayCount / 4u) : (gMaxRayCount / 4u); 118 | uint upper = isSleeping ? gMinRayCount : gMaxRayCount; 119 | 120 | uint rayOffset = 0; 121 | uint rayRequestCount = clamp(lerp(lower, upper, length(surfel.msmeData.variance) * gVarianceSensitivity), lower, upper); 122 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::RequestedRay, rayRequestCount, rayOffset); 123 | 124 | if (rayOffset < kRayBudget) 125 | { 126 | surfel.rayOffset = rayOffset; 127 | surfel.rayCount = rayRequestCount; 128 | 129 | SurfelRayResult initSurfelRayResult; 130 | initSurfelRayResult.surfelIndex = surfelIndex; 131 | 132 | for (uint rayIndex = 0; rayIndex < rayRequestCount; ++rayIndex) 133 | gSurfelRayResultBuffer[rayOffset + rayIndex] = initSurfelRayResult; 134 | } 135 | 136 | // Set status value. Last seen value is always reset. 137 | surfelRecycleInfo.status = isSleeping ? 0x0001 : 0x0000; 138 | 139 | // Write back to buffer. 140 | gSurfelRecycleInfoBuffer[surfelIndex] = surfelRecycleInfo; 141 | gSurfelBuffer[surfelIndex] = surfel; 142 | 143 | // Reset surfel ref count. 144 | gSurfelRefCounter.Store(surfelIndex, 0); 145 | } 146 | } 147 | else 148 | { 149 | if (!gLockSurfel) 150 | { 151 | // De-allocate surfel. 152 | int freeSurfelCount; 153 | gSurfelCounter.InterlockedAdd((int)SurfelCounterOffset::FreeSurfel, 1, freeSurfelCount); 154 | gSurfelFreeIndexBuffer[freeSurfelCount] = surfelIndex; 155 | } 156 | } 157 | } 158 | 159 | // Calculate offset of cell to surfel buffer. 160 | [numthreads(64, 1, 1)] 161 | void accumulateCellInfo(uint3 dispatchThreadId: SV_DispatchThreadID) 162 | { 163 | if (dispatchThreadId.x >= kCellCount) 164 | return; 165 | 166 | uint flattenIndex = dispatchThreadId.x; 167 | gSurfelReservationBuffer.Store(flattenIndex, 0u); 168 | 169 | if (gCellInfoBuffer[flattenIndex].surfelCount == 0) 170 | return; 171 | 172 | // Calculate offsets. 173 | gSurfelCounter.InterlockedAdd( 174 | (int)SurfelCounterOffset::Cell, 175 | gCellInfoBuffer[flattenIndex].surfelCount, 176 | gCellInfoBuffer[flattenIndex].cellToSurfelBufferOffset 177 | ); 178 | 179 | gCellInfoBuffer[flattenIndex].surfelCount = 0; 180 | } 181 | 182 | // Update cell to surfel buffer using pre-calculated offsets. 183 | // This operation is duplicated, might be possible to merge. 184 | [numthreads(32, 1, 1)] 185 | void updateCellToSurfelBuffer(uint3 dispatchThreadId: SV_DispatchThreadID) 186 | { 187 | uint vaildSurfelCount = gSurfelCounter.Load((int)SurfelCounterOffset::ValidSurfel); 188 | if (dispatchThreadId.x >= vaildSurfelCount) 189 | return; 190 | 191 | uint surfelIndex = gSurfelValidIndexBuffer[dispatchThreadId.x]; 192 | Surfel surfel = gSurfelBuffer[surfelIndex]; 193 | 194 | // Check surfel is intersected with neighbor cells. 195 | int3 cellPos = getCellPos(surfel.position, gCameraPos, kCellUnit); 196 | for (uint i = 0; i < 125; ++i) 197 | { 198 | int3 neighborPos = cellPos + neighborOffset[i]; 199 | if (isSurfelIntersectCell(surfel, neighborPos, gCameraPos, kCellUnit)) 200 | { 201 | uint flattenIndex = getFlattenCellIndex(neighborPos); 202 | 203 | uint prevCount; 204 | InterlockedAdd(gCellInfoBuffer[flattenIndex].surfelCount, 1, prevCount); 205 | 206 | gCellToSurfelBuffer[gCellInfoBuffer[flattenIndex].cellToSurfelBufferOffset + prevCount] = surfelIndex; 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGI/SurfelUtils.slang: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Utils/Math/MathConstants.slangh" 3 | 4 | import RenderPasses.Surfel.Random; 5 | import RenderPasses.Surfel.SurfelGI.SurfelTypes; 6 | import RenderPasses.Surfel.SurfelGI.StaticParams; 7 | 8 | static const int3 neighborOffset[125] = 9 | { 10 | int3(-2, -2, -2) , 11 | int3(-2, -2, -1) , 12 | int3(-2, -2, 0) , 13 | int3(-2, -2, 1) , 14 | int3(-2, -2, 2) , 15 | int3(-2, -1, -2) , 16 | int3(-2, -1, -1) , 17 | int3(-2, -1, 0) , 18 | int3(-2, -1, 1) , 19 | int3(-2, -1, 2) , 20 | int3(-2, 0, -2) , 21 | int3(-2, 0, -1) , 22 | int3(-2, 0, 0) , 23 | int3(-2, 0, 1) , 24 | int3(-2, 0, 2) , 25 | int3(-2, 1, -2) , 26 | int3(-2, 1, -1) , 27 | int3(-2, 1, 0) , 28 | int3(-2, 1, 1) , 29 | int3(-2, 1, 2) , 30 | int3(-2, 2, -2) , 31 | int3(-2, 2, -1) , 32 | int3(-2, 2, 0) , 33 | int3(-2, 2, 1) , 34 | int3(-2, 2, 2) , 35 | int3(-1, -2, -2) , 36 | int3(-1, -2, -1) , 37 | int3(-1, -2, 0) , 38 | int3(-1, -2, 1) , 39 | int3(-1, -2, 2) , 40 | int3(-1, -1, -2) , 41 | int3(-1, -1, -1) , 42 | int3(-1, -1, 0) , 43 | int3(-1, -1, 1) , 44 | int3(-1, -1, 2) , 45 | int3(-1, 0, -2) , 46 | int3(-1, 0, -1) , 47 | int3(-1, 0, 0) , 48 | int3(-1, 0, 1) , 49 | int3(-1, 0, 2) , 50 | int3(-1, 1, -2) , 51 | int3(-1, 1, -1) , 52 | int3(-1, 1, 0) , 53 | int3(-1, 1, 1) , 54 | int3(-1, 1, 2) , 55 | int3(-1, 2, -2) , 56 | int3(-1, 2, -1) , 57 | int3(-1, 2, 0) , 58 | int3(-1, 2, 1) , 59 | int3(-1, 2, 2) , 60 | int3(0, -2, -2) , 61 | int3(0, -2, -1) , 62 | int3(0, -2, 0) , 63 | int3(0, -2, 1) , 64 | int3(0, -2, 2) , 65 | int3(0, -1, -2) , 66 | int3(0, -1, -1) , 67 | int3(0, -1, 0) , 68 | int3(0, -1, 1) , 69 | int3(0, -1, 2) , 70 | int3(0, 0, -2) , 71 | int3(0, 0, -1) , 72 | int3(0, 0, 0) , 73 | int3(0, 0, 1) , 74 | int3(0, 0, 2) , 75 | int3(0, 1, -2) , 76 | int3(0, 1, -1) , 77 | int3(0, 1, 0) , 78 | int3(0, 1, 1) , 79 | int3(0, 1, 2) , 80 | int3(0, 2, -2) , 81 | int3(0, 2, -1) , 82 | int3(0, 2, 0) , 83 | int3(0, 2, 1) , 84 | int3(0, 2, 2) , 85 | int3(1, -2, -2) , 86 | int3(1, -2, -1) , 87 | int3(1, -2, 0) , 88 | int3(1, -2, 1) , 89 | int3(1, -2, 2) , 90 | int3(1, -1, -2) , 91 | int3(1, -1, -1) , 92 | int3(1, -1, 0) , 93 | int3(1, -1, 1) , 94 | int3(1, -1, 2) , 95 | int3(1, 0, -2) , 96 | int3(1, 0, -1) , 97 | int3(1, 0, 0) , 98 | int3(1, 0, 1) , 99 | int3(1, 0, 2) , 100 | int3(1, 1, -2) , 101 | int3(1, 1, -1) , 102 | int3(1, 1, 0) , 103 | int3(1, 1, 1) , 104 | int3(1, 1, 2) , 105 | int3(1, 2, -2) , 106 | int3(1, 2, -1) , 107 | int3(1, 2, 0) , 108 | int3(1, 2, 1) , 109 | int3(1, 2, 2) , 110 | int3(2, -2, -2) , 111 | int3(2, -2, -1) , 112 | int3(2, -2, 0) , 113 | int3(2, -2, 1) , 114 | int3(2, -2, 2) , 115 | int3(2, -1, -2) , 116 | int3(2, -1, -1) , 117 | int3(2, -1, 0) , 118 | int3(2, -1, 1) , 119 | int3(2, -1, 2) , 120 | int3(2, 0, -2) , 121 | int3(2, 0, -1) , 122 | int3(2, 0, 0) , 123 | int3(2, 0, 1) , 124 | int3(2, 0, 2) , 125 | int3(2, 1, -2) , 126 | int3(2, 1, -1) , 127 | int3(2, 1, 0) , 128 | int3(2, 1, 1) , 129 | int3(2, 1, 2) , 130 | int3(2, 2, -2) , 131 | int3(2, 2, -1) , 132 | int3(2, 2, 0) , 133 | int3(2, 2, 1) , 134 | int3(2, 2, 2) 135 | }; 136 | 137 | float3 unProject(float2 uv, float depth, float4x4 invViewProj) 138 | { 139 | float x = uv.x * 2 - 1; 140 | float y = (1 - uv.y) * 2 - 1; 141 | float4 pos = mul(invViewProj, float4(x, y, depth, 1)); 142 | 143 | return pos.xyz / pos.w; 144 | } 145 | 146 | float calcProjectArea(float radius, float distance, float fovy, uint2 resolution) 147 | { 148 | float projRadius = atan(radius / distance) * max(resolution.x, resolution.y) / fovy; 149 | return M_PI * projRadius * projRadius; 150 | } 151 | 152 | float calcRadiusApprox(float area, float distance, float fovy, uint2 resolution) 153 | { 154 | return distance * tan(sqrt(area / M_PI) * fovy / max(resolution.x, resolution.y)); 155 | } 156 | 157 | float calcRadius(float area, float distance, float fovy, uint2 resolution) 158 | { 159 | float cosTheta = 1 - (area * (1 - cos(fovy / 2)) / M_PI) / (resolution.x * resolution.y); 160 | float sinTheta = sqrt(1 - pow(cosTheta, 2)); 161 | return distance * sinTheta; 162 | } 163 | 164 | float calcSurfelRadius(float distance, float fovy, uint2 resolution, float area, float cellUnit) 165 | { 166 | return min(calcRadiusApprox(area, distance, fovy, resolution), cellUnit * 2); 167 | } 168 | 169 | float3 randomizeColor(float3 color, inout RNG randomState) 170 | { 171 | float3 randomColor = randomState.next_float3(); 172 | float strength = clamp(length(randomColor), 0.1f, 0.9f); 173 | 174 | return 0.99f * (color * strength) + 0.01f * randomColor; 175 | } 176 | 177 | int3 getCellPos(float3 posW, float3 cameraPosW, float cellUnit) 178 | { 179 | float3 posC = posW - cameraPosW; 180 | posC /= cellUnit; 181 | return (int3)round(posC); 182 | } 183 | 184 | uint getFlattenCellIndex(int3 cellPos) 185 | { 186 | uint3 unsignedPos = cellPos + uint3(kCellDimension / 2); 187 | return (unsignedPos.z * kCellDimension * kCellDimension) + (unsignedPos.y * kCellDimension) + unsignedPos.x; 188 | } 189 | 190 | bool isCellValid(int3 cellPos) 191 | { 192 | if (abs(cellPos.x) >= kCellDimension / 2) 193 | return false; 194 | if (abs(cellPos.y) >= kCellDimension / 2) 195 | return false; 196 | if (abs(cellPos.z) >= kCellDimension / 2) 197 | return false; 198 | 199 | return true; 200 | } 201 | 202 | bool isSurfelIntersectCell(Surfel surfel, int3 cellPos, float3 cameraPosW, float cellUnit) 203 | { 204 | if (!isCellValid(cellPos)) 205 | return false; 206 | 207 | float3 minPosW = cellPos * cellUnit - float3(cellUnit, cellUnit, cellUnit) / 2.0f + cameraPosW; 208 | float3 maxPosW = cellPos * cellUnit + float3(cellUnit, cellUnit, cellUnit) / 2.0f + cameraPosW; 209 | float3 closePoint = min(max(surfel.position, minPosW), maxPosW); 210 | 211 | float dist = distance(closePoint, surfel.position); 212 | return dist < surfel.radius; 213 | } 214 | 215 | float3x3 get_tangentspace(in float3 normal) 216 | { 217 | float3 helper = abs(normal.x) > 0.99 ? float3(0, 0, 1) : float3(1, 0, 0); 218 | 219 | float3 tangent = normalize(cross(normal, helper)); 220 | float3 binormal = normalize(cross(normal, tangent)); 221 | return float3x3(tangent, binormal, normal); 222 | } 223 | 224 | float3 hemispherepoint_uniform(float u, float v) { 225 | float phi = v * 2 * M_PI; 226 | float cosTheta = 1 - u; 227 | float sinTheta = sqrt(1 - cosTheta * cosTheta); 228 | return float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); 229 | } 230 | 231 | float3 hemispherepoint_cos(float u, float v) { 232 | float phi = v * 2 * M_PI; 233 | float cosTheta = sqrt(1 - u); 234 | float sinTheta = sqrt(1 - cosTheta * cosTheta); 235 | return float3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta); 236 | } 237 | 238 | float3 sample_hemisphere_uniform(in float3 normal, inout RNG rng) 239 | { 240 | return mul(hemispherepoint_uniform(rng.next_float(), rng.next_float()), get_tangentspace(normal)); 241 | } 242 | 243 | float3 sample_hemisphere_cos(in float3 normal, inout RNG rng) 244 | { 245 | return mul(hemispherepoint_cos(rng.next_float(), rng.next_float()), get_tangentspace(normal)); 246 | } 247 | 248 | // Octahedral Normal Vectors (ONV) Encode & Decode. 249 | // Modified for hemisphere projection. 250 | // https://jcgt.org/published/0003/02/01/ 251 | float2 octEncode(float3 v) 252 | { 253 | float l1norm = abs(v.x) + abs(v.y) + abs(v.z); 254 | float2 result = v.xy * (1.f / l1norm); 255 | return float2(result.x - result.y, result.x + result.y); 256 | } 257 | 258 | float3 octDecode(float2 uv) 259 | { 260 | float2 result = float2((uv.x + uv.y) / 2.f, (uv.y - uv.x) / 2.f); 261 | float3 v = float3(result.x, result.y, 1.f - abs(result.x) - abs(result.y)); 262 | return normalize(v); 263 | } 264 | 265 | float3 lerpColor(float t) 266 | { 267 | float3 color; 268 | float normalizedT; 269 | 270 | if (t <= 0.5f) 271 | { 272 | normalizedT = t / 0.5f; 273 | return float3(0.f, normalizedT, 1.f - normalizedT); 274 | } 275 | 276 | normalizedT = (t - 0.5f) / 0.5f; 277 | return float3(normalizedT, 1.f - normalizedT, 0.f); 278 | } 279 | 280 | float3 stepColor(float t, float r, float g) 281 | { 282 | if (t > r) 283 | return float3(1, 0, 0); 284 | else if (t > g) 285 | return float3(0, 1, 0); 286 | else 287 | return float3(0, 0, 1); 288 | } 289 | 290 | float2 getSurfelDepthUV(uint surfelIndex, float3 dirW, float3 normalW) 291 | { 292 | uint2 ltCoord = uint2( 293 | surfelIndex % (kSurfelDepthTextureRes.x / kSurfelDepthTextureUnit.x), 294 | surfelIndex / (kSurfelDepthTextureRes.x / kSurfelDepthTextureUnit.y) 295 | ); 296 | ltCoord *= kSurfelDepthTextureUnit; 297 | ltCoord += uint2(1, 1); 298 | float2 ltUV = (float2)ltCoord / kSurfelDepthTextureRes; 299 | 300 | float3 dirTangent = mul(get_tangentspace(normalW), dirW); 301 | 302 | float2 localSignedUV = octEncode(normalize(dirTangent)); 303 | float2 localUnsignedUV = (localSignedUV + float2(1.f, 1.f)) / 2.f; 304 | float2 localToGlobal = (float2)(kSurfelDepthTextureUnit - uint2(2, 2)) / kSurfelDepthTextureRes; 305 | float2 uv = ltUV + localUnsignedUV * localToGlobal; 306 | 307 | return uv; 308 | } 309 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGIRenderPass/SurfelGIRenderPass.cpp: -------------------------------------------------------------------------------- 1 | #include "SurfelGIRenderPass.h" 2 | 3 | SurfelGIRenderPass::SurfelGIRenderPass(ref pDevice, const Properties& props) : RenderPass(pDevice) 4 | { 5 | // Check device feature support. 6 | mpDevice = pDevice; 7 | if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_5)) 8 | FALCOR_THROW("SceneDebugger requires Shader Model 6.5 support."); 9 | 10 | mpState = ComputeState::create(mpDevice); 11 | 12 | mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_DEFAULT); 13 | FALCOR_ASSERT(mpSampleGenerator); 14 | 15 | mFrameIndex = 0; 16 | } 17 | 18 | RenderPassReflection SurfelGIRenderPass::reflect(const CompileData& compileData) 19 | { 20 | RenderPassReflection reflector; 21 | 22 | // Input 23 | reflector.addInput("packedHitInfo", "packed hit info texture") 24 | .format(ResourceFormat::RGBA32Uint) 25 | .bindFlags(ResourceBindFlags::ShaderResource); 26 | 27 | reflector.addInput("indirectLighting", "indirect lighting texture") 28 | .format(ResourceFormat::RGBA32Float) 29 | .bindFlags(ResourceBindFlags::ShaderResource); 30 | 31 | // Output 32 | reflector.addOutput("output", "output texture") 33 | .format(ResourceFormat::RGBA32Float) 34 | .bindFlags(ResourceBindFlags::UnorderedAccess); 35 | 36 | return reflector; 37 | } 38 | 39 | void SurfelGIRenderPass::execute(RenderContext* pRenderContext, const RenderData& renderData) 40 | { 41 | const auto& pPackedHitInfo = renderData.getTexture("packedHitInfo"); 42 | const auto& pIndirectLighting = renderData.getTexture("indirectLighting"); 43 | const auto& pOutput = renderData.getTexture("output"); 44 | 45 | FALCOR_ASSERT(pPackedHitInfo && pIndirectLighting && pOutput); 46 | 47 | if (mpProgram) 48 | { 49 | auto var = mpVars->getRootVar(); 50 | mpScene->setRaytracingShaderData(pRenderContext, var); 51 | 52 | var["CB"]["gResolution"] = renderData.getDefaultTextureDims(); 53 | var["CB"]["gFrameIndex"] = mFrameIndex; 54 | var["CB"]["gRenderDirectLighting"] = mRenderDirectLighting; 55 | var["CB"]["gRenderIndirectLighting"] = mRenderIndirectLighting; 56 | 57 | var["gPackedHitInfo"] = pPackedHitInfo; 58 | var["gIndirectLighting"] = pIndirectLighting; 59 | var["gOutput"] = pOutput; 60 | 61 | pRenderContext->clearUAV(pOutput->getUAV().get(), float4(0)); 62 | 63 | uint3 threadGroupSize = mpProgram->getReflector()->getThreadGroupSize(); 64 | uint3 groups = div_round_up(uint3(renderData.getDefaultTextureDims(), 1), threadGroupSize); 65 | pRenderContext->dispatch(mpState.get(), mpVars.get(), groups); 66 | } 67 | 68 | mFrameIndex++; 69 | } 70 | 71 | void SurfelGIRenderPass::renderUI(Gui::Widgets& widget) 72 | { 73 | widget.checkbox("Direct Lighting", mRenderDirectLighting); 74 | widget.checkbox("Indirect Lighting", mRenderIndirectLighting); 75 | } 76 | 77 | void SurfelGIRenderPass::setScene(RenderContext* pRenderContext, const ref& pScene) 78 | { 79 | mpScene = pScene; 80 | mRenderDirectLighting = true; 81 | mRenderIndirectLighting = true; 82 | 83 | if (mpScene) 84 | { 85 | ProgramDesc desc; 86 | desc.addShaderModules(mpScene->getShaderModules()); 87 | desc.addShaderLibrary("RenderPasses/Surfel/SurfelGIRenderPass/SurfelGIRenderPass.cs.slang") 88 | .csEntry("csMain"); 89 | desc.addTypeConformances(mpScene->getTypeConformances()); 90 | 91 | mpProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); 92 | mpProgram->addDefines(mpSampleGenerator->getDefines()); 93 | 94 | mpState->setProgram(mpProgram); 95 | 96 | mpVars = ProgramVars::create(mpDevice, mpProgram.get()); 97 | mpSampleGenerator->bindShaderData(mpVars->getRootVar()); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGIRenderPass/SurfelGIRenderPass.cs.slang: -------------------------------------------------------------------------------- 1 | #include "Utils/Math/MathConstants.slangh" 2 | 3 | import Scene.RaytracingInline; 4 | import Utils.Sampling.SampleGenerator; 5 | import Rendering.Lights.EnvMapSampler; 6 | import Rendering.Lights.LightHelpers; 7 | import Utils.Geometry.GeometryHelpers; 8 | 9 | cbuffer CB 10 | { 11 | uint2 gResolution; 12 | uint gFrameIndex; 13 | bool gRenderDirectLighting; 14 | bool gRenderIndirectLighting; 15 | } 16 | 17 | Texture2D gPackedHitInfo; 18 | Texture2D gIndirectLighting; 19 | RWTexture2D gOutput; 20 | 21 | // [Sample ALL light sources] 22 | float3 evalAnalyticLight(const ShadingData sd, const IMaterialInstance mi, inout SampleGenerator sg) 23 | { 24 | const uint lightCount = gScene.getLightCount(); 25 | if (lightCount == 0) 26 | return float3(0.f); 27 | 28 | float3 Lr = float3(0.f); 29 | for (uint i = 0; i < lightCount; ++i) 30 | { 31 | AnalyticLightSample ls; 32 | if (!sampleLight(sd.posW, gScene.getLight(i), sg, ls)) 33 | continue; 34 | 35 | const uint lobeTypes = mi.getLobeTypes(sd); 36 | const bool hasReflection = lobeTypes & uint(LobeType::Reflection); 37 | const bool hasTransmission = lobeTypes & uint(LobeType::Transmission); 38 | float NdotL = dot(sd.getOrientedFaceNormal(), ls.dir); 39 | if ((NdotL <= kMinCosTheta && !hasTransmission) || (NdotL >= -kMinCosTheta && !hasReflection)) 40 | continue; 41 | 42 | // Trace shadow ray to check light source is visible or not. 43 | HitInfo hit; 44 | { 45 | SceneRayQuery<0> sceneRayQuery; 46 | float hitT; 47 | 48 | Ray ray; 49 | ray.origin = computeRayOrigin(sd.posW, dot(sd.faceN, ls.dir) >= 0.f ? sd.faceN : -sd.faceN); 50 | ray.dir = ls.dir; 51 | ray.tMin = 0.f; 52 | ray.tMax = ls.distance; 53 | 54 | sceneRayQuery.traceRay(ray, hit, hitT, RAY_FLAG_ACCEPT_FIRST_HIT_AND_END_SEARCH, 0xff); 55 | } 56 | 57 | if (!hit.isValid()) 58 | Lr += mi.eval(sd, ls.dir, sg) * ls.Li; 59 | } 60 | 61 | return Lr; 62 | } 63 | 64 | [numthreads(32, 32, 1)] 65 | void csMain(uint3 dispatchThreadId: SV_DispatchThreadID, uint3 groupdId: SV_GroupID) 66 | { 67 | if (dispatchThreadId.x >= gResolution.x || dispatchThreadId.y >= gResolution.y) 68 | return; 69 | 70 | uint2 pixelPos = dispatchThreadId.xy; 71 | HitInfo hitInfo = HitInfo(gPackedHitInfo[pixelPos]); 72 | 73 | // If hit is miss, draw environment map. 74 | if (!hitInfo.isValid()) 75 | { 76 | const float3 ray = gScene.camera.computeRayPinhole(pixelPos, gResolution).dir; 77 | gOutput[pixelPos] = float4(gScene.envMap.eval(ray), 1); 78 | } 79 | else 80 | { 81 | TriangleHit triangleHit = hitInfo.getTriangleHit(); 82 | VertexData v = gScene.getVertexData(triangleHit); 83 | float3 viewDir = normalize(gScene.camera.getPosition() - v.posW); 84 | uint materialID = gScene.getMaterialID(triangleHit.instanceID); 85 | let lod = ExplicitLodTextureSampler(0.f); 86 | ShadingData sd = gScene.materials.prepareShadingData(v, materialID, viewDir, lod); 87 | let mi = gScene.materials.getMaterialInstance(sd, lod); 88 | 89 | SampleGenerator sg = SampleGenerator(pixelPos, gFrameIndex); 90 | 91 | float3 finalGather = float3(0.f); 92 | 93 | const float3 directLighting = evalAnalyticLight(sd, mi, sg); 94 | const float3 indirectLighting = gIndirectLighting[pixelPos].xyz; 95 | const float3 emissiveLighting = mi.getProperties(sd).emission; 96 | 97 | if (gRenderDirectLighting && gRenderIndirectLighting) 98 | { 99 | // So far, specular is not supported. 100 | 101 | // weight = f(wi, wo) * dot(wo, n) / pdf(wo). 102 | // Becuase it is lambertian, 103 | // - BRDF term is albedo / PI, 104 | // - pdf is 1 / PI, 105 | // dot(wo, n) is already calculated when accumulating surfel radiance. 106 | // so, weight is equal to albedo. 107 | 108 | BSDFProperties props = mi.getProperties(sd); 109 | float3 weight = props.diffuseReflectionAlbedo; 110 | finalGather = directLighting + weight * indirectLighting + emissiveLighting; 111 | } 112 | else 113 | { 114 | finalGather = gRenderDirectLighting ? directLighting : gRenderIndirectLighting ? indirectLighting : float3(0.f); 115 | finalGather += emissiveLighting; 116 | } 117 | 118 | gOutput[pixelPos] = float4(finalGather, 1.f); 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelGIRenderPass/SurfelGIRenderPass.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Falcor.h" 3 | #include "RenderGraph/RenderPass.h" 4 | 5 | using namespace Falcor; 6 | 7 | class SurfelGIRenderPass : public RenderPass 8 | { 9 | public: 10 | FALCOR_PLUGIN_CLASS(SurfelGIRenderPass, "SurfelGIRenderPass", "Calculate direct lighting and integrate with input indirect lighting"); 11 | 12 | static ref create(ref pDevice, const Properties& props) 13 | { 14 | return make_ref(pDevice, props); 15 | } 16 | 17 | SurfelGIRenderPass(ref pDevice, const Properties& props); 18 | 19 | virtual Properties getProperties() const override { return {}; } 20 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 21 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override {} 22 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 23 | virtual void renderUI(Gui::Widgets& widget) override; 24 | virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; 25 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 26 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 27 | 28 | private: 29 | ref mpScene; 30 | ref mpState; 31 | ref mpProgram; 32 | ref mpVars; 33 | 34 | ref mpSampleGenerator; 35 | 36 | uint mFrameIndex; 37 | bool mRenderDirectLighting; 38 | bool mRenderIndirectLighting; 39 | }; 40 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelVBuffer/SurfelVBuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "SurfelVBuffer.h" 2 | 3 | SurfelVBuffer::SurfelVBuffer(ref pDevice, const Properties& props) : RenderPass(pDevice) 4 | { 5 | // Check for required features. 6 | if (!mpDevice->isShaderModelSupported(ShaderModel::SM6_2)) 7 | FALCOR_THROW("requires Shader Model 6.2 support."); 8 | if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::Barycentrics)) 9 | FALCOR_THROW("requires pixel shader barycentrics support."); 10 | if (!mpDevice->isFeatureSupported(Device::SupportedFeatures::RasterizerOrderedViews)) 11 | FALCOR_THROW("requires rasterizer ordered views (ROVs) support."); 12 | 13 | mpSampleGenerator = SampleGenerator::create(mpDevice, SAMPLE_GENERATOR_UNIFORM); 14 | } 15 | 16 | RenderPassReflection SurfelVBuffer::reflect(const CompileData& compileData) 17 | { 18 | RenderPassReflection reflector; 19 | 20 | reflector.addOutput("packedHitInfo", "Packed Hit Info") 21 | .format(ResourceFormat::RGBA32Uint) 22 | .bindFlags(ResourceBindFlags::UnorderedAccess); 23 | 24 | reflector.addOutput("depth", "Depth buffer") 25 | .format(ResourceFormat::R32Float) 26 | .bindFlags(ResourceBindFlags::UnorderedAccess); 27 | 28 | if (math::any(mFrameDim != compileData.defaultTexDims)) 29 | mFrameDim = compileData.defaultTexDims; 30 | 31 | return reflector; 32 | } 33 | 34 | void SurfelVBuffer::execute(RenderContext* pRenderContext, const RenderData& renderData) 35 | { 36 | if (!mpScene) 37 | return; 38 | 39 | auto var = mRtPass.pVars->getRootVar(); 40 | 41 | var["CB"]["gResolution"] = mFrameDim; 42 | 43 | var["gPackedHitInfo"] = renderData.getTexture("packedHitInfo"); 44 | var["gDepth"] = renderData.getTexture("depth"); 45 | 46 | mpScene->raytrace(pRenderContext, mRtPass.pProgram.get(), mRtPass.pVars, uint3(mFrameDim, 1)); 47 | } 48 | 49 | void SurfelVBuffer::setScene(RenderContext* pRenderContext, const ref& pScene) 50 | { 51 | mpScene = pScene; 52 | mFrameDim = uint2(0, 0); 53 | 54 | if (mpScene) 55 | { 56 | ProgramDesc desc; 57 | desc.addShaderModules(mpScene->getShaderModules()); 58 | desc.addShaderLibrary("RenderPasses/Surfel/SurfelVBuffer/SurfelVBuffer.rt.slang"); 59 | desc.setMaxPayloadSize(72u); 60 | desc.setMaxAttributeSize(mpScene->getRaytracingMaxAttributeSize()); 61 | desc.setMaxTraceRecursionDepth(2u); 62 | 63 | mRtPass.pBindingTable = RtBindingTable::create(1, 1, mpScene->getGeometryCount()); 64 | mRtPass.pBindingTable->setRayGen(desc.addRayGen("rayGen")); 65 | mRtPass.pBindingTable->setMiss(0, desc.addMiss("miss")); 66 | mRtPass.pBindingTable->setHitGroup( 67 | 0, mpScene->getGeometryIDs(Scene::GeometryType::TriangleMesh), desc.addHitGroup("closeHit", "anyHit") 68 | ); 69 | 70 | mRtPass.pProgram = Program::create(mpDevice, desc, mpScene->getSceneDefines()); 71 | mRtPass.pProgram->addDefines(mpSampleGenerator->getDefines()); 72 | mRtPass.pProgram->setTypeConformances(mpScene->getTypeConformances()); 73 | 74 | mRtPass.pVars = RtProgramVars::create(mpDevice, mRtPass.pProgram, mRtPass.pBindingTable); 75 | mpSampleGenerator->bindShaderData(mRtPass.pVars->getRootVar()); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelVBuffer/SurfelVBuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include "Falcor.h" 3 | #include "RenderGraph/RenderPass.h" 4 | #include "RenderGraph/RenderPassHelpers.h" 5 | 6 | using namespace Falcor; 7 | 8 | class SurfelVBuffer : public RenderPass 9 | { 10 | public: 11 | FALCOR_PLUGIN_CLASS(SurfelVBuffer, "SurfelVBuffer", "Surfel VBuffer"); 12 | 13 | static ref create(ref pDevice, const Properties& props) 14 | { 15 | return make_ref(pDevice, props); 16 | } 17 | 18 | SurfelVBuffer(ref pDevice, const Properties& props); 19 | 20 | virtual Properties getProperties() const override { return {}; } 21 | virtual RenderPassReflection reflect(const CompileData& compileData) override; 22 | virtual void compile(RenderContext* pRenderContext, const CompileData& compileData) override {} 23 | virtual void execute(RenderContext* pRenderContext, const RenderData& renderData) override; 24 | virtual void renderUI(Gui::Widgets& widget) override {} 25 | virtual void setScene(RenderContext* pRenderContext, const ref& pScene) override; 26 | virtual bool onMouseEvent(const MouseEvent& mouseEvent) override { return false; } 27 | virtual bool onKeyEvent(const KeyboardEvent& keyEvent) override { return false; } 28 | 29 | private: 30 | ref mpScene; 31 | ref mpSampleGenerator; 32 | 33 | uint2 mFrameDim; 34 | 35 | struct 36 | { 37 | ref pProgram; 38 | ref pBindingTable; 39 | ref pVars; 40 | } mRtPass; 41 | }; 42 | -------------------------------------------------------------------------------- /RenderPasses/Surfel/SurfelVBuffer/SurfelVBuffer.rt.slang: -------------------------------------------------------------------------------- 1 | #include "Utils/Math/MathConstants.slangh" 2 | 3 | import Scene.Raytracing; 4 | 5 | cbuffer CB 6 | { 7 | uint2 gResolution; 8 | } 9 | 10 | struct Payload 11 | { 12 | float3 origin; 13 | float3 direction; 14 | uint length; 15 | }; 16 | 17 | RWTexture2D gPackedHitInfo; 18 | RWTexture2D gDepth; 19 | 20 | [shader("miss")] 21 | void miss(inout Payload payload) 22 | { 23 | uint2 pixelPos = DispatchRaysIndex().xy; 24 | 25 | gPackedHitInfo[pixelPos] = {}; 26 | gDepth[pixelPos] = 1.f; 27 | } 28 | 29 | [shader("anyhit")] 30 | void anyHit(inout Payload payload, BuiltInTriangleIntersectionAttributes attribs) 31 | { 32 | // Alpha test. 33 | GeometryInstanceID instanceID = getGeometryInstanceID(); 34 | VertexData v = getVertexData(instanceID, PrimitiveIndex(), attribs); 35 | const uint materialID = gScene.getMaterialID(instanceID); 36 | if (gScene.materials.alphaTest(v, materialID, 0.f)) 37 | IgnoreHit(); 38 | } 39 | 40 | [shader("closesthit")] 41 | void closeHit(inout Payload payload, BuiltInTriangleIntersectionAttributes attribs) 42 | { 43 | uint2 pixelPos = DispatchRaysIndex().xy; 44 | 45 | TriangleHit triangleHit; 46 | triangleHit.instanceID = getGeometryInstanceID(); 47 | triangleHit.primitiveIndex = PrimitiveIndex(); 48 | triangleHit.barycentrics = attribs.barycentrics; 49 | 50 | VertexData v = gScene.getVertexData(triangleHit); 51 | 52 | if (payload.length >= 1) 53 | { 54 | float4 curPosH = mul(gScene.camera.data.viewProjMatNoJitter, float4(v.posW, 1.f)); 55 | float depth = curPosH.z / curPosH.w; 56 | 57 | gPackedHitInfo[pixelPos] = triangleHit.pack(); 58 | gDepth[pixelPos] = depth; 59 | } 60 | else 61 | { 62 | const float3 incidentRayDir = normalize(payload.direction); 63 | const float3 normal = normalize(v.normalW); 64 | const float3 reflectionRayDir = incidentRayDir - 2.f * dot(incidentRayDir, normal) * normal; 65 | 66 | payload.origin = v.posW; 67 | payload.direction = normalize(reflectionRayDir); 68 | payload.length++; 69 | 70 | RayDesc rayDesc; 71 | rayDesc.Origin = payload.origin; 72 | rayDesc.TMin = 0.f; 73 | rayDesc.TMax = FLT_MAX; 74 | rayDesc.Direction = payload.direction; 75 | 76 | TraceRay( 77 | gScene.rtAccel, 78 | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES | RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 79 | 0xff, 80 | 0 /* hitIdx */, 81 | rayTypeCount, 82 | 0 /* missIdx */, 83 | rayDesc, 84 | payload 85 | ); 86 | } 87 | } 88 | 89 | [shader("raygeneration")] 90 | void rayGen() 91 | { 92 | uint2 pixelPos = DispatchRaysIndex().xy; 93 | 94 | // Trace ray 95 | const Ray ray = gScene.camera.computeRayPinhole(pixelPos, gResolution); 96 | 97 | Payload payload; 98 | payload.origin = ray.origin; 99 | payload.direction = ray.dir; 100 | payload.length = 0; 101 | 102 | TraceRay( 103 | gScene.rtAccel, 104 | RAY_FLAG_SKIP_PROCEDURAL_PRIMITIVES | RAY_FLAG_CULL_BACK_FACING_TRIANGLES, 105 | 0xff, 106 | 0 /* hitIdx */, 107 | rayTypeCount, 108 | 0 /* missIdx */, 109 | ray.toRayDesc(), 110 | payload 111 | ); 112 | } 113 | -------------------------------------------------------------------------------- /docs/images/teaser.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/W298/SurfelGI/8361942f7d799632b32d37356b8057814456a8a2/docs/images/teaser.png -------------------------------------------------------------------------------- /docs/index.md: -------------------------------------------------------------------------------- 1 | ![teaser](images/teaser.png) 2 | 3 | - Getting Started 4 | - Overview 5 | - How it works? 6 | -------------------------------------------------------------------------------- /scripts/BasicSurfelGI.py: -------------------------------------------------------------------------------- 1 | from pathlib import WindowsPath, PosixPath 2 | from falcor import * 3 | 4 | def render_graph_DefaultRenderGraph(): 5 | g = RenderGraph('DefaultRenderGraph') 6 | g.create_pass('ToneMapper', 'ToneMapper', {'outputSize': 'Default', 'useSceneMetadata': True, 'exposureCompensation': 0.0, 'autoExposure': False, 'filmSpeed': 100.0, 'whiteBalance': False, 'whitePoint': 6500.0, 'operator': 'HableUc2', 'clamp': True, 'whiteMaxLuminance': 1.0, 'whiteScale': 11.199999809265137, 'fNumber': 1.0, 'shutter': 1.0, 'exposureMode': 'AperturePriority'}) 7 | g.create_pass('SurfelGI', 'SurfelGI', {}) 8 | g.create_pass('SurfelGBuffer', 'SurfelGBuffer', {}) 9 | g.create_pass('SurfelGIRenderPass', 'SurfelGIRenderPass', {}) 10 | g.create_pass('SimplePostFX', 'SimplePostFX', {}) 11 | g.add_edge('SurfelGBuffer.packedHitInfo', 'SurfelGI.packedHitInfo') 12 | g.add_edge('SurfelGBuffer.packedHitInfo', 'SurfelGIRenderPass.packedHitInfo') 13 | g.add_edge('SurfelGI.output', 'SurfelGIRenderPass.indirectLighting') 14 | g.add_edge('SurfelGIRenderPass.output', 'SimplePostFX.src') 15 | g.add_edge('SimplePostFX.dst', 'ToneMapper.src') 16 | g.mark_output('ToneMapper.dst') 17 | return g 18 | 19 | DefaultRenderGraph = render_graph_DefaultRenderGraph() 20 | try: m.addGraph(DefaultRenderGraph) 21 | except NameError: None 22 | --------------------------------------------------------------------------------