summaryrefslogtreecommitdiffstats
path: root/PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp
diff options
context:
space:
mode:
authorEgor <egor.yusov@gmail.com>2019-11-27 22:14:55 +0000
committerEgor <egor.yusov@gmail.com>2019-11-27 22:14:55 +0000
commit49374ffc4fb6dcb3287fc930e62ca0840cd031d4 (patch)
treef9d1cfabe2981aca79c2a3e83124db920655383e /PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp
parentAdded reference to code formatting page (diff)
downloadDiligentFX-49374ffc4fb6dcb3287fc930e62ca0840cd031d4.tar.gz
DiligentFX-49374ffc4fb6dcb3287fc930e62ca0840cd031d4.zip
Renamed Postprocess -> PostProcess
Diffstat (limited to 'PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp')
-rw-r--r--PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp2780
1 files changed, 2780 insertions, 0 deletions
diff --git a/PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp b/PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp
new file mode 100644
index 0000000..55a88fb
--- /dev/null
+++ b/PostProcess/EpipolarLightScattering/src/EpipolarLightScattering.cpp
@@ -0,0 +1,2780 @@
+/* Copyright 2019 Diligent Graphics LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS.
+ *
+ * In no event and under no legal theory, whether in tort (including negligence),
+ * contract, or otherwise, unless required by applicable law (such as deliberate
+ * and grossly negligent acts) or agreed to in writing, shall any Contributor be
+ * liable for any damages, including any direct, indirect, special, incidental,
+ * or consequential damages of any character arising as a result of this License or
+ * out of the use or inability to use the software (including but not limited to damages
+ * for loss of goodwill, work stoppage, computer failure or malfunction, or any and
+ * all other commercial damages or losses), even if such Contributor has been advised
+ * of the possibility of such damages.
+ */
+
+#include <algorithm>
+#include <unordered_set>
+#include <array>
+#include <cstring>
+
+#include "EpipolarLightScattering.h"
+#include "ShaderMacroHelper.h"
+#include "GraphicsUtilities.h"
+#include "GraphicsAccessories.h"
+#include "../../../Utilities/include/DiligentFXShaderSourceStreamFactory.h"
+#include "MapHelper.h"
+#include "CommonlyUsedStates.h"
+#include "Align.h"
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+
+namespace Diligent
+{
+
+// clang-format off
+static const DepthStencilStateDesc DSS_CmpEqNoWrites
+{
+ True, // DepthEnable
+ False, // DepthWriteEnable
+ COMPARISON_FUNC_EQUAL // DepthFunc
+};
+
+// Disable depth testing and always increment stencil value.
+// This depth stencil state is used to mark samples which will undergo further processing.
+// Pixel shader discards pixels that should not be further processed, thus keeping the
+// stencil value untouched.
+// For instance, pixel shader performing epipolar coordinates generation discards all
+// sampes, whoose coordinates are outside the screen [-1,1]x[-1,1] area.
+static const DepthStencilStateDesc DSS_IncStencilAlways
+{
+ False, // DepthEnable
+ False, // DepthWriteEnable
+ COMPARISON_FUNC_LESS, // DepthFunc
+ True, // StencilEnable
+ 0xFF, // StencilReadMask
+ 0xFF, // StencilWriteMask
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_INCR_SAT, // StencilPassOp
+ COMPARISON_FUNC_ALWAYS // StencilFunc
+ },
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_INCR_SAT, // StencilPassOp
+ COMPARISON_FUNC_ALWAYS // StencilFunc
+ }
+};
+
+
+// Disable depth testing, stencil testing function equal, increment stencil.
+// This state is used to process only those pixels that were marked at the previous pass.
+// All pixels whith different stencil value are discarded from further processing as well
+// as some pixels can also be discarded during the draw call.
+// For instance, pixel shader marking ray marching samples processes only those pixels which are inside
+// the screen. It also discards all but those samples that are interpolated from themselves.
+static const DepthStencilStateDesc DSS_StencilEqIncStencil
+{
+ False, // DepthEnable
+ False, // DepthWriteEnable
+ COMPARISON_FUNC_LESS, // DepthFunc
+ True, // StencilEnable
+ 0xFF, // StencilReadMask
+ 0xFF, // StencilWriteMask
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_INCR_SAT, // StencilPassOp
+ COMPARISON_FUNC_EQUAL // StencilFunc
+ },
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_INCR_SAT, // StencilPassOp
+ COMPARISON_FUNC_EQUAL // StencilFunc
+ }
+};
+
+
+// Disable depth testing, stencil testing function equal, keep stencil.
+static const DepthStencilStateDesc DSS_StencilEqKeepStencil =
+{
+ False, // DepthEnable
+ False, // DepthWriteEnable
+ COMPARISON_FUNC_LESS, // DepthFunc
+ True, // StencilEnable
+ 0xFF, // StencilReadMask
+ 0xFF, // StencilWriteMask
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_KEEP, // StencilPassOp
+ COMPARISON_FUNC_EQUAL // StencilFunc
+ },
+ StencilOpDesc
+ {
+ STENCIL_OP_KEEP, // StencilFailOp
+ STENCIL_OP_KEEP, // StencilDepthFailOp
+ STENCIL_OP_KEEP, // StencilPassOp
+ COMPARISON_FUNC_EQUAL // StencilFunc
+ }
+};
+
+static const BlendStateDesc BS_AdditiveBlend =
+{
+ False, // AlphaToCoverageEnable
+ False, // IndependentBlendEnable
+ RenderTargetBlendDesc
+ {
+ True, // BlendEnable
+ False, // LogicOperationEnable
+ BLEND_FACTOR_ONE, // SrcBlend
+ BLEND_FACTOR_ONE, // DestBlend
+ BLEND_OPERATION_ADD, // BlendOp
+ BLEND_FACTOR_ONE, // SrcBlendAlpha
+ BLEND_FACTOR_ONE, // DestBlendAlpha
+ BLEND_OPERATION_ADD // BlendOpAlpha
+ }
+};
+// clang-format on
+
+static void RenderFullScreenTriangle(IDeviceContext* pDeviceContext,
+ IPipelineState* PSO,
+ IShaderResourceBinding* SRB,
+ Uint8 StencilRef = 0,
+ Uint32 NumQuads = 1)
+{
+ pDeviceContext->SetPipelineState(PSO);
+ pDeviceContext->CommitShaderResources(SRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ pDeviceContext->SetStencilRef(StencilRef);
+
+ DrawAttribs FullScreenTriangleDrawAttrs;
+ FullScreenTriangleDrawAttrs.NumVertices = 3;
+ FullScreenTriangleDrawAttrs.NumInstances = NumQuads;
+ pDeviceContext->Draw(FullScreenTriangleDrawAttrs);
+}
+
+
+void EpipolarLightScattering::RenderTechnique::InitializeFullScreenTriangleTechnique(
+ IRenderDevice* pDevice,
+ const char* PSOName,
+ IShader* VertexShader,
+ IShader* PixelShader,
+ const PipelineResourceLayoutDesc& ResourceLayout,
+ Uint8 NumRTVs,
+ TEXTURE_FORMAT RTVFmts[],
+ TEXTURE_FORMAT DSVFmt = TEX_FORMAT_UNKNOWN,
+ const DepthStencilStateDesc& DSSDesc = DSS_Default,
+ const BlendStateDesc& BSDesc = BS_Default)
+{
+ PipelineStateDesc PSODesc;
+ PSODesc.Name = PSOName;
+ PSODesc.ResourceLayout = ResourceLayout;
+
+ auto& GraphicsPipeline = PSODesc.GraphicsPipeline;
+
+ GraphicsPipeline.RasterizerDesc.FillMode = FILL_MODE_SOLID;
+ GraphicsPipeline.RasterizerDesc.CullMode = CULL_MODE_NONE;
+ GraphicsPipeline.RasterizerDesc.FrontCounterClockwise = true;
+ GraphicsPipeline.DepthStencilDesc = DSSDesc;
+ GraphicsPipeline.BlendDesc = BSDesc;
+ GraphicsPipeline.pVS = VertexShader;
+ GraphicsPipeline.pPS = PixelShader;
+ GraphicsPipeline.PrimitiveTopology = PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ GraphicsPipeline.NumRenderTargets = NumRTVs;
+ GraphicsPipeline.DSVFormat = DSVFmt;
+ for (Uint32 rt = 0; rt < NumRTVs; ++rt)
+ GraphicsPipeline.RTVFormats[rt] = RTVFmts[rt];
+
+ PSO.Release();
+ SRB.Release();
+ pDevice->CreatePipelineState(PSODesc, &PSO);
+}
+
+void EpipolarLightScattering::RenderTechnique::InitializeFullScreenTriangleTechnique(
+ IRenderDevice* pDevice,
+ const char* PSOName,
+ IShader* VertexShader,
+ IShader* PixelShader,
+ const PipelineResourceLayoutDesc& ResourceLayout,
+ TEXTURE_FORMAT RTVFmt,
+ TEXTURE_FORMAT DSVFmt = TEX_FORMAT_UNKNOWN,
+ const DepthStencilStateDesc& DSSDesc = DSS_DisableDepth,
+ const BlendStateDesc& BSDesc = BS_Default)
+{
+ InitializeFullScreenTriangleTechnique(pDevice, PSOName, VertexShader, PixelShader, ResourceLayout, 1, &RTVFmt, DSVFmt, DSSDesc, BSDesc);
+}
+
+void EpipolarLightScattering::RenderTechnique::InitializeComputeTechnique(IRenderDevice* pDevice,
+ const char* PSOName,
+ IShader* ComputeShader,
+ const PipelineResourceLayoutDesc& ResourceLayout)
+{
+ PipelineStateDesc PSODesc;
+ PSODesc.Name = PSOName;
+ PSODesc.ResourceLayout = ResourceLayout;
+ PSODesc.IsComputePipeline = true;
+ PSODesc.ComputePipeline.pCS = ComputeShader;
+ PSO.Release();
+ SRB.Release();
+ pDevice->CreatePipelineState(PSODesc, &PSO);
+}
+
+void EpipolarLightScattering::RenderTechnique::PrepareSRB(IRenderDevice* pDevice, IResourceMapping* pResMapping, Uint32 Flags = BIND_SHADER_RESOURCES_KEEP_EXISTING | BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED)
+{
+ if (!SRB)
+ {
+ PSO->CreateShaderResourceBinding(&SRB, true);
+ SRB->BindResources(SHADER_TYPE_PIXEL | SHADER_TYPE_COMPUTE, pResMapping, Flags);
+ }
+}
+
+void EpipolarLightScattering::RenderTechnique::Render(IDeviceContext* pDeviceContext, Uint8 StencilRef, Uint32 NumQuads)
+{
+ RenderFullScreenTriangle(pDeviceContext, PSO, SRB, StencilRef, NumQuads);
+}
+
+void EpipolarLightScattering::RenderTechnique::DispatchCompute(IDeviceContext* pDeviceContext, const DispatchComputeAttribs& DispatchAttrs)
+{
+ pDeviceContext->SetPipelineState(PSO);
+ pDeviceContext->CommitShaderResources(SRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ pDeviceContext->DispatchCompute(DispatchAttrs);
+}
+
+void EpipolarLightScattering::RenderTechnique::CheckStaleFlags(Uint32 StalePSODependencies, Uint32 StaleSRBDependencies)
+{
+ if ((PSODependencyFlags & StalePSODependencies) != 0)
+ {
+ PSO.Release();
+ }
+
+ if ((SRBDependencyFlags & StaleSRBDependencies) != 0)
+ {
+ SRB.Release();
+ }
+}
+
+static RefCntAutoPtr<IShader> CreateShader(IRenderDevice* pDevice,
+ const Char* FileName,
+ const Char* EntryPoint,
+ SHADER_TYPE Type,
+ const ShaderMacro* Macros = nullptr)
+{
+ ShaderCreateInfo ShaderCI;
+ ShaderCI.EntryPoint = EntryPoint;
+ ShaderCI.FilePath = FileName;
+ ShaderCI.Macros = Macros;
+ ShaderCI.SourceLanguage = SHADER_SOURCE_LANGUAGE_HLSL;
+ ShaderCI.Desc.ShaderType = Type;
+ ShaderCI.Desc.Name = EntryPoint;
+ ShaderCI.pShaderSourceStreamFactory = &DiligentFXShaderSourceStreamFactory::GetInstance();
+ ShaderCI.UseCombinedTextureSamplers = true;
+ RefCntAutoPtr<IShader> pShader;
+ pDevice->CreateShader(ShaderCI, &pShader);
+ return pShader;
+}
+
+EpipolarLightScattering ::EpipolarLightScattering(IRenderDevice* pDevice,
+ IDeviceContext* pContext,
+ TEXTURE_FORMAT BackBufferFmt,
+ TEXTURE_FORMAT DepthBufferFmt,
+ TEXTURE_FORMAT OffscreenBackBufferFmt,
+ const AirScatteringAttribs& ScatteringAttibs) :
+ m_BackBufferFmt(BackBufferFmt),
+ m_DepthBufferFmt(DepthBufferFmt),
+ m_OffscreenBackBufferFmt(OffscreenBackBufferFmt),
+ m_bUseCombinedMinMaxTexture(false),
+ m_uiSampleRefinementCSThreadGroupSize(0),
+ // Using small group size is inefficient because a lot of SIMD lanes become idle
+ m_uiSampleRefinementCSMinimumThreadGroupSize(128), // Must be greater than 32
+ m_uiNumRandomSamplesOnSphere(pDevice->GetDeviceCaps().DevType == DeviceType::OpenGLES ? 64 : 128),
+ m_MediaParams(ScatteringAttibs),
+ m_uiUpToDateResourceFlags(0)
+{
+ pDevice->CreateResourceMapping(ResourceMappingDesc(), &m_pResMapping);
+
+ // clang-format off
+ CreateUniformBuffer(pDevice, sizeof(EpipolarLightScatteringAttribs), "Epipolar Light Scattering Attribs CB", &m_pcbPostProcessingAttribs);
+ CreateUniformBuffer(pDevice, sizeof(MiscDynamicParams), "Misc Dynamic Params CB", &m_pcbMiscParams);
+ // clang-format on
+
+ {
+ BufferDesc CBDesc;
+ CBDesc.Usage = USAGE_DEFAULT;
+ CBDesc.BindFlags = BIND_UNIFORM_BUFFER;
+ CBDesc.uiSizeInBytes = sizeof(AirScatteringAttribs);
+
+ BufferData InitData{&m_MediaParams, CBDesc.uiSizeInBytes};
+ pDevice->CreateBuffer(CBDesc, &InitData, &m_pcbMediaAttribs);
+ }
+
+ // clang-format off
+ // Add uniform buffers to the shader resource mapping. These buffers will never change.
+ // Note that only buffer objects will stay unchanged, while the buffer contents can be updated.
+ m_pResMapping->AddResource("cbPostProcessingAttribs", m_pcbPostProcessingAttribs, true);
+ m_pResMapping->AddResource("cbParticipatingMediaScatteringParams", m_pcbMediaAttribs, true);
+ m_pResMapping->AddResource("cbMiscDynamicParams", m_pcbMiscParams, true);
+ // clang-format on
+
+ pDevice->CreateSampler(Sam_LinearClamp, &m_pLinearClampSampler);
+ pDevice->CreateSampler(Sam_PointClamp, &m_pPointClampSampler);
+ m_pFullScreenTriangleVS = CreateShader(pDevice, "FullScreenTriangleVS.fx", "FullScreenTriangleVS", SHADER_TYPE_VERTEX);
+
+ ComputeScatteringCoefficients(pContext);
+
+ PrecomputeOpticalDepthTexture(pDevice, pContext);
+
+ CreateAmbientSkyLightTexture(pDevice);
+}
+
+EpipolarLightScattering ::~EpipolarLightScattering()
+{
+}
+
+
+void EpipolarLightScattering ::OnWindowResize(IRenderDevice* pDevice, Uint32 uiBackBufferWidth, Uint32 uiBackBufferHeight)
+{
+ m_uiBackBufferWidth = uiBackBufferWidth;
+ m_uiBackBufferHeight = uiBackBufferHeight;
+ m_ptex2DCamSpaceZRTV.Release();
+}
+
+void EpipolarLightScattering ::DefineMacros(ShaderMacroHelper& Macros)
+{
+ // Define common shader macros
+
+ // clang-format off
+ Macros.AddShaderMacro("OPTIMIZE_SAMPLE_LOCATIONS", m_PostProcessingAttribs.bOptimizeSampleLocations);
+ Macros.AddShaderMacro("USE_COMBINED_MIN_MAX_TEXTURE", m_bUseCombinedMinMaxTexture);
+ Macros.AddShaderMacro("EXTINCTION_EVAL_MODE", m_PostProcessingAttribs.iExtinctionEvalMode);
+ Macros.AddShaderMacro("ENABLE_LIGHT_SHAFTS", m_PostProcessingAttribs.bEnableLightShafts);
+ Macros.AddShaderMacro("MULTIPLE_SCATTERING_MODE", m_PostProcessingAttribs.iMultipleScatteringMode);
+ Macros.AddShaderMacro("SINGLE_SCATTERING_MODE", m_PostProcessingAttribs.iSingleScatteringMode);
+ // clang-format on
+
+ {
+ std::stringstream ss;
+ ss << "float4(" << sm_iPrecomputedSctrUDim << ".0,"
+ << sm_iPrecomputedSctrVDim << ".0,"
+ << sm_iPrecomputedSctrWDim << ".0,"
+ << sm_iPrecomputedSctrQDim << ".0)";
+ Macros.AddShaderMacro("PRECOMPUTED_SCTR_LUT_DIM", ss.str());
+ }
+}
+
+void EpipolarLightScattering::PrecomputeOpticalDepthTexture(IRenderDevice* pDevice,
+ IDeviceContext* pDeviceContext)
+{
+ if (!m_ptex2DOccludedNetDensityToAtmTopSRV)
+ {
+ // Create texture if it has not been created yet.
+ // Do not recreate texture if it already exists as this may
+ // break static resource bindings.
+ TextureDesc TexDesc;
+ TexDesc.Name = "Occluded Net Density to Atm Top";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = sm_iNumPrecomputedHeights;
+ TexDesc.Height = sm_iNumPrecomputedAngles;
+ TexDesc.Format = PrecomputedNetDensityTexFmt;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET;
+ RefCntAutoPtr<ITexture> tex2DOccludedNetDensityToAtmTop;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DOccludedNetDensityToAtmTop);
+ m_ptex2DOccludedNetDensityToAtmTopSRV = tex2DOccludedNetDensityToAtmTop->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DOccludedNetDensityToAtmTopSRV->SetSampler(m_pLinearClampSampler);
+ m_ptex2DOccludedNetDensityToAtmTopRTV = tex2DOccludedNetDensityToAtmTop->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ m_pResMapping->AddResource("g_tex2DOccludedNetDensityToAtmTop", m_ptex2DOccludedNetDensityToAtmTopSRV, false);
+ }
+
+ ITextureView* pRTVs[] = {m_ptex2DOccludedNetDensityToAtmTopRTV};
+ pDeviceContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ auto& PrecomputeNetDensityToAtmTopTech = m_RenderTech[RENDER_TECH_PRECOMPUTE_NET_DENSITY_TO_ATM_TOP];
+ if (!PrecomputeNetDensityToAtmTopTech.PSO)
+ {
+ RefCntAutoPtr<IShader> pPrecomputeNetDensityToAtmTopPS;
+ pPrecomputeNetDensityToAtmTopPS = CreateShader(pDevice, "PrecomputeNetDensityToAtmTop.fx", "PrecomputeNetDensityToAtmTopPS", SHADER_TYPE_PIXEL);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC;
+ PrecomputeNetDensityToAtmTopTech.InitializeFullScreenTriangleTechnique(pDevice, "PrecomputeNetDensityToAtmTopPSO", m_pFullScreenTriangleVS,
+ pPrecomputeNetDensityToAtmTopPS, ResourceLayout, PrecomputedNetDensityTexFmt);
+ PrecomputeNetDensityToAtmTopTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ }
+
+ PrecomputeNetDensityToAtmTopTech.PrepareSRB(pDevice, m_pResMapping);
+ PrecomputeNetDensityToAtmTopTech.Render(pDeviceContext);
+
+ m_uiUpToDateResourceFlags |= UpToDateResourceFlags::PrecomputedOpticalDepthTex;
+}
+
+
+
+void EpipolarLightScattering ::CreateRandomSphereSamplingTexture(IRenderDevice* pDevice)
+{
+ TextureDesc RandomSphereSamplingTexDesc;
+ RandomSphereSamplingTexDesc.Type = RESOURCE_DIM_TEX_2D;
+ RandomSphereSamplingTexDesc.Width = m_uiNumRandomSamplesOnSphere;
+ RandomSphereSamplingTexDesc.Height = 1;
+ RandomSphereSamplingTexDesc.MipLevels = 1;
+ RandomSphereSamplingTexDesc.Format = TEX_FORMAT_RGBA32_FLOAT;
+ RandomSphereSamplingTexDesc.Usage = USAGE_STATIC;
+ RandomSphereSamplingTexDesc.BindFlags = BIND_SHADER_RESOURCE;
+
+ std::vector<float4> SphereSampling(m_uiNumRandomSamplesOnSphere);
+ for (Uint32 iSample = 0; iSample < m_uiNumRandomSamplesOnSphere; ++iSample)
+ {
+ float4& f4Sample = SphereSampling[iSample];
+ f4Sample.z = ((float)rand() / (float)RAND_MAX) * 2.f - 1.f;
+ float t = ((float)rand() / (float)RAND_MAX) * 2.f * PI_F;
+ float r = sqrt(std::max(1.f - f4Sample.z * f4Sample.z, 0.f));
+ f4Sample.x = r * cos(t);
+ f4Sample.y = r * sin(t);
+ f4Sample.w = 0;
+ }
+ TextureSubResData Mip0Data;
+ Mip0Data.pData = SphereSampling.data();
+ Mip0Data.Stride = m_uiNumRandomSamplesOnSphere * sizeof(float4);
+
+ TextureData TexData;
+ TexData.NumSubresources = 1;
+ TexData.pSubResources = &Mip0Data;
+
+ RefCntAutoPtr<ITexture> ptex2DSphereRandomSampling;
+ pDevice->CreateTexture(RandomSphereSamplingTexDesc, &TexData, &ptex2DSphereRandomSampling);
+ m_ptex2DSphereRandomSamplingSRV = ptex2DSphereRandomSampling->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DSphereRandomSamplingSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DSphereRandomSampling", m_ptex2DSphereRandomSamplingSRV, true);
+}
+
+void EpipolarLightScattering ::CreateEpipolarTextures(IRenderDevice* pDevice)
+{
+ TextureDesc TexDesc;
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ TexDesc.Width = m_PostProcessingAttribs.uiMaxSamplesInSlice;
+ TexDesc.Height = m_PostProcessingAttribs.uiNumEpipolarSlices;
+
+ {
+ // MaxSamplesInSlice x NumSlices RG32F texture to store screen-space coordinates
+ // for every epipolar sample
+ TexDesc.Name = "Coordinate Texture";
+ TexDesc.Format = CoordinateTexFmt;
+ TexDesc.ClearValue.Format = TexDesc.Format;
+ TexDesc.ClearValue.Color[0] = -1e+30f;
+ TexDesc.ClearValue.Color[1] = -1e+30f;
+ TexDesc.ClearValue.Color[2] = -1e+30f;
+ TexDesc.ClearValue.Color[3] = -1e+30f;
+
+ RefCntAutoPtr<ITexture> tex2DCoordinateTexture;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DCoordinateTexture);
+ auto* tex2DCoordinateTextureSRV = tex2DCoordinateTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DCoordinateTextureRTV = tex2DCoordinateTexture->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DCoordinateTextureSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DCoordinates", tex2DCoordinateTextureSRV, false);
+ }
+
+ TexDesc.ClearValue.Format = TEX_FORMAT_UNKNOWN;
+
+ {
+ TexDesc.Name = "Interpolation Source";
+ // MaxSamplesInSlice x NumSlices RG16U texture to store two indices from which
+ // the sample should be interpolated, for every epipolar sample
+
+ // In fact we only need RG16U texture to store interpolation source indices.
+ // However, NVidia GLES does not supported imge load/store operations on this format,
+ // so we have to resort to RGBA32U.
+ TexDesc.Format = InterpolationSourceTexFmt;
+
+ TexDesc.BindFlags = BIND_UNORDERED_ACCESS | BIND_SHADER_RESOURCE;
+ RefCntAutoPtr<ITexture> tex2DInterpolationSource;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DInterpolationSource);
+ auto* tex2DInterpolationSourceSRV = tex2DInterpolationSource->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ auto* tex2DInterpolationSourceUAV = tex2DInterpolationSource->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS);
+ tex2DInterpolationSourceSRV->SetSampler(m_pPointClampSampler);
+ m_pResMapping->AddResource("g_tex2DInterpolationSource", tex2DInterpolationSourceSRV, false);
+ m_pResMapping->AddResource("g_rwtex2DInterpolationSource", tex2DInterpolationSourceUAV, false);
+ }
+
+ {
+ // MaxSamplesInSlice x NumSlices R32F texture to store camera-space Z coordinate,
+ // for every epipolar sample
+ TexDesc.Name = "Epipolar Cam Space Z";
+ TexDesc.Format = EpipolarCamSpaceZFmt;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ RefCntAutoPtr<ITexture> tex2DEpipolarCamSpaceZ;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DEpipolarCamSpaceZ);
+ auto* tex2DEpipolarCamSpaceZSRV = tex2DEpipolarCamSpaceZ->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DEpipolarCamSpaceZRTV = tex2DEpipolarCamSpaceZ->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DEpipolarCamSpaceZSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DEpipolarCamSpaceZ", tex2DEpipolarCamSpaceZSRV, false);
+ }
+
+ {
+ // MaxSamplesInSlice x NumSlices RGBA16F texture to store interpolated inscattered light,
+ // for every epipolar sample
+ TexDesc.Name = "Epipolar Inscattering";
+ TexDesc.Format = EpipolarInsctrTexFmt;
+ constexpr float flt16max = 65504.f;
+ TexDesc.ClearValue.Format = TexDesc.Format;
+ TexDesc.ClearValue.Color[0] = -flt16max;
+ TexDesc.ClearValue.Color[1] = -flt16max;
+ TexDesc.ClearValue.Color[2] = -flt16max;
+ TexDesc.ClearValue.Color[3] = -flt16max;
+ RefCntAutoPtr<ITexture> tex2DEpipolarInscattering;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DEpipolarInscattering);
+ auto* tex2DEpipolarInscatteringSRV = tex2DEpipolarInscattering->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DEpipolarInscatteringRTV = tex2DEpipolarInscattering->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DEpipolarInscatteringSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DScatteredColor", tex2DEpipolarInscatteringSRV, false);
+ }
+
+ {
+ // MaxSamplesInSlice x NumSlices RGBA16F texture to store initial inscattered light,
+ // for every epipolar sample
+ TexDesc.Name = "Initial Scattered Light";
+ TexDesc.ClearValue.Format = TexDesc.Format;
+ TexDesc.ClearValue.Color[0] = 0;
+ TexDesc.ClearValue.Color[1] = 0;
+ TexDesc.ClearValue.Color[2] = 0;
+ TexDesc.ClearValue.Color[3] = 0;
+ RefCntAutoPtr<ITexture> tex2DInitialScatteredLight;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DInitialScatteredLight);
+ auto* tex2DInitialScatteredLightSRV = tex2DInitialScatteredLight->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DInitialScatteredLightRTV = tex2DInitialScatteredLight->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DInitialScatteredLightSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DInitialInsctrIrradiance", tex2DInitialScatteredLightSRV, false);
+ }
+
+ TexDesc.ClearValue.Format = TEX_FORMAT_UNKNOWN;
+
+ {
+ // MaxSamplesInSlice x NumSlices depth stencil texture to mark samples for processing,
+ // for every epipolar sample
+ TexDesc.Name = "Epipolar Image Depth";
+ TexDesc.Format = TEX_FORMAT_UNKNOWN;
+ for (auto Fmt : {EpipolarImageDepthFmt0, EpipolarImageDepthFmt1})
+ {
+ const auto& FmtInfo = pDevice->GetTextureFormatInfoExt(Fmt);
+ if (FmtInfo.DepthRenderable)
+ {
+ TexDesc.Format = Fmt;
+ break;
+ }
+ }
+ if (TexDesc.Format == TEX_FORMAT_UNKNOWN)
+ LOG_ERROR_AND_THROW("Failed to find suitable depth-stencil format for epipolar image depth buffer");
+
+ TexDesc.BindFlags = BIND_DEPTH_STENCIL;
+ TexDesc.ClearValue.Format = TexDesc.Format;
+ TexDesc.ClearValue.DepthStencil.Depth = 1;
+ TexDesc.ClearValue.DepthStencil.Stencil = 0;
+ RefCntAutoPtr<ITexture> tex2DEpipolarImageDepth;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DEpipolarImageDepth);
+ m_ptex2DEpipolarImageDSV = tex2DEpipolarImageDepth->GetDefaultView(TEXTURE_VIEW_DEPTH_STENCIL);
+ }
+}
+
+void EpipolarLightScattering ::CreateSliceEndPointsTexture(IRenderDevice* pDevice)
+{
+ // NumSlices x 1 RGBA32F texture to store end point coordinates for every epipolar slice
+ TextureDesc TexDesc;
+ TexDesc.Name = "Slice Endpoints";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ TexDesc.Width = m_PostProcessingAttribs.uiNumEpipolarSlices;
+ TexDesc.Height = 1;
+ TexDesc.Format = SliceEndpointsFmt;
+
+ TexDesc.ClearValue.Format = TexDesc.Format;
+ TexDesc.ClearValue.Color[0] = -1e+30f;
+ TexDesc.ClearValue.Color[1] = -1e+30f;
+ TexDesc.ClearValue.Color[2] = -1e+30f;
+ TexDesc.ClearValue.Color[3] = -1e+30f;
+
+ RefCntAutoPtr<ITexture> tex2DSliceEndpoints;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DSliceEndpoints);
+ auto* tex2DSliceEndpointsSRV = tex2DSliceEndpoints->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DSliceEndpointsRTV = tex2DSliceEndpoints->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DSliceEndpointsSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DSliceEndPoints", tex2DSliceEndpointsSRV, false);
+}
+
+void EpipolarLightScattering ::PrecomputeScatteringLUT(IRenderDevice* pDevice, IDeviceContext* pContext)
+{
+ const int ThreadGroupSize = pDevice->GetDeviceCaps().DevType == DeviceType::OpenGLES ? 8 : 16;
+ auto& PrecomputeSingleSctrTech = m_RenderTech[RENDER_TECH_PRECOMPUTE_SINGLE_SCATTERING];
+ if (!PrecomputeSingleSctrTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.Finalize();
+ auto pPrecomputeSingleSctrCS =
+ CreateShader(pDevice, "PrecomputeSingleScattering.fx", "PrecomputeSingleScatteringCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ PrecomputeSingleSctrTech.InitializeComputeTechnique(pDevice, "PrecomputeSingleScattering", pPrecomputeSingleSctrCS, ResourceLayout);
+ PrecomputeSingleSctrTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ auto& ComputeSctrRadianceTech = m_RenderTech[RENDER_TECH_COMPUTE_SCATTERING_RADIANCE];
+ if (!ComputeSctrRadianceTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.AddShaderMacro("NUM_RANDOM_SPHERE_SAMPLES", static_cast<Int32>(m_uiNumRandomSamplesOnSphere));
+ Macros.Finalize();
+ auto pComputeSctrRadianceCS =
+ CreateShader(pDevice, "ComputeSctrRadiance.fx", "ComputeSctrRadianceCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ ComputeSctrRadianceTech.InitializeComputeTechnique(pDevice, "ComputeSctrRadiance", pComputeSctrRadianceCS, ResourceLayout);
+ ComputeSctrRadianceTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ auto& ComputeScatteringOrderTech = m_RenderTech[RENDER_TECH_COMPUTE_SCATTERING_ORDER];
+ if (!ComputeScatteringOrderTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.Finalize();
+ auto pComputeScatteringOrderCS =
+ CreateShader(pDevice, "ComputeScatteringOrder.fx", "ComputeScatteringOrderCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ ComputeScatteringOrderTech.InitializeComputeTechnique(pDevice, "ComputeScatteringOrder", pComputeScatteringOrderCS, ResourceLayout);
+ ComputeScatteringOrderTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ auto& InitHighOrderScatteringTech = m_RenderTech[RENDER_TECH_INIT_HIGH_ORDER_SCATTERING];
+ if (!InitHighOrderScatteringTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.Finalize();
+ auto pInitHighOrderScatteringCS =
+ CreateShader(pDevice, "InitHighOrderScattering.fx", "InitHighOrderScatteringCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ InitHighOrderScatteringTech.InitializeComputeTechnique(pDevice, "InitHighOrderScattering", pInitHighOrderScatteringCS, ResourceLayout);
+ InitHighOrderScatteringTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ auto& UpdateHighOrderScatteringTech = m_RenderTech[RENDER_TECH_UPDATE_HIGH_ORDER_SCATTERING];
+ if (!UpdateHighOrderScatteringTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.Finalize();
+ auto pUpdateHighOrderScatteringCS =
+ CreateShader(pDevice, "UpdateHighOrderScattering.fx", "UpdateHighOrderScatteringCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ UpdateHighOrderScatteringTech.InitializeComputeTechnique(pDevice, "UpdateHighOrderScattering", pUpdateHighOrderScatteringCS, ResourceLayout);
+ UpdateHighOrderScatteringTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ auto& CombineScatteringOrdersTech = m_RenderTech[RENDER_TECH_COMBINE_SCATTERING_ORDERS];
+ if (!CombineScatteringOrdersTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", ThreadGroupSize);
+ Macros.Finalize();
+ auto pCombineScatteringOrdersCS =
+ CreateShader(pDevice, "CombineScatteringOrders.fx", "CombineScatteringOrdersCS",
+ SHADER_TYPE_COMPUTE, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_DYNAMIC;
+ CombineScatteringOrdersTech.InitializeComputeTechnique(pDevice, "CombineScatteringOrders", pCombineScatteringOrdersCS, ResourceLayout);
+ CombineScatteringOrdersTech.PrepareSRB(pDevice, m_pResMapping, 0);
+ }
+
+ if (!m_ptex2DSphereRandomSamplingSRV)
+ CreateRandomSphereSamplingTexture(pDevice);
+
+ TextureDesc PrecomputedSctrTexDesc;
+ PrecomputedSctrTexDesc.Type = RESOURCE_DIM_TEX_3D;
+ PrecomputedSctrTexDesc.Width = sm_iPrecomputedSctrUDim;
+ PrecomputedSctrTexDesc.Height = sm_iPrecomputedSctrVDim;
+ PrecomputedSctrTexDesc.Depth = sm_iPrecomputedSctrWDim * sm_iPrecomputedSctrQDim;
+ PrecomputedSctrTexDesc.MipLevels = 1;
+ PrecomputedSctrTexDesc.Format = TEX_FORMAT_RGBA16_FLOAT;
+ PrecomputedSctrTexDesc.Usage = USAGE_DEFAULT;
+ PrecomputedSctrTexDesc.BindFlags = BIND_UNORDERED_ACCESS | BIND_SHADER_RESOURCE;
+
+ if (!m_ptex3DSingleScatteringSRV)
+ {
+ RefCntAutoPtr<ITexture> ptex3DSingleSctr, ptex3DMultipleSctr;
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &ptex3DSingleSctr);
+ m_ptex3DSingleScatteringSRV = ptex3DSingleSctr->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex3DSingleScatteringSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_rwtex3DSingleScattering", ptex3DSingleSctr->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS), true);
+
+ // We have to bother with two texture, because HLSL only allows read-write operations on single
+ // component textures
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &m_ptex3DHighOrderSctr);
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &m_ptex3DHighOrderSctr2);
+ m_ptex3DHighOrderSctr->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)->SetSampler(m_pLinearClampSampler);
+ m_ptex3DHighOrderSctr2->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE)->SetSampler(m_pLinearClampSampler);
+
+
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &ptex3DMultipleSctr);
+ m_ptex3DMultipleScatteringSRV = ptex3DMultipleSctr->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex3DMultipleScatteringSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_rwtex3DMultipleSctr", ptex3DMultipleSctr->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS), true);
+
+ m_pResMapping->AddResource("g_tex3DSingleSctrLUT", m_ptex3DSingleScatteringSRV, true);
+
+ m_pResMapping->AddResource("g_tex3DMultipleSctrLUT", m_ptex3DMultipleScatteringSRV, true);
+ }
+
+ // Precompute single scattering
+ PrecomputeSingleSctrTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ DispatchComputeAttribs DispatchAttrs{
+ PrecomputedSctrTexDesc.Width / ThreadGroupSize,
+ PrecomputedSctrTexDesc.Height / ThreadGroupSize,
+ PrecomputedSctrTexDesc.Depth};
+ PrecomputeSingleSctrTech.DispatchCompute(pContext, DispatchAttrs);
+
+ // Precompute multiple scattering
+ // We need higher precision to store intermediate data
+ PrecomputedSctrTexDesc.Format = TEX_FORMAT_RGBA32_FLOAT;
+ RefCntAutoPtr<ITexture> ptex3DSctrRadiance, ptex3DInsctrOrder;
+ RefCntAutoPtr<ITextureView> ptex3DSctrRadianceSRV, ptex3DInsctrOrderSRV;
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &ptex3DSctrRadiance);
+ pDevice->CreateTexture(PrecomputedSctrTexDesc, nullptr, &ptex3DInsctrOrder);
+ ptex3DSctrRadianceSRV = ptex3DSctrRadiance->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ ptex3DInsctrOrderSRV = ptex3DInsctrOrder->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ ptex3DSctrRadianceSRV->SetSampler(m_pLinearClampSampler);
+ ptex3DInsctrOrderSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_rwtex3DSctrRadiance", ptex3DSctrRadiance->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS), true);
+ m_pResMapping->AddResource("g_rwtex3DInsctrOrder", ptex3DInsctrOrder->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS), true);
+
+
+ ComputeSctrRadianceTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, 0);
+ ComputeScatteringOrderTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, 0);
+ InitHighOrderScatteringTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, 0);
+ UpdateHighOrderScatteringTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, 0);
+
+ const int iNumScatteringOrders = pDevice->GetDeviceCaps().DevType == DeviceType::OpenGLES ? 3 : 4;
+ for (int iSctrOrder = 1; iSctrOrder < iNumScatteringOrders; ++iSctrOrder)
+ {
+ // Step 1: compute differential in-scattering
+ ComputeSctrRadianceTech.SRB->GetVariableByName(SHADER_TYPE_COMPUTE, "g_tex3DPreviousSctrOrder")->Set((iSctrOrder == 1) ? m_ptex3DSingleScatteringSRV : ptex3DInsctrOrderSRV);
+ ComputeSctrRadianceTech.DispatchCompute(pContext, DispatchAttrs);
+
+ // It seemse like on Intel GPU, the driver accumulates work into big batch.
+ // The resulting batch turns out to be too big for GPU to process it in allowed time
+ // limit, and the system kills the driver. So we have to flush the command buffer to
+ // force execution of compute shaders.
+ pContext->Flush();
+
+ // Step 2: integrate differential in-scattering
+ ComputeScatteringOrderTech.SRB->GetVariableByName(SHADER_TYPE_COMPUTE, "g_tex3DPointwiseSctrRadiance")->Set(ptex3DSctrRadianceSRV);
+ ComputeScatteringOrderTech.DispatchCompute(pContext, DispatchAttrs);
+
+ RenderTechnique* pRenderTech = nullptr;
+ // Step 3: accumulate high-order scattering scattering
+ if (iSctrOrder == 1)
+ {
+ pRenderTech = &m_RenderTech[RENDER_TECH_INIT_HIGH_ORDER_SCATTERING];
+ }
+ else
+ {
+ pRenderTech = &m_RenderTech[RENDER_TECH_UPDATE_HIGH_ORDER_SCATTERING];
+ std::swap(m_ptex3DHighOrderSctr, m_ptex3DHighOrderSctr2);
+ pRenderTech->SRB->GetVariableByName(SHADER_TYPE_COMPUTE, "g_tex3DHighOrderOrderScattering")->Set(m_ptex3DHighOrderSctr2->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE));
+ }
+ pRenderTech->SRB->GetVariableByName(SHADER_TYPE_COMPUTE, "g_rwtex3DHighOrderSctr")->Set(m_ptex3DHighOrderSctr->GetDefaultView(TEXTURE_VIEW_UNORDERED_ACCESS));
+ pRenderTech->SRB->GetVariableByName(SHADER_TYPE_COMPUTE, "g_tex3DCurrentOrderScattering")->Set(ptex3DInsctrOrderSRV);
+ pRenderTech->DispatchCompute(pContext, DispatchAttrs);
+
+ // Flush the command buffer to force execution of compute shaders and avoid device
+ // reset on low-end Intel GPUs.
+ pContext->Flush();
+ }
+
+ // Note that m_ptex3DHighOrderSctr and m_ptex3DHighOrderSctr2 are ping-ponged during pre-processing
+ m_ptex3DHighOrderScatteringSRV = m_ptex3DHighOrderSctr->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex3DHighOrderScatteringSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex3DHighOrderSctrLUT", m_ptex3DHighOrderScatteringSRV, false);
+
+
+ // Combine single scattering and higher order scattering into single texture
+ CombineScatteringOrdersTech.SRB->BindResources(SHADER_TYPE_COMPUTE, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ CombineScatteringOrdersTech.DispatchCompute(pContext, DispatchAttrs);
+
+ // Remove temporary textures from the resource mapping
+ m_pResMapping->RemoveResourceByName("g_rwtex3DSctrRadiance");
+ m_pResMapping->RemoveResourceByName("g_rwtex3DInsctrOrder");
+
+ m_uiUpToDateResourceFlags |= UpToDateResourceFlags::PrecomputedIntegralsTex;
+}
+
+void EpipolarLightScattering ::CreateLowResLuminanceTexture(IRenderDevice* pDevice, IDeviceContext* pDeviceCtx)
+{
+ // Create low-resolution texture to store image luminance
+ TextureDesc TexDesc;
+ TexDesc.Name = "Low Res Luminance";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = 1 << (sm_iLowResLuminanceMips - 1);
+ TexDesc.Height = 1 << (sm_iLowResLuminanceMips - 1);
+ TexDesc.Format = WeightedLogLumTexFmt,
+ TexDesc.MipLevels = sm_iLowResLuminanceMips;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ TexDesc.MiscFlags = MISC_TEXTURE_FLAG_GENERATE_MIPS;
+
+ RefCntAutoPtr<ITexture> tex2DLowResLuminance;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DLowResLuminance);
+ m_ptex2DLowResLuminanceSRV = tex2DLowResLuminance->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DLowResLuminanceRTV = tex2DLowResLuminance->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ m_ptex2DLowResLuminanceSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DLowResLuminance", m_ptex2DLowResLuminanceSRV, false);
+
+
+ TexDesc.Name = "Average Luminance";
+ TexDesc.Width = 1;
+ TexDesc.Height = 1;
+ TexDesc.MipLevels = 1;
+ TexDesc.MiscFlags = MISC_TEXTURE_FLAG_NONE;
+ TexDesc.Format = AverageLuminanceTexFmt;
+
+ TexDesc.ClearValue.Color[0] = 0.1f;
+ TexDesc.ClearValue.Color[1] = 0.1f;
+ TexDesc.ClearValue.Color[2] = 0.1f;
+ TexDesc.ClearValue.Color[3] = 0.1f;
+
+ RefCntAutoPtr<ITexture> tex2DAverageLuminance;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DAverageLuminance);
+ auto* tex2DAverageLuminanceSRV = tex2DAverageLuminance->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DAverageLuminanceRTV = tex2DAverageLuminance->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DAverageLuminanceSRV->SetSampler(m_pLinearClampSampler);
+ // Set intial luminance to 1
+ ITextureView* pRTVs[] = {m_ptex2DAverageLuminanceRTV};
+ pDeviceCtx->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ pDeviceCtx->ClearRenderTarget(m_ptex2DAverageLuminanceRTV, TexDesc.ClearValue.Color, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ tex2DAverageLuminanceSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DAverageLuminance", tex2DAverageLuminanceSRV, false);
+}
+
+void EpipolarLightScattering ::CreateSliceUVDirAndOriginTexture(IRenderDevice* pDevice)
+{
+ TextureDesc TexDesc;
+ TexDesc.Name = "Slice UV Dir and Origin";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = m_PostProcessingAttribs.uiNumEpipolarSlices;
+ TexDesc.Height = m_PostProcessingAttribs.iNumCascades;
+ TexDesc.Format = SliceUVDirAndOriginTexFmt;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+
+ RefCntAutoPtr<ITexture> tex2DSliceUVDirAndOrigin;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DSliceUVDirAndOrigin);
+ auto* tex2DSliceUVDirAndOriginSRV = tex2DSliceUVDirAndOrigin->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DSliceUVDirAndOriginRTV = tex2DSliceUVDirAndOrigin->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ tex2DSliceUVDirAndOriginSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DSliceUVDirAndOrigin", tex2DSliceUVDirAndOriginSRV, false);
+}
+
+void EpipolarLightScattering ::CreateCamSpaceZTexture(IRenderDevice* pDevice)
+{
+ TextureDesc TexDesc;
+ TexDesc.Name = "Cam-space Z";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = m_uiBackBufferWidth;
+ TexDesc.Height = m_uiBackBufferHeight;
+ TexDesc.Format = CamSpaceZFmt;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+
+ RefCntAutoPtr<ITexture> ptex2DCamSpaceZ;
+ pDevice->CreateTexture(TexDesc, nullptr, &ptex2DCamSpaceZ);
+ m_ptex2DCamSpaceZRTV = ptex2DCamSpaceZ->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ auto* tex2DCamSpaceZSRV = ptex2DCamSpaceZ->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ tex2DCamSpaceZSRV->SetSampler(m_pLinearClampSampler);
+
+ // Add texture to resource mapping
+ m_pResMapping->AddResource("g_tex2DCamSpaceZ", tex2DCamSpaceZSRV, false);
+}
+
+void EpipolarLightScattering ::ReconstructCameraSpaceZ()
+{
+ // Depth buffer is non-linear and cannot be interpolated directly
+ // We have to reconstruct camera space z to be able to use bilinear filtering
+ auto& ReconstrCamSpaceZTech = m_RenderTech[RENDER_TECH_RECONSTRUCT_CAM_SPACE_Z];
+ if (!ReconstrCamSpaceZTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+ auto pReconstrCamSpaceZPS =
+ CreateShader(m_FrameAttribs.pDevice, "ReconstructCameraSpaceZ.fx", "ReconstructCameraSpaceZPS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ ReconstrCamSpaceZTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "ReconstructCameraSpaceZPSO", m_pFullScreenTriangleVS,
+ pReconstrCamSpaceZPS, ResourceLayout, CamSpaceZFmt);
+ // Bind input resources required by the shader
+ ReconstrCamSpaceZTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ ReconstrCamSpaceZTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_SRC_DEPTH_BUFFER;
+ }
+ ReconstrCamSpaceZTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping, BIND_SHADER_RESOURCES_KEEP_EXISTING);
+ ReconstrCamSpaceZTech.SRB->GetVariableByName(SHADER_TYPE_PIXEL, "g_tex2DDepthBuffer")->Set(m_FrameAttribs.ptex2DSrcDepthBufferSRV);
+ ITextureView* ppRTVs[] = {m_ptex2DCamSpaceZRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ ReconstrCamSpaceZTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+void EpipolarLightScattering ::RenderSliceEndpoints()
+{
+ auto& RendedSliceEndpointsTech = m_RenderTech[RENDER_TECH_RENDER_SLICE_END_POINTS];
+ if (!RendedSliceEndpointsTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pRendedSliceEndpointsPS =
+ CreateShader(m_FrameAttribs.pDevice, "RenderSliceEndPoints.fx", "GenerateSliceEndpointsPS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}};
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+ RendedSliceEndpointsTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "RenderSliceEndPoints", m_pFullScreenTriangleVS,
+ pRendedSliceEndpointsPS, ResourceLayout, SliceEndpointsFmt);
+ // Bind static resources required by the shaders
+ RendedSliceEndpointsTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RendedSliceEndpointsTech.PSODependencyFlags = PSO_DEPENDENCY_OPTIMIZE_SAMPLE_LOCATIONS;
+ RendedSliceEndpointsTech.SRBDependencyFlags = 0;
+ }
+
+ RendedSliceEndpointsTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+ ITextureView* ppRTVs[] = {m_ptex2DSliceEndpointsRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ RendedSliceEndpointsTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+void EpipolarLightScattering ::RenderCoordinateTexture()
+{
+ auto& RendedCoordTexTech = m_RenderTech[RENDER_TECH_RENDER_COORD_TEXTURE];
+ if (!RendedCoordTexTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+ auto pRendedCoordTexPS = CreateShader(m_FrameAttribs.pDevice, "RenderCoordinateTexture.fx", "GenerateCoordinateTexturePS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ StaticSamplerDesc StaticSamplers[] =
+ {
+ {SHADER_TYPE_PIXEL, "g_tex2DCamSpaceZ", Sam_LinearClamp}
+ };
+ // clang-format on
+ ResourceLayout.StaticSamplers = StaticSamplers;
+ ResourceLayout.NumStaticSamplers = _countof(StaticSamplers);
+ TEXTURE_FORMAT RTVFmts[] = {CoordinateTexFmt, EpipolarCamSpaceZFmt};
+ auto EpipolarImageDepthFmt = m_ptex2DEpipolarImageDSV->GetTexture()->GetDesc().Format;
+ RendedCoordTexTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "RenderCoordinateTexture", m_pFullScreenTriangleVS,
+ pRendedCoordTexPS, ResourceLayout, 2, RTVFmts, EpipolarImageDepthFmt, DSS_IncStencilAlways);
+ RendedCoordTexTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RendedCoordTexTech.PSODependencyFlags = 0;
+ RendedCoordTexTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_SLICE_END_POINTS_TEX;
+ }
+
+ RendedCoordTexTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+
+ ITextureView* ppRTVs[] = {m_ptex2DCoordinateTextureRTV, m_ptex2DEpipolarCamSpaceZRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(2, ppRTVs, m_ptex2DEpipolarImageDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ // Clear both render targets with values that can't be correct projection space coordinates and camera space Z:
+ float InvalidCoords[] = {-1e+30f, -1e+30f, -1e+30f, -1e+30f};
+ m_FrameAttribs.pDeviceContext->ClearRenderTarget(m_ptex2DCoordinateTextureRTV, InvalidCoords, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ m_FrameAttribs.pDeviceContext->ClearRenderTarget(m_ptex2DEpipolarCamSpaceZRTV, InvalidCoords, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ m_FrameAttribs.pDeviceContext->ClearDepthStencil(m_ptex2DEpipolarImageDSV, CLEAR_DEPTH_FLAG | CLEAR_STENCIL_FLAG, 1.0, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ // Depth stencil state is configured to always increment stencil value. If coordinates are outside the screen,
+ // the pixel shader discards the pixel and stencil value is left untouched. All such pixels will be skipped from
+ // further processing
+ RendedCoordTexTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+void EpipolarLightScattering ::RenderCoarseUnshadowedInctr()
+{
+ auto& RenderCoarseUnshadowedInsctrTech = m_RenderTech[RENDER_TECH_RENDER_COARSE_UNSHADOWED_INSCTR];
+ if (!RenderCoarseUnshadowedInsctrTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+ auto EntryPoint =
+ m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR ?
+ "RenderCoarseUnshadowedInsctrAndExtinctionPS" :
+ "RenderCoarseUnshadowedInsctrPS";
+
+ auto pRenderCoarseUnshadowedInsctrPS =
+ CreateShader(m_FrameAttribs.pDevice, "CoarseInsctr.fx", EntryPoint, SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ std::vector<ShaderResourceVariableDesc> Vars;
+ std::vector<StaticSamplerDesc> StaticSamplers;
+
+ std::unordered_set<std::string> ResourceNames;
+
+ const auto ResCount = pRenderCoarseUnshadowedInsctrPS->GetResourceCount();
+ for (Uint32 r = 0; r < ResCount; ++r)
+ ResourceNames.emplace(pRenderCoarseUnshadowedInsctrPS->GetResource(r).Name);
+
+ // clang-format off
+ const std::array<std::string, 4> StaticTextures =
+ {
+ std::string{"g_tex3DSingleSctrLUT"},
+ std::string{"g_tex3DHighOrderSctrLUT"},
+ std::string{"g_tex3DMultipleSctrLUT"},
+ std::string{"g_tex2DOccludedNetDensityToAtmTop"}
+ };
+ // clang-format on
+ for (const auto& Tex : StaticTextures)
+ {
+ if (ResourceNames.find(Tex) != ResourceNames.end())
+ {
+ Vars.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), Sam_LinearClamp);
+ }
+ }
+
+ if (ResourceNames.find("cbParticipatingMediaScatteringParams") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbParticipatingMediaScatteringParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ if (ResourceNames.find("cbPostProcessingAttribs") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+
+ ResourceLayout.Variables = Vars.data();
+ ResourceLayout.NumVariables = static_cast<Uint32>(Vars.size());
+ ResourceLayout.StaticSamplers = StaticSamplers.data();
+ ResourceLayout.NumStaticSamplers = static_cast<Uint32>(StaticSamplers.size());
+
+ const auto* PSOName = m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR ?
+ "RenderCoarseUnshadowedInsctrAndExtinctionPSO" :
+ "RenderCoarseUnshadowedInsctrPSO";
+ TEXTURE_FORMAT RTVFmts[] = {EpipolarInsctrTexFmt, EpipolarExtinctionFmt};
+ Uint8 NumRTVs = m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR ? 2 : 1;
+ auto EpipolarImageDepthFmt = m_ptex2DEpipolarImageDSV->GetTexture()->GetDesc().Format;
+ RenderCoarseUnshadowedInsctrTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, PSOName, m_pFullScreenTriangleVS,
+ pRenderCoarseUnshadowedInsctrPS, ResourceLayout, NumRTVs, RTVFmts,
+ EpipolarImageDepthFmt, DSS_StencilEqKeepStencil);
+ RenderCoarseUnshadowedInsctrTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RenderCoarseUnshadowedInsctrTech.PSODependencyFlags =
+ PSO_DEPENDENCY_EXTINCTION_EVAL_MODE |
+ PSO_DEPENDENCY_SINGLE_SCATTERING_MODE |
+ PSO_DEPENDENCY_MULTIPLE_SCATTERING_MODE;
+ RenderCoarseUnshadowedInsctrTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_LIGHT_ATTRIBS |
+ SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_COORDINATE_TEX;
+ }
+
+ if (m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR &&
+ !m_ptex2DEpipolarExtinctionRTV)
+ {
+ // Extinction texture size is num_slices x max_samples_in_slice. So the texture must be re-created when either changes.
+ CreateExtinctionTexture(m_FrameAttribs.pDevice);
+ }
+
+ ITextureView* ppRTVs[] = {m_ptex2DEpipolarInscatteringRTV, m_ptex2DEpipolarExtinctionRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR ? 2 : 1, ppRTVs, m_ptex2DEpipolarImageDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ float flt16max = 65504.f; // Epipolar Inscattering is 16-bit float
+ const float InvalidInsctr[] = {-flt16max, -flt16max, -flt16max, -flt16max};
+ if (m_ptex2DEpipolarInscatteringRTV)
+ m_FrameAttribs.pDeviceContext->ClearRenderTarget(m_ptex2DEpipolarInscatteringRTV, InvalidInsctr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ if (m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR)
+ {
+ const float One[] = {1, 1, 1, 1};
+ m_FrameAttribs.pDeviceContext->ClearRenderTarget(m_ptex2DEpipolarExtinctionRTV, One, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ }
+
+ RenderCoarseUnshadowedInsctrTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+ RenderCoarseUnshadowedInsctrTech.Render(m_FrameAttribs.pDeviceContext, 1);
+}
+
+void EpipolarLightScattering ::RefineSampleLocations()
+{
+ auto& RefineSampleLocationsTech = m_RenderTech[RENDER_TECH_REFINE_SAMPLE_LOCATIONS];
+ if (!RefineSampleLocationsTech.PSO)
+ {
+ // Thread group size must be at least as large as initial sample step
+ m_uiSampleRefinementCSThreadGroupSize = std::max(m_uiSampleRefinementCSMinimumThreadGroupSize, m_PostProcessingAttribs.uiInitialSampleStepInSlice);
+ // Thread group size cannot be larger than the total number of samples in slice
+ m_uiSampleRefinementCSThreadGroupSize = std::min(m_uiSampleRefinementCSThreadGroupSize, m_PostProcessingAttribs.uiMaxSamplesInSlice);
+ // Using small group size is inefficient since a lot of SIMD lanes become idle
+
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ // clang-format off
+ Macros.AddShaderMacro("INITIAL_SAMPLE_STEP", static_cast<Int32>(m_PostProcessingAttribs.uiInitialSampleStepInSlice));
+ Macros.AddShaderMacro("THREAD_GROUP_SIZE", static_cast<Int32>(m_uiSampleRefinementCSThreadGroupSize));
+ Macros.AddShaderMacro("REFINEMENT_CRITERION", m_PostProcessingAttribs.iRefinementCriterion);
+ Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.ToneMapping.bAutoExposure);
+ // clang-format on
+ Macros.Finalize();
+
+ auto pRefineSampleLocationsCS = CreateShader(m_FrameAttribs.pDevice, "RefineSampleLocations.fx", "RefineSampleLocationsCS",
+ SHADER_TYPE_COMPUTE, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_COMPUTE, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+ // clang-format on
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+
+ RefineSampleLocationsTech.InitializeComputeTechnique(m_FrameAttribs.pDevice, "RefineSampleLocations", pRefineSampleLocationsCS, ResourceLayout);
+ RefineSampleLocationsTech.PSO->BindStaticResources(SHADER_TYPE_COMPUTE, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RefineSampleLocationsTech.PSODependencyFlags =
+ PSO_DEPENDENCY_INITIAL_SAMPLE_STEP |
+ PSO_DEPENDENCY_REFINEMENT_CRITERION |
+ PSO_DEPENDENCY_AUTO_EXPOSURE;
+ RefineSampleLocationsTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_INTERPOLATION_SOURCE_TEX |
+ SRB_DEPENDENCY_COORDINATE_TEX |
+ SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_EPIPOLAR_INSCTR_TEX |
+ SRB_DEPENDENCY_AVERAGE_LUMINANCE_TEX;
+ }
+
+ RefineSampleLocationsTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+ DispatchComputeAttribs DispatchAttrs{
+ m_PostProcessingAttribs.uiMaxSamplesInSlice / m_uiSampleRefinementCSThreadGroupSize,
+ m_PostProcessingAttribs.uiNumEpipolarSlices};
+ RefineSampleLocationsTech.DispatchCompute(m_FrameAttribs.pDeviceContext, DispatchAttrs);
+}
+
+void EpipolarLightScattering ::MarkRayMarchingSamples()
+{
+ auto& MarkRayMarchingSamplesInStencilTech = m_RenderTech[RENDER_TECH_MARK_RAY_MARCHING_SAMPLES];
+ if (!MarkRayMarchingSamplesInStencilTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pMarkRayMarchingSamplesInStencilPS =
+ CreateShader(m_FrameAttribs.pDevice, "MarkRayMarchingSamples.fx", "MarkRayMarchingSamplesInStencilPS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ auto EpipolarImageDepthFmt = m_ptex2DEpipolarImageDSV->GetTexture()->GetDesc().Format;
+ MarkRayMarchingSamplesInStencilTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "MarkRayMarchingSamples",
+ m_pFullScreenTriangleVS, pMarkRayMarchingSamplesInStencilPS,
+ ResourceLayout, 0, nullptr, EpipolarImageDepthFmt, DSS_StencilEqIncStencil);
+ MarkRayMarchingSamplesInStencilTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ MarkRayMarchingSamplesInStencilTech.SRBDependencyFlags = SRB_DEPENDENCY_INTERPOLATION_SOURCE_TEX;
+ }
+
+ // Mark ray marching samples in the stencil
+ // The depth stencil state is configured to pass only pixels, whose stencil value equals 1. Thus all epipolar samples with
+ // coordinates outsied the screen (generated on the previous pass) are automatically discarded. The pixel shader only
+ // passes samples which are interpolated from themselves, the rest are discarded. Thus after this pass all ray
+ // marching samples will be marked with 2 in stencil
+ MarkRayMarchingSamplesInStencilTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(0, nullptr, m_ptex2DEpipolarImageDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ MarkRayMarchingSamplesInStencilTech.Render(m_FrameAttribs.pDeviceContext, 1);
+}
+
+void EpipolarLightScattering ::RenderSliceUVDirAndOrig()
+{
+ auto& RenderSliceUVDirInSMTech = m_RenderTech[RENDER_TECH_RENDER_SLICE_UV_DIRECTION];
+ if (!RenderSliceUVDirInSMTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pRenderSliceUVDirInSMPS =
+ CreateShader(m_FrameAttribs.pDevice, "SliceUVDirection.fx", "RenderSliceUVDirInShadowMapTexturePS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+ // clang-format on
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+
+ RenderSliceUVDirInSMTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "RenderSliceUVDirAndOrigin",
+ m_pFullScreenTriangleVS, pRenderSliceUVDirInSMPS,
+ ResourceLayout, SliceUVDirAndOriginTexFmt);
+ RenderSliceUVDirInSMTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RenderSliceUVDirInSMTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_LIGHT_ATTRIBS |
+ SRB_DEPENDENCY_SLICE_END_POINTS_TEX;
+ }
+
+ if (!m_ptex2DSliceUVDirAndOriginRTV)
+ {
+ CreateSliceUVDirAndOriginTexture(m_FrameAttribs.pDevice);
+ }
+
+ RenderSliceUVDirInSMTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+
+ ITextureView* ppRTVs[] = {m_ptex2DSliceUVDirAndOriginRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ RenderSliceUVDirInSMTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+void EpipolarLightScattering ::Build1DMinMaxMipMap(int iCascadeIndex)
+{
+ auto& InitMinMaxShadowMapTech = m_RenderTech[RENDER_TECH_INIT_MIN_MAX_SHADOW_MAP];
+ if (!InitMinMaxShadowMapTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("IS_32BIT_MIN_MAX_MAP", m_PostProcessingAttribs.bIs32BitMinMaxMipMap);
+ Macros.Finalize();
+
+ auto pInitializeMinMaxShadowMapPS = CreateShader(m_FrameAttribs.pDevice, "InitializeMinMaxShadowMap.fx", "InitializeMinMaxShadowMapPS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_PIXEL, m_bUseCombinedMinMaxTexture ? "cbPostProcessingAttribs" : "cbMiscDynamicParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+
+ StaticSamplerDesc StaticSamplers[] =
+ {
+ {SHADER_TYPE_PIXEL, "g_tex2DLightSpaceDepthMap", Sam_LinearClamp} // Linear, not comparison
+ };
+ // clang-format on
+
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+ ResourceLayout.StaticSamplers = StaticSamplers;
+ ResourceLayout.NumStaticSamplers = _countof(StaticSamplers);
+
+ TEXTURE_FORMAT ShadowMapFmt = m_ptex2DMinMaxShadowMapSRV[0]->GetTexture()->GetDesc().Format;
+ InitMinMaxShadowMapTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "InitMinMaxShadowMap",
+ m_pFullScreenTriangleVS, pInitializeMinMaxShadowMapPS,
+ ResourceLayout, ShadowMapFmt);
+ InitMinMaxShadowMapTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ InitMinMaxShadowMapTech.PSODependencyFlags =
+ PSO_DEPENDENCY_USE_1D_MIN_MAX_TREE |
+ PSO_DEPENDENCY_USE_COMBINED_MIN_MAX_TEX |
+ PSO_DEPENDENCY_IS_32_BIT_MIN_MAX_TREE;
+ InitMinMaxShadowMapTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_SLICE_UV_DIR_TEX |
+ SRB_DEPENDENCY_SHADOW_MAP;
+ }
+
+ auto& ComputeMinMaxSMLevelTech = m_RenderTech[RENDER_TECH_COMPUTE_MIN_MAX_SHADOW_MAP_LEVEL];
+ if (!ComputeMinMaxSMLevelTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pComputeMinMaxSMLevelPS = CreateShader(m_FrameAttribs.pDevice, "ComputeMinMaxShadowMapLevel.fx", "ComputeMinMaxShadowMapLevelPS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc VarDesc[] =
+ {
+ {SHADER_TYPE_PIXEL, "cbMiscDynamicParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+ // clang-format on
+ ResourceLayout.Variables = VarDesc;
+ ResourceLayout.NumVariables = _countof(VarDesc);
+
+ TEXTURE_FORMAT ShadowMapFmt = m_ptex2DMinMaxShadowMapSRV[0]->GetTexture()->GetDesc().Format;
+ ComputeMinMaxSMLevelTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "ComputeMinMaxShadowMapLevel",
+ m_pFullScreenTriangleVS, pComputeMinMaxSMLevelPS, ResourceLayout, ShadowMapFmt);
+ ComputeMinMaxSMLevelTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ ComputeMinMaxSMLevelTech.PSODependencyFlags = PSO_DEPENDENCY_IS_32_BIT_MIN_MAX_TREE;
+ ComputeMinMaxSMLevelTech.SRBDependencyFlags = SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP;
+
+ m_pComputeMinMaxSMLevelSRB[0].Release();
+ m_pComputeMinMaxSMLevelSRB[1].Release();
+ }
+
+ if (!m_pComputeMinMaxSMLevelSRB[0])
+ {
+ for (int Parity = 0; Parity < 2; ++Parity)
+ {
+ ComputeMinMaxSMLevelTech.PSO->CreateShaderResourceBinding(&m_pComputeMinMaxSMLevelSRB[Parity], true);
+ auto* pVar = m_pComputeMinMaxSMLevelSRB[Parity]->GetVariableByName(SHADER_TYPE_PIXEL, "g_tex2DMinMaxLightSpaceDepth");
+ pVar->Set(m_ptex2DMinMaxShadowMapSRV[Parity]);
+ m_pComputeMinMaxSMLevelSRB[Parity]->BindResources(SHADER_TYPE_PIXEL | SHADER_TYPE_VERTEX, m_pResMapping, BIND_SHADER_RESOURCES_KEEP_EXISTING | BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ }
+ }
+
+ auto iMinMaxTexHeight = m_PostProcessingAttribs.uiNumEpipolarSlices;
+ if (m_bUseCombinedMinMaxTexture)
+ iMinMaxTexHeight *= (m_PostProcessingAttribs.iNumCascades - m_PostProcessingAttribs.iFirstCascadeToRayMarch);
+
+ auto tex2DMinMaxShadowMap0 = m_ptex2DMinMaxShadowMapRTV[0]->GetTexture();
+ auto tex2DMinMaxShadowMap1 = m_ptex2DMinMaxShadowMapRTV[1]->GetTexture();
+
+ // Computing min/max mip map using compute shader is much slower because a lot of threads are idle
+ Uint32 uiXOffset = 0;
+ Uint32 uiPrevXOffset = 0;
+ Uint32 uiParity = 0;
+#ifdef _DEBUG
+ {
+ const auto& MinMaxShadowMapTexDesc = m_ptex2DMinMaxShadowMapRTV[0]->GetTexture()->GetDesc();
+ VERIFY_EXPR(MinMaxShadowMapTexDesc.Width == m_PostProcessingAttribs.uiMinMaxShadowMapResolution);
+ VERIFY_EXPR(MinMaxShadowMapTexDesc.Height == iMinMaxTexHeight);
+ }
+#endif
+ // Note that we start rendering min/max shadow map from step == 2
+ for (Uint32 iStep = 2; iStep <= (Uint32)m_PostProcessingAttribs.fMaxShadowMapStep; iStep *= 2, uiParity = (uiParity + 1) % 2)
+ {
+ // Use two buffers which are in turn used as the source and destination
+ ITextureView* pRTVs[] = {m_ptex2DMinMaxShadowMapRTV[uiParity]};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(_countof(pRTVs), pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ Viewport VP;
+ VP.Width = static_cast<float>(m_PostProcessingAttribs.uiMinMaxShadowMapResolution / iStep);
+ VP.Height = static_cast<float>(iMinMaxTexHeight);
+ VP.TopLeftX = static_cast<float>(uiXOffset);
+ VP.TopLeftY = 0;
+ m_FrameAttribs.pDeviceContext->SetViewports(1, &VP, 0, 0);
+
+ // Set source and destination min/max data offsets:
+ {
+ MapHelper<MiscDynamicParams> pMiscDynamicParams(m_FrameAttribs.pDeviceContext, m_pcbMiscParams, MAP_WRITE, MAP_FLAG_DISCARD);
+ pMiscDynamicParams->ui4SrcMinMaxLevelXOffset = uiPrevXOffset;
+ pMiscDynamicParams->ui4DstMinMaxLevelXOffset = uiXOffset;
+ pMiscDynamicParams->fCascadeInd = static_cast<float>(iCascadeIndex);
+ }
+
+ if (iStep == 2)
+ {
+ // At the initial pass, the shader gathers 8 depths which will be used for
+ // PCF filtering at the sample location and its next neighbor along the slice
+ // and outputs min/max depths
+ InitMinMaxShadowMapTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+ InitMinMaxShadowMapTech.Render(m_FrameAttribs.pDeviceContext);
+ }
+ else
+ {
+ // At the subsequent passes, the shader loads two min/max values from the next finer level
+ // to compute next level of the binary tree
+ RenderFullScreenTriangle(m_FrameAttribs.pDeviceContext, ComputeMinMaxSMLevelTech.PSO, m_pComputeMinMaxSMLevelSRB[(uiParity + 1) % 2]);
+ }
+
+ // All the data must reside in 0-th texture, so copy current level, if necessary, from 1-st texture
+ if (uiParity == 1)
+ {
+ Box SrcBox;
+ SrcBox.MinX = uiXOffset;
+ SrcBox.MaxX = uiXOffset + m_PostProcessingAttribs.uiMinMaxShadowMapResolution / iStep;
+ SrcBox.MinY = 0;
+ SrcBox.MaxY = iMinMaxTexHeight;
+
+ CopyTextureAttribs CopyAttribs(tex2DMinMaxShadowMap1, RESOURCE_STATE_TRANSITION_MODE_TRANSITION, tex2DMinMaxShadowMap0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ CopyAttribs.pSrcBox = &SrcBox;
+ CopyAttribs.DstX = uiXOffset;
+ m_FrameAttribs.pDeviceContext->CopyTexture(CopyAttribs);
+ }
+
+ uiPrevXOffset = uiXOffset;
+ uiXOffset += m_PostProcessingAttribs.uiMinMaxShadowMapResolution / iStep;
+ }
+}
+
+void EpipolarLightScattering ::DoRayMarching(Uint32 uiMaxStepsAlongRay,
+ int iCascadeIndex)
+{
+ auto& DoRayMarchTech = m_RenderTech[m_PostProcessingAttribs.bUse1DMinMaxTree ? RENDER_TECH_RAY_MARCH_MIN_MAX_OPT : RENDER_TECH_RAY_MARCH_NO_MIN_MAX_OPT];
+ if (!DoRayMarchTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("CASCADE_PROCESSING_MODE", m_PostProcessingAttribs.iCascadeProcessingMode);
+ Macros.AddShaderMacro("USE_1D_MIN_MAX_TREE", m_PostProcessingAttribs.bUse1DMinMaxTree);
+ Macros.Finalize();
+
+ auto pDoRayMarchPS =
+ CreateShader(m_FrameAttribs.pDevice, "RayMarch.fx", "RayMarchPS", SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ std::vector<ShaderResourceVariableDesc> Vars;
+ std::vector<StaticSamplerDesc> StaticSamplers;
+
+ std::unordered_set<std::string> ResourceNames;
+
+ const auto ResCount = pDoRayMarchPS->GetResourceCount();
+ for (Uint32 r = 0; r < ResCount; ++r)
+ ResourceNames.emplace(pDoRayMarchPS->GetResource(r).Name);
+
+ // clang-format off
+ const std::array<std::string, 4> StaticLinearTextures =
+ {
+ "g_tex3DSingleSctrLUT",
+ "g_tex3DHighOrderSctrLUT",
+ "g_tex3DMultipleSctrLUT",
+ "g_tex2DOccludedNetDensityToAtmTop"
+ };
+ // clang-format on
+ for (const auto& Tex : StaticLinearTextures)
+ {
+ if (ResourceNames.find(Tex) != ResourceNames.end())
+ {
+ Vars.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), Sam_LinearClamp);
+ }
+ }
+
+ if (ResourceNames.find("cbParticipatingMediaScatteringParams") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbParticipatingMediaScatteringParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ if (ResourceNames.find("cbPostProcessingAttribs") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ if (ResourceNames.find("cbMiscDynamicParams") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbMiscDynamicParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+
+ if (ResourceNames.find("g_tex2DCamSpaceZ") != ResourceNames.end())
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, "g_tex2DCamSpaceZ", Sam_LinearClamp);
+
+ ResourceLayout.Variables = Vars.data();
+ ResourceLayout.NumVariables = static_cast<Uint32>(Vars.size());
+ ResourceLayout.StaticSamplers = StaticSamplers.data();
+ ResourceLayout.NumStaticSamplers = static_cast<Uint32>(StaticSamplers.size());
+
+ auto EpipolarImageDepthFmt = m_ptex2DEpipolarImageDSV->GetTexture()->GetDesc().Format;
+ DoRayMarchTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "RayMarch", m_pFullScreenTriangleVS,
+ pDoRayMarchPS, ResourceLayout, EpipolarInsctrTexFmt, EpipolarImageDepthFmt,
+ DSS_StencilEqKeepStencil, BS_AdditiveBlend);
+ DoRayMarchTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ DoRayMarchTech.PSODependencyFlags =
+ PSO_DEPENDENCY_USE_1D_MIN_MAX_TREE |
+ PSO_DEPENDENCY_CASCADE_PROCESSING_MODE |
+ PSO_DEPENDENCY_USE_COMBINED_MIN_MAX_TEX |
+ PSO_DEPENDENCY_ENABLE_LIGHT_SHAFTS |
+ PSO_DEPENDENCY_MULTIPLE_SCATTERING_MODE |
+ PSO_DEPENDENCY_SINGLE_SCATTERING_MODE;
+
+ DoRayMarchTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_LIGHT_ATTRIBS |
+ SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_SLICE_UV_DIR_TEX |
+ SRB_DEPENDENCY_SHADOW_MAP |
+ SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP |
+ SRB_DEPENDENCY_COORDINATE_TEX |
+ SRB_DEPENDENCY_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_SRC_COLOR_BUFFER |
+ SRB_DEPENDENCY_AVERAGE_LUMINANCE_TEX;
+ }
+
+ {
+ MapHelper<MiscDynamicParams> pMiscDynamicParams(m_FrameAttribs.pDeviceContext, m_pcbMiscParams, MAP_WRITE, MAP_FLAG_DISCARD);
+ pMiscDynamicParams->fMaxStepsAlongRay = static_cast<float>(uiMaxStepsAlongRay);
+ pMiscDynamicParams->fCascadeInd = static_cast<float>(iCascadeIndex);
+ }
+
+ int iNumInst = 0;
+ if (m_PostProcessingAttribs.bEnableLightShafts)
+ {
+ switch (m_PostProcessingAttribs.iCascadeProcessingMode)
+ {
+ case CASCADE_PROCESSING_MODE_SINGLE_PASS:
+ case CASCADE_PROCESSING_MODE_MULTI_PASS:
+ iNumInst = 1;
+ break;
+ case CASCADE_PROCESSING_MODE_MULTI_PASS_INST:
+ iNumInst = m_PostProcessingAttribs.iNumCascades - m_PostProcessingAttribs.iFirstCascadeToRayMarch;
+ break;
+ }
+ }
+ else
+ {
+ iNumInst = 1;
+ }
+
+ // Depth stencil view now contains 2 for these pixels, for which ray marchings is to be performed
+ // Depth stencil state is configured to pass only these pixels and discard the rest
+ DoRayMarchTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+
+ ITextureView* ppRTVs[] = {m_ptex2DInitialScatteredLightRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, m_ptex2DEpipolarImageDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ DoRayMarchTech.Render(m_FrameAttribs.pDeviceContext, 2, iNumInst);
+}
+
+void EpipolarLightScattering ::InterpolateInsctrIrradiance()
+{
+ auto& InterpolateIrradianceTech = m_RenderTech[RENDER_TECH_INTERPOLATE_IRRADIANCE];
+ if (!InterpolateIrradianceTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pInterpolateIrradiancePS = CreateShader(m_FrameAttribs.pDevice, "InterpolateIrradiance.fx", "InterpolateIrradiancePS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ InterpolateIrradianceTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "InterpolateIrradiance",
+ m_pFullScreenTriangleVS, pInterpolateIrradiancePS,
+ ResourceLayout, EpipolarInsctrTexFmt);
+ InterpolateIrradianceTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ InterpolateIrradianceTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_INTERPOLATION_SOURCE_TEX |
+ SRB_DEPENDENCY_INITIAL_SCTR_LIGHT_TEX;
+ }
+
+ InterpolateIrradianceTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+
+ ITextureView* ppRTVs[] = {m_ptex2DEpipolarInscatteringRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ InterpolateIrradianceTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+
+void EpipolarLightScattering ::UnwarpEpipolarScattering(bool bRenderLuminance)
+{
+ static constexpr Uint32 SRBDependencies =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_LIGHT_ATTRIBS |
+ SRB_DEPENDENCY_SRC_COLOR_BUFFER |
+ SRB_DEPENDENCY_SLICE_END_POINTS_TEX |
+ SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_EPIPOLAR_INSCTR_TEX |
+ SRB_DEPENDENCY_SLICE_UV_DIR_TEX |
+ SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP |
+ SRB_DEPENDENCY_SHADOW_MAP |
+ SRB_DEPENDENCY_AVERAGE_LUMINANCE_TEX |
+ SRB_DEPENDENCY_EPIPOLAR_EXTINCTION_TEX;
+
+ auto& UnwarpEpipolarSctrImgTech = m_RenderTech[RENDER_TECH_UNWARP_EPIPOLAR_SCATTERING];
+ if (!UnwarpEpipolarSctrImgTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ // clang-format off
+ Macros.AddShaderMacro("PERFORM_TONE_MAPPING", true);
+ Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.ToneMapping.bAutoExposure);
+ Macros.AddShaderMacro("TONE_MAPPING_MODE", m_PostProcessingAttribs.ToneMapping.iToneMappingMode);
+ Macros.AddShaderMacro("CORRECT_INSCATTERING_AT_DEPTH_BREAKS", m_PostProcessingAttribs.bCorrectScatteringAtDepthBreaks);
+ // clang-format on
+ Macros.Finalize();
+
+ auto pUnwarpEpipolarSctrImgPS = CreateShader(m_FrameAttribs.pDevice, "UnwarpEpipolarScattering.fx", "ApplyInscatteredRadiancePS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ //{SHADER_TYPE_PIXEL, "cbParticipatingMediaScatteringParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}, // To avoid warnings
+ {SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+
+ std::vector<StaticSamplerDesc> StaticSamplers =
+ {
+ {SHADER_TYPE_PIXEL, "g_tex2DSliceEndPoints", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DEpipolarCamSpaceZ", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DScatteredColor", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DCamSpaceZ", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DColorBuffer", Sam_PointClamp}
+ };
+ // clang-format on
+ if (m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR)
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, "g_tex2DEpipolarExtinction", Sam_LinearClamp);
+
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+ ResourceLayout.StaticSamplers = StaticSamplers.data();
+ ResourceLayout.NumStaticSamplers = static_cast<Uint32>(StaticSamplers.size());
+
+ UnwarpEpipolarSctrImgTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "UnwarpEpipolarScattering",
+ m_pFullScreenTriangleVS, pUnwarpEpipolarSctrImgPS,
+ ResourceLayout, m_BackBufferFmt, m_DepthBufferFmt, DSS_Default);
+ UnwarpEpipolarSctrImgTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, 0);
+
+ UnwarpEpipolarSctrImgTech.PSODependencyFlags =
+ PSO_DEPENDENCY_AUTO_EXPOSURE |
+ PSO_DEPENDENCY_TONE_MAPPING_MODE |
+ PSO_DEPENDENCY_CORRECT_SCATTERING |
+ PSO_DEPENDENCY_EXTINCTION_EVAL_MODE;
+
+ UnwarpEpipolarSctrImgTech.SRBDependencyFlags = SRBDependencies;
+ }
+
+ auto& UnwarpAndRenderLuminanceTech = m_RenderTech[RENDER_TECH_UNWARP_AND_RENDER_LUMINANCE];
+ if (!UnwarpAndRenderLuminanceTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("PERFORM_TONE_MAPPING", false);
+ // No inscattering correction - we need to render the entire image in low resolution
+ Macros.AddShaderMacro("CORRECT_INSCATTERING_AT_DEPTH_BREAKS", false);
+ Macros.Finalize();
+
+ auto pUnwarpAndRenderLuminancePS = CreateShader(m_FrameAttribs.pDevice, "UnwarpEpipolarScattering.fx", "ApplyInscatteredRadiancePS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ //{SHADER_TYPE_PIXEL, "cbParticipatingMediaScatteringParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}, // To avoid warnings
+ {SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+
+ std::vector<StaticSamplerDesc> StaticSamplers =
+ {
+ {SHADER_TYPE_PIXEL, "g_tex2DSliceEndPoints", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DEpipolarCamSpaceZ", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DScatteredColor", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DCamSpaceZ", Sam_LinearClamp},
+ {SHADER_TYPE_PIXEL, "g_tex2DColorBuffer", Sam_PointClamp}
+ };
+ // clang-format on
+
+ if (m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR)
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, "g_tex2DEpipolarExtinction", Sam_LinearClamp);
+
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+ ResourceLayout.StaticSamplers = StaticSamplers.data();
+ ResourceLayout.NumStaticSamplers = static_cast<Uint32>(StaticSamplers.size());
+
+ UnwarpAndRenderLuminanceTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "UnwarpAndRenderLuminance",
+ m_pFullScreenTriangleVS, pUnwarpAndRenderLuminancePS,
+ ResourceLayout, WeightedLogLumTexFmt);
+ UnwarpAndRenderLuminanceTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, 0);
+
+ UnwarpAndRenderLuminanceTech.PSODependencyFlags = PSO_DEPENDENCY_EXTINCTION_EVAL_MODE;
+ UnwarpAndRenderLuminanceTech.SRBDependencyFlags = SRBDependencies;
+ }
+
+ // Unwarp inscattering image and apply it to attenuated backgorund
+ if (bRenderLuminance)
+ {
+ UnwarpAndRenderLuminanceTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping, BIND_SHADER_RESOURCES_KEEP_EXISTING);
+
+ // Disable depth testing - we need to render the entire image in low resolution
+ UnwarpAndRenderLuminanceTech.Render(m_FrameAttribs.pDeviceContext);
+ }
+ else
+ {
+ UnwarpEpipolarSctrImgTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping, BIND_SHADER_RESOURCES_KEEP_EXISTING);
+
+ // Enable depth testing to write 0.0 to the depth buffer. All pixel that require
+ // inscattering correction (if enabled) will be discarded, so that 1.0 will be retained
+ // This 1.0 will then be used to perform inscattering correction
+ UnwarpEpipolarSctrImgTech.Render(m_FrameAttribs.pDeviceContext);
+ }
+}
+
+void EpipolarLightScattering ::UpdateAverageLuminance()
+{
+ auto& UpdateAverageLuminanceTech = m_RenderTech[RENDER_TECH_UPDATE_AVERAGE_LUMINANCE];
+ if (!UpdateAverageLuminanceTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.AddShaderMacro("LIGHT_ADAPTATION", m_PostProcessingAttribs.ToneMapping.bLightAdaptation);
+ // static_cast<int>() is required because Run() gets its arguments by reference
+ // and gcc will try to find reference to sm_iLowResLuminanceMips, which does not exist
+ Macros.AddShaderMacro("LOW_RES_LUMINANCE_MIPS", static_cast<int>(sm_iLowResLuminanceMips));
+ Macros.Finalize();
+
+ auto pUpdateAverageLuminancePS = CreateShader(m_FrameAttribs.pDevice, "UpdateAverageLuminance.fx", "UpdateAverageLuminancePS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_PIXEL, "cbMiscDynamicParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+ // clang-format on
+
+ if (m_PostProcessingAttribs.ToneMapping.bLightAdaptation)
+ {
+ ResourceLayout.Variables = Vars;
+ ResourceLayout.NumVariables = _countof(Vars);
+ }
+ UpdateAverageLuminanceTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "UpdateAverageLuminance",
+ m_pFullScreenTriangleVS, pUpdateAverageLuminancePS,
+ ResourceLayout, AverageLuminanceTexFmt, TEX_FORMAT_UNKNOWN,
+ DSS_DisableDepth, BS_AlphaBlend);
+ UpdateAverageLuminanceTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ UpdateAverageLuminanceTech.PSODependencyFlags = PSO_DEPENDENCY_LIGHT_ADAPTATION;
+ }
+
+ {
+ MapHelper<MiscDynamicParams> pMiscDynamicParams(m_FrameAttribs.pDeviceContext, m_pcbMiscParams, MAP_WRITE, MAP_FLAG_DISCARD);
+ pMiscDynamicParams->fElapsedTime = (float)m_FrameAttribs.dElapsedTime;
+ }
+
+ UpdateAverageLuminanceTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping);
+
+ ITextureView* ppRTVs[] = {m_ptex2DAverageLuminanceRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ UpdateAverageLuminanceTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+
+void EpipolarLightScattering ::FixInscatteringAtDepthBreaks(Uint32 uiMaxStepsAlongRay,
+ EFixInscatteringMode Mode)
+{
+ auto& FixInsctrAtDepthBreaksTech = m_RenderTech[RENDER_TECH_FIX_INSCATTERING_LUM_ONLY + static_cast<int>(Mode)];
+ if (!FixInsctrAtDepthBreaksTech.PSO)
+ {
+ bool bRenderLuminance = Mode == EFixInscatteringMode::LuminanceOnly;
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ // clang-format off
+ Macros.AddShaderMacro("CASCADE_PROCESSING_MODE", CASCADE_PROCESSING_MODE_SINGLE_PASS);
+ Macros.AddShaderMacro("PERFORM_TONE_MAPPING", !bRenderLuminance);
+ Macros.AddShaderMacro("AUTO_EXPOSURE", m_PostProcessingAttribs.ToneMapping.bAutoExposure);
+ Macros.AddShaderMacro("TONE_MAPPING_MODE", m_PostProcessingAttribs.ToneMapping.iToneMappingMode);
+ Macros.AddShaderMacro("USE_1D_MIN_MAX_TREE", false);
+ // clang-format on
+ Macros.Finalize();
+
+ auto pFixInsctrAtDepthBreaksPS =
+ CreateShader(m_FrameAttribs.pDevice, "RayMarch.fx", "FixAndApplyInscatteredRadiancePS",
+ SHADER_TYPE_PIXEL, Macros);
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+
+ std::vector<ShaderResourceVariableDesc> Vars;
+ std::vector<StaticSamplerDesc> StaticSamplers;
+
+ std::unordered_set<std::string> ResourceNames;
+
+ const auto ResCount = pFixInsctrAtDepthBreaksPS->GetResourceCount();
+ for (Uint32 r = 0; r < ResCount; ++r)
+ ResourceNames.emplace(pFixInsctrAtDepthBreaksPS->GetResource(r).Name);
+
+ // clang-format off
+ const std::array<std::string, 4> StaticLinearTextures =
+ {
+ "g_tex3DSingleSctrLUT",
+ "g_tex3DHighOrderSctrLUT",
+ "g_tex3DMultipleSctrLUT",
+ "g_tex2DOccludedNetDensityToAtmTop"
+ };
+ // clang-format on
+ for (const auto& Tex : StaticLinearTextures)
+ {
+ if (ResourceNames.find(Tex) != ResourceNames.end())
+ {
+ Vars.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, Tex.c_str(), Sam_LinearClamp);
+ }
+ }
+
+ if (ResourceNames.find("cbParticipatingMediaScatteringParams") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbParticipatingMediaScatteringParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ if (ResourceNames.find("cbPostProcessingAttribs") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+ if (ResourceNames.find("cbMiscDynamicParams") != ResourceNames.end())
+ Vars.emplace_back(SHADER_TYPE_PIXEL, "cbMiscDynamicParams", SHADER_RESOURCE_VARIABLE_TYPE_STATIC);
+
+ if (ResourceNames.find("g_tex2DCamSpaceZ") != ResourceNames.end())
+ StaticSamplers.emplace_back(SHADER_TYPE_PIXEL, "g_tex2DCamSpaceZ", Sam_LinearClamp);
+
+ ResourceLayout.Variables = Vars.data();
+ ResourceLayout.NumVariables = static_cast<Uint32>(Vars.size());
+ ResourceLayout.StaticSamplers = StaticSamplers.data();
+ ResourceLayout.NumStaticSamplers = static_cast<Uint32>(StaticSamplers.size());
+
+ if (Mode == EFixInscatteringMode::LuminanceOnly)
+ {
+ // Luminance Only
+ // Disable depth and stencil tests to render all pixels
+ // Use default blend state to overwrite old luminance values
+ FixInsctrAtDepthBreaksTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "FixInsctrAtDepthBreaksLumOnly",
+ m_pFullScreenTriangleVS, pFixInsctrAtDepthBreaksPS,
+ ResourceLayout, WeightedLogLumTexFmt);
+ }
+ else if (Mode == EFixInscatteringMode::FixInscattering)
+ {
+ // Fix Inscattering
+ // Depth breaks are marked with 1.0 in depth, so we enable depth test
+ // to render only pixels that require correction
+ // Use default blend state - the rendering is always done in single pass
+ FixInsctrAtDepthBreaksTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "FixInsctrAtDepthBreaks",
+ m_pFullScreenTriangleVS, pFixInsctrAtDepthBreaksPS,
+ ResourceLayout, m_BackBufferFmt, m_DepthBufferFmt, DSS_Default);
+ }
+ else if (Mode == EFixInscatteringMode::FullScreenRayMarching)
+ {
+ // Full Screen Ray Marching
+ // Disable depth and stencil tests since we are performing
+ // full screen ray marching
+ // Use default blend state - the rendering is always done in single pass
+ FixInsctrAtDepthBreaksTech.InitializeFullScreenTriangleTechnique(m_FrameAttribs.pDevice, "FixInsctrAtDepthBreaks",
+ m_pFullScreenTriangleVS, pFixInsctrAtDepthBreaksPS,
+ ResourceLayout, m_BackBufferFmt, m_DepthBufferFmt, DSS_DisableDepth);
+ }
+ FixInsctrAtDepthBreaksTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+
+ FixInsctrAtDepthBreaksTech.PSODependencyFlags =
+ PSO_DEPENDENCY_CASCADE_PROCESSING_MODE |
+ PSO_DEPENDENCY_USE_COMBINED_MIN_MAX_TEX |
+ PSO_DEPENDENCY_ENABLE_LIGHT_SHAFTS |
+ PSO_DEPENDENCY_MULTIPLE_SCATTERING_MODE |
+ PSO_DEPENDENCY_SINGLE_SCATTERING_MODE |
+ PSO_DEPENDENCY_AUTO_EXPOSURE |
+ PSO_DEPENDENCY_TONE_MAPPING_MODE;
+
+ FixInsctrAtDepthBreaksTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_CAMERA_ATTRIBS |
+ SRB_DEPENDENCY_LIGHT_ATTRIBS |
+ SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_SLICE_UV_DIR_TEX |
+ SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP |
+ SRB_DEPENDENCY_COORDINATE_TEX |
+ SRB_DEPENDENCY_CAM_SPACE_Z_TEX |
+ SRB_DEPENDENCY_SRC_COLOR_BUFFER |
+ SRB_DEPENDENCY_AVERAGE_LUMINANCE_TEX;
+ }
+
+ {
+ MapHelper<MiscDynamicParams> pMiscDynamicParams(m_FrameAttribs.pDeviceContext, m_pcbMiscParams, MAP_WRITE, MAP_FLAG_DISCARD);
+ pMiscDynamicParams->fMaxStepsAlongRay = static_cast<float>(uiMaxStepsAlongRay);
+ pMiscDynamicParams->fCascadeInd = static_cast<float>(m_PostProcessingAttribs.iFirstCascadeToRayMarch);
+ }
+
+ FixInsctrAtDepthBreaksTech.PrepareSRB(m_FrameAttribs.pDevice, m_pResMapping, BIND_SHADER_RESOURCES_KEEP_EXISTING);
+
+ FixInsctrAtDepthBreaksTech.Render(m_FrameAttribs.pDeviceContext);
+}
+
+void EpipolarLightScattering ::RenderSampleLocations()
+{
+ auto& RenderSampleLocationsTech = m_RenderTech[RENDER_TECH_RENDER_SAMPLE_LOCATIONS];
+ if (!RenderSampleLocationsTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ DefineMacros(Macros);
+ Macros.Finalize();
+
+ auto pRenderSampleLocationsVS = CreateShader(m_FrameAttribs.pDevice, "RenderSampling.fx", "RenderSampleLocationsVS",
+ SHADER_TYPE_VERTEX, Macros);
+ auto pRenderSampleLocationsPS = CreateShader(m_FrameAttribs.pDevice, "RenderSampling.fx", "RenderSampleLocationsPS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineStateDesc PSODesc;
+ PSODesc.ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_VERTEX, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}};
+ PSODesc.ResourceLayout.Variables = Vars;
+ PSODesc.ResourceLayout.NumVariables = _countof(Vars);
+
+ PSODesc.Name = "Render sample locations PSO";
+ auto& GraphicsPipeline = PSODesc.GraphicsPipeline;
+ GraphicsPipeline.RasterizerDesc.FillMode = FILL_MODE_SOLID;
+ GraphicsPipeline.RasterizerDesc.CullMode = CULL_MODE_NONE;
+ GraphicsPipeline.RasterizerDesc.FrontCounterClockwise = true;
+ GraphicsPipeline.DepthStencilDesc = DSS_DisableDepth;
+ GraphicsPipeline.BlendDesc = BS_AlphaBlend;
+ GraphicsPipeline.pVS = pRenderSampleLocationsVS;
+ GraphicsPipeline.pPS = pRenderSampleLocationsPS;
+ GraphicsPipeline.NumRenderTargets = 1;
+ GraphicsPipeline.RTVFormats[0] = m_BackBufferFmt;
+ GraphicsPipeline.DSVFormat = m_DepthBufferFmt;
+ GraphicsPipeline.PrimitiveTopology = PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ m_FrameAttribs.pDevice->CreatePipelineState(PSODesc, &RenderSampleLocationsTech.PSO);
+ RenderSampleLocationsTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RenderSampleLocationsTech.SRB.Release();
+
+ RenderSampleLocationsTech.PSODependencyFlags = 0;
+ RenderSampleLocationsTech.SRBDependencyFlags =
+ SRB_DEPENDENCY_INTERPOLATION_SOURCE_TEX |
+ SRB_DEPENDENCY_COORDINATE_TEX;
+ }
+
+ if (!RenderSampleLocationsTech.SRB)
+ {
+ RenderSampleLocationsTech.PSO->CreateShaderResourceBinding(&RenderSampleLocationsTech.SRB, true);
+ RenderSampleLocationsTech.SRB->BindResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ }
+
+ DrawAttribs Attribs;
+ Attribs.NumVertices = 4;
+ Attribs.NumInstances = m_PostProcessingAttribs.uiMaxSamplesInSlice * m_PostProcessingAttribs.uiNumEpipolarSlices;
+ m_FrameAttribs.pDeviceContext->SetPipelineState(RenderSampleLocationsTech.PSO);
+ m_FrameAttribs.pDeviceContext->CommitShaderResources(RenderSampleLocationsTech.SRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ m_FrameAttribs.pDeviceContext->Draw(Attribs);
+}
+
+void EpipolarLightScattering ::CreateExtinctionTexture(IRenderDevice* pDevice)
+{
+ TextureDesc TexDesc;
+ TexDesc.Name = "Epipolar Extinction",
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = m_PostProcessingAttribs.uiMaxSamplesInSlice;
+ TexDesc.Height = m_PostProcessingAttribs.uiNumEpipolarSlices;
+ TexDesc.Format = EpipolarExtinctionFmt;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ TexDesc.ClearValue.Format = TEX_FORMAT_UNKNOWN;
+ TexDesc.ClearValue.Color[0] = 1;
+ TexDesc.ClearValue.Color[1] = 1;
+ TexDesc.ClearValue.Color[2] = 1;
+ TexDesc.ClearValue.Color[3] = 1;
+
+ // MaxSamplesInSlice x NumSlices RGBA8_UNORM texture to store extinction
+ // for every epipolar sample
+ RefCntAutoPtr<ITexture> tex2DEpipolarExtinction;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DEpipolarExtinction);
+ auto* tex2DEpipolarExtinctionSRV = tex2DEpipolarExtinction->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ tex2DEpipolarExtinctionSRV->SetSampler(m_pLinearClampSampler);
+ m_pResMapping->AddResource("g_tex2DEpipolarExtinction", tex2DEpipolarExtinctionSRV, false);
+ m_ptex2DEpipolarExtinctionRTV = tex2DEpipolarExtinction->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+}
+
+void EpipolarLightScattering ::CreateAmbientSkyLightTexture(IRenderDevice* pDevice)
+{
+ TextureDesc TexDesc;
+ TexDesc.Name = "Ambient Sky Light";
+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
+ TexDesc.Width = sm_iAmbientSkyLightTexDim;
+ TexDesc.Height = 1;
+ TexDesc.Format = AmbientSkyLightTexFmt;
+ TexDesc.MipLevels = 1;
+ TexDesc.Usage = USAGE_DEFAULT;
+ TexDesc.BindFlags = BIND_RENDER_TARGET | BIND_SHADER_RESOURCE;
+ RefCntAutoPtr<ITexture> tex2DAmbientSkyLight;
+ pDevice->CreateTexture(TexDesc, nullptr, &tex2DAmbientSkyLight);
+
+ m_ptex2DAmbientSkyLightSRV = tex2DAmbientSkyLight->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DAmbientSkyLightRTV = tex2DAmbientSkyLight->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+ m_ptex2DAmbientSkyLightSRV->SetSampler(m_pLinearClampSampler);
+}
+
+void EpipolarLightScattering ::PerformPostProcessing(FrameAttribs& frameAttribs,
+ EpipolarLightScatteringAttribs& PPAttribs)
+{
+ VERIFY(PPAttribs.uiNumEpipolarSlices > 0, "Number of epipolar slices must not be 0");
+ VERIFY(PPAttribs.uiMaxSamplesInSlice > 0, "Max samples in slice must not be 0");
+ VERIFY(PPAttribs.uiInitialSampleStepInSlice > 0, "Initial sample step in slice must not be 0");
+ VERIFY(IsPowerOfTwo(PPAttribs.uiInitialSampleStepInSlice), "Initial sample step in slice (", PPAttribs.uiInitialSampleStepInSlice, ") must be power of two");
+ VERIFY(PPAttribs.uiEpipoleSamplingDensityFactor > 0, "Epipole sampling density factor must not be 0");
+ VERIFY(IsPowerOfTwo(PPAttribs.uiEpipoleSamplingDensityFactor), "Epipole sampling desity factor (", PPAttribs.uiEpipoleSamplingDensityFactor, ") must be power of two");
+ VERIFY(PPAttribs.uiInstrIntegralSteps > 0, "Inscattering integral steps must not be 0");
+ VERIFY(PPAttribs.f2ShadowMapTexelSize.x != 0 && PPAttribs.f2ShadowMapTexelSize.y != 0, "Shadow map texel size must not be 0");
+ VERIFY(PPAttribs.uiMaxSamplesOnTheRay != 0, "Max samples on the ray must not be 0");
+ VERIFY(PPAttribs.uiMinMaxShadowMapResolution != 0, "Minmax shadow map resolution must not be 0");
+ VERIFY(PPAttribs.iNumCascades != 0, "Num cascades must not be 0");
+ VERIFY(PPAttribs.iFirstCascadeToRayMarch < PPAttribs.iNumCascades, "First cascade to ray march (", PPAttribs.fFirstCascadeToRayMarch, ") is invalid");
+ VERIFY(PPAttribs.fMaxShadowMapStep != 0, "Max shadow map step must not be 0");
+ // clang-format off
+ VERIFY(PPAttribs.iLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING ||
+ PPAttribs.iLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE,
+ "Incorrect light scattering technique (", PPAttribs.iLightSctrTechnique, ")");
+ VERIFY(PPAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_SINGLE_PASS ||
+ PPAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS ||
+ PPAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS_INST,
+ "Incorrect cascade processing mode (", PPAttribs.iCascadeProcessingMode, ")");
+ VERIFY(PPAttribs.iRefinementCriterion == REFINEMENT_CRITERION_DEPTH_DIFF ||
+ PPAttribs.iRefinementCriterion == REFINEMENT_CRITERION_INSCTR_DIFF,
+ "Incorrect refinement criterion (", PPAttribs.iRefinementCriterion, ")");
+ VERIFY(PPAttribs.iSingleScatteringMode == SINGLE_SCTR_MODE_NONE ||
+ PPAttribs.iSingleScatteringMode == SINGLE_SCTR_MODE_INTEGRATION ||
+ PPAttribs.iSingleScatteringMode == SINGLE_SCTR_MODE_LUT,
+ "Incorrect single scattering mode (", PPAttribs.iSingleScatteringMode, ")");
+ VERIFY(PPAttribs.iMultipleScatteringMode == MULTIPLE_SCTR_MODE_NONE ||
+ PPAttribs.iMultipleScatteringMode == MULTIPLE_SCTR_MODE_UNOCCLUDED ||
+ PPAttribs.iMultipleScatteringMode == MULTIPLE_SCTR_MODE_OCCLUDED,
+ "Incorrect multiple scattering mode (", PPAttribs.iMultipleScatteringMode, ")");
+ VERIFY(PPAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_PER_PIXEL ||
+ PPAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR,
+ "Incorrect extinction evaluation mode (", PPAttribs.iExtinctionEvalMode, ")");
+
+ Uint32 StalePSODependencyFlags = 0;
+#define CHECK_PSO_DEPENDENCY(Flag, Member)StalePSODependencyFlags |= (PPAttribs.Member != m_PostProcessingAttribs.Member) ? Flag : 0
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_INITIAL_SAMPLE_STEP, uiInitialSampleStepInSlice);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_EPIPOLE_SAMPLING_DENSITY, uiEpipoleSamplingDensityFactor);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_CORRECT_SCATTERING, bCorrectScatteringAtDepthBreaks);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_OPTIMIZE_SAMPLE_LOCATIONS, bOptimizeSampleLocations);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_ENABLE_LIGHT_SHAFTS, bEnableLightShafts);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_USE_1D_MIN_MAX_TREE, bUse1DMinMaxTree);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_LIGHT_SCTR_TECHNIQUE, iLightSctrTechnique);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_CASCADE_PROCESSING_MODE, iCascadeProcessingMode);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_REFINEMENT_CRITERION, iRefinementCriterion);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_IS_32_BIT_MIN_MAX_TREE, bIs32BitMinMaxMipMap);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_MULTIPLE_SCATTERING_MODE, iMultipleScatteringMode);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_SINGLE_SCATTERING_MODE, iSingleScatteringMode);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_AUTO_EXPOSURE, ToneMapping.bAutoExposure);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_TONE_MAPPING_MODE, ToneMapping.iToneMappingMode);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_LIGHT_ADAPTATION, ToneMapping.bLightAdaptation);
+ CHECK_PSO_DEPENDENCY(PSO_DEPENDENCY_EXTINCTION_EVAL_MODE, iExtinctionEvalMode);
+#undef CHECK_PSO_DEPENDENCY
+
+ bool bUseCombinedMinMaxTexture = PPAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_SINGLE_PASS ||
+ PPAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS_INST ||
+ PPAttribs.bCorrectScatteringAtDepthBreaks ||
+ PPAttribs.iLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE;
+ StalePSODependencyFlags |= (m_bUseCombinedMinMaxTexture != bUseCombinedMinMaxTexture) ? PSO_DEPENDENCY_USE_COMBINED_MIN_MAX_TEX : 0;
+
+ auto* pcbCameraAttribs = frameAttribs.pcbCameraAttribs != nullptr ? frameAttribs.pcbCameraAttribs : m_pcbCameraAttribs;
+ auto* pcbLightAttribs = frameAttribs.pcbLightAttribs != nullptr ? frameAttribs.pcbLightAttribs : m_pcbLightAttribs;
+
+ UserResourceIds NewUserResourceIds;
+ NewUserResourceIds.LightAttribs = pcbCameraAttribs != nullptr ? pcbCameraAttribs->GetUniqueID() : -1;
+ NewUserResourceIds.CameraAttribs = pcbLightAttribs != nullptr ? pcbLightAttribs->GetUniqueID() : -1;
+ NewUserResourceIds.SrcColorBufferSRV = frameAttribs.ptex2DSrcColorBufferSRV->GetUniqueID();
+ NewUserResourceIds.SrcColorBufferRTV = frameAttribs.ptex2DSrcColorBufferRTV->GetUniqueID();
+ NewUserResourceIds.SrcDepthBufferDSV = frameAttribs.ptex2DSrcDepthBufferDSV->GetUniqueID();
+ NewUserResourceIds.SrcDepthBufferSRV = frameAttribs.ptex2DSrcDepthBufferSRV->GetUniqueID();
+ NewUserResourceIds.ShadowMapSRV = frameAttribs.ptex2DShadowMapSRV->GetUniqueID();
+ // clang-format on
+
+ Uint32 StaleSRBDependencyFlags = 0;
+#define CHECK_SRB_DEPENDENCY(Flag, Member) StaleSRBDependencyFlags |= (m_UserResourceIds.Member != NewUserResourceIds.Member) ? Flag : 0
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SRC_COLOR_BUFFER, SrcColorBufferSRV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SRC_COLOR_BUFFER, SrcColorBufferRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SRC_DEPTH_BUFFER, SrcDepthBufferDSV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SRC_DEPTH_BUFFER, SrcDepthBufferSRV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SHADOW_MAP, ShadowMapSRV);
+#undef CHECK_SRB_DEPENDENCY
+
+ StaleSRBDependencyFlags |= (!pcbCameraAttribs || m_UserResourceIds.CameraAttribs != NewUserResourceIds.CameraAttribs) ? SRB_DEPENDENCY_CAMERA_ATTRIBS : 0;
+ StaleSRBDependencyFlags |= (!pcbLightAttribs || m_UserResourceIds.LightAttribs != NewUserResourceIds.LightAttribs) ? SRB_DEPENDENCY_LIGHT_ATTRIBS : 0;
+
+ if (PPAttribs.uiNumEpipolarSlices != m_PostProcessingAttribs.uiNumEpipolarSlices ||
+ PPAttribs.uiMaxSamplesInSlice != m_PostProcessingAttribs.uiMaxSamplesInSlice)
+ {
+ m_ptex2DCoordinateTextureRTV.Release(); // Max Samples X Num Slices RG32F
+ m_ptex2DEpipolarCamSpaceZRTV.Release(); // Max Samples X Num Slices R32F
+ m_ptex2DEpipolarInscatteringRTV.Release(); // Max Samples X Num Slices RGBA16F
+ m_ptex2DEpipolarExtinctionRTV.Release(); // Max Samples X Num Slices RGBA8_UNORM
+ m_ptex2DEpipolarImageDSV.Release(); // Max Samples X Num Slices D24S8
+ m_ptex2DInitialScatteredLightRTV.Release(); // Max Samples X Num Slices RGBA16F
+ StaleSRBDependencyFlags |= SRB_DEPENDENCY_INTERPOLATION_SOURCE_TEX;
+ }
+
+ if (PPAttribs.uiNumEpipolarSlices != m_PostProcessingAttribs.uiNumEpipolarSlices)
+ {
+ m_ptex2DSliceEndpointsRTV.Release(); // Num Slices X 1 RGBA32F
+ }
+
+ if (PPAttribs.uiNumEpipolarSlices != m_PostProcessingAttribs.uiNumEpipolarSlices ||
+ PPAttribs.iNumCascades != m_PostProcessingAttribs.iNumCascades)
+ {
+ m_ptex2DSliceUVDirAndOriginRTV.Release(); // Num Slices X Num Cascaes RGBA32F
+ }
+
+ // clang-format off
+ if (PPAttribs.uiMinMaxShadowMapResolution != m_PostProcessingAttribs.uiMinMaxShadowMapResolution ||
+ PPAttribs.uiNumEpipolarSlices != m_PostProcessingAttribs.uiNumEpipolarSlices ||
+ PPAttribs.bUse1DMinMaxTree != m_PostProcessingAttribs.bUse1DMinMaxTree ||
+ PPAttribs.bIs32BitMinMaxMipMap != m_PostProcessingAttribs.bIs32BitMinMaxMipMap ||
+ bUseCombinedMinMaxTexture != m_bUseCombinedMinMaxTexture ||
+ (bUseCombinedMinMaxTexture &&
+ (PPAttribs.iFirstCascadeToRayMarch != m_PostProcessingAttribs.iFirstCascadeToRayMarch ||
+ PPAttribs.iNumCascades != m_PostProcessingAttribs.iNumCascades)))
+ {
+ for (int i = 0; i < _countof(m_ptex2DMinMaxShadowMapSRV); ++i)
+ m_ptex2DMinMaxShadowMapSRV[i].Release();
+ for (int i = 0; i < _countof(m_ptex2DMinMaxShadowMapRTV); ++i)
+ m_ptex2DMinMaxShadowMapRTV[i].Release();
+ }
+
+#define CHECK_SRB_DEPENDENCY(Flag, Res)StaleSRBDependencyFlags |= !Res ? Flag : 0
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_COORDINATE_TEX, m_ptex2DCoordinateTextureRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SLICE_END_POINTS_TEX, m_ptex2DSliceEndpointsRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_EPIPOLAR_CAM_SPACE_Z_TEX, m_ptex2DEpipolarCamSpaceZRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_EPIPOLAR_INSCTR_TEX, m_ptex2DEpipolarInscatteringRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_EPIPOLAR_EXTINCTION_TEX, m_ptex2DEpipolarExtinctionRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_EPIPOLAR_IMAGE_DEPTH, m_ptex2DEpipolarImageDSV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_INITIAL_SCTR_LIGHT_TEX, m_ptex2DInitialScatteredLightRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_AVERAGE_LUMINANCE_TEX, m_ptex2DAverageLuminanceRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_SLICE_UV_DIR_TEX, m_ptex2DSliceUVDirAndOriginRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_CAM_SPACE_Z_TEX, m_ptex2DCamSpaceZRTV);
+ CHECK_SRB_DEPENDENCY(SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP, m_ptex2DMinMaxShadowMapRTV[0]);
+#undef CHECK_SRB_DEPENDENCY
+ // clang-format on
+
+ for (int i = 0; i < RENDER_TECH_TOTAL_TECHNIQUES; ++i)
+ m_RenderTech[i].CheckStaleFlags(StalePSODependencyFlags, StaleSRBDependencyFlags);
+
+ if (StaleSRBDependencyFlags & SRB_DEPENDENCY_SRC_COLOR_BUFFER)
+ m_pResMapping->AddResource("g_tex2DColorBuffer", frameAttribs.ptex2DSrcColorBufferSRV, false);
+
+ if (StaleSRBDependencyFlags & SRB_DEPENDENCY_MIN_MAX_SHADOW_MAP)
+ {
+ m_pComputeMinMaxSMLevelSRB[0].Release();
+ m_pComputeMinMaxSMLevelSRB[1].Release();
+ }
+
+ //if (PPAttribs.uiExtinctionEvalMode != m_PostProcessingAttribs.uiExtinctionEvalMode)
+ //{
+ // m_ptex2DEpipolarExtinctionRTV.Release();
+ //}
+
+ // clang-format off
+ bool bRecomputeSctrCoeffs = m_PostProcessingAttribs.bUseCustomSctrCoeffs != PPAttribs.bUseCustomSctrCoeffs ||
+ m_PostProcessingAttribs.fAerosolDensityScale != PPAttribs.fAerosolDensityScale ||
+ m_PostProcessingAttribs.fAerosolAbsorbtionScale != PPAttribs.fAerosolAbsorbtionScale ||
+ (PPAttribs.bUseCustomSctrCoeffs &&
+ (m_PostProcessingAttribs.f4CustomRlghBeta != PPAttribs.f4CustomRlghBeta ||
+ m_PostProcessingAttribs.f4CustomMieBeta != PPAttribs.f4CustomMieBeta) );
+ // clang-format on
+
+ m_PostProcessingAttribs = PPAttribs;
+
+ m_PostProcessingAttribs.f4ScreenResolution = float4(
+ static_cast<float>(m_uiBackBufferWidth),
+ static_cast<float>(m_uiBackBufferHeight),
+ 1.f / static_cast<float>(m_uiBackBufferWidth),
+ 1.f / static_cast<float>(m_uiBackBufferHeight));
+
+ auto mCameraViewProj = frameAttribs.pCameraAttribs->mViewProjT.Transpose();
+ float4 f4LightPosPS = -frameAttribs.pLightAttribs->f4Direction * mCameraViewProj;
+ f4LightPosPS.x /= f4LightPosPS.w;
+ f4LightPosPS.y /= f4LightPosPS.w;
+ f4LightPosPS.z /= f4LightPosPS.w;
+ float fDistToLightOnScreen = length((float2&)f4LightPosPS);
+ float fMaxDist = 100;
+ if (fDistToLightOnScreen > fMaxDist)
+ {
+ f4LightPosPS.x *= fMaxDist / fDistToLightOnScreen;
+ f4LightPosPS.y *= fMaxDist / fDistToLightOnScreen;
+ }
+ m_PostProcessingAttribs.f4LightScreenPos = f4LightPosPS;
+
+ // Note that in fact the outermost visible screen pixels do not land exactly on the boundary (+1 or -1), but are biased by
+ // 0.5 screen pixel size inwards. Using these adjusted boundaries improves precision and results in
+ // smaller number of pixels which require inscattering correction
+ m_PostProcessingAttribs.bIsLightOnScreen =
+ (fabs(m_PostProcessingAttribs.f4LightScreenPos.x) <= 1.f - 1.f / (float)m_uiBackBufferWidth &&
+ fabs(m_PostProcessingAttribs.f4LightScreenPos.y) <= 1.f - 1.f / (float)m_uiBackBufferHeight);
+
+ m_PostProcessingAttribs.fFirstCascadeToRayMarch = static_cast<float>(m_PostProcessingAttribs.iFirstCascadeToRayMarch);
+ m_PostProcessingAttribs.fNumCascades = static_cast<float>(m_PostProcessingAttribs.iNumCascades);
+
+ m_bUseCombinedMinMaxTexture = bUseCombinedMinMaxTexture;
+
+ m_FrameAttribs = frameAttribs;
+ m_FrameAttribs.pcbCameraAttribs = pcbCameraAttribs;
+ m_FrameAttribs.pcbLightAttribs = pcbLightAttribs;
+ m_UserResourceIds = NewUserResourceIds;
+
+ if (frameAttribs.pcbCameraAttribs == nullptr)
+ {
+ if (!m_pcbCameraAttribs)
+ {
+ CreateUniformBuffer(m_FrameAttribs.pDevice, sizeof(CameraAttribs), "Camera attribs", &m_pcbCameraAttribs);
+ VERIFY_EXPR(StaleSRBDependencyFlags & SRB_DEPENDENCY_CAMERA_ATTRIBS);
+ }
+ MapHelper<CameraAttribs> CamAttribs(m_FrameAttribs.pDeviceContext, m_pcbCameraAttribs, MAP_WRITE, MAP_FLAG_DISCARD);
+ *CamAttribs = *m_FrameAttribs.pCameraAttribs;
+
+ m_FrameAttribs.pcbCameraAttribs = m_pcbCameraAttribs;
+ }
+
+ if (frameAttribs.pcbLightAttribs == nullptr)
+ {
+ if (!m_pcbLightAttribs)
+ {
+ CreateUniformBuffer(m_FrameAttribs.pDevice, sizeof(LightAttribs), "Light attribs", &m_pcbLightAttribs);
+ VERIFY_EXPR(StaleSRBDependencyFlags & SRB_DEPENDENCY_LIGHT_ATTRIBS);
+ }
+ MapHelper<LightAttribs> LightAttribs(m_FrameAttribs.pDeviceContext, m_pcbLightAttribs, MAP_WRITE, MAP_FLAG_DISCARD);
+ *LightAttribs = *m_FrameAttribs.pLightAttribs;
+
+ m_FrameAttribs.pcbLightAttribs = m_pcbLightAttribs;
+ }
+
+ if (bRecomputeSctrCoeffs)
+ {
+ m_uiUpToDateResourceFlags &= ~UpToDateResourceFlags::PrecomputedOpticalDepthTex;
+ m_uiUpToDateResourceFlags &= ~UpToDateResourceFlags::AmbientSkyLightTex;
+ m_uiUpToDateResourceFlags &= ~UpToDateResourceFlags::PrecomputedIntegralsTex;
+ ComputeScatteringCoefficients(m_FrameAttribs.pDeviceContext);
+ }
+
+ if (!m_ptex2DCoordinateTextureRTV)
+ {
+ CreateEpipolarTextures(m_FrameAttribs.pDevice);
+ }
+
+ if (!m_ptex2DSliceEndpointsRTV)
+ {
+ CreateSliceEndPointsTexture(m_FrameAttribs.pDevice);
+ }
+
+ if (!m_ptex2DCamSpaceZRTV)
+ {
+ CreateCamSpaceZTexture(m_FrameAttribs.pDevice);
+ }
+
+ if (m_PostProcessingAttribs.bEnableLightShafts && m_PostProcessingAttribs.bUse1DMinMaxTree && !m_ptex2DMinMaxShadowMapSRV[0])
+ {
+ CreateMinMaxShadowMap(m_FrameAttribs.pDevice);
+ }
+
+ {
+ MapHelper<EpipolarLightScatteringAttribs> pPPAttribsBuffData(m_FrameAttribs.pDeviceContext, m_pcbPostProcessingAttribs, MAP_WRITE, MAP_FLAG_DISCARD);
+ memcpy(pPPAttribsBuffData, &m_PostProcessingAttribs, sizeof(m_PostProcessingAttribs));
+ }
+
+
+ if (!(m_uiUpToDateResourceFlags & UpToDateResourceFlags::PrecomputedOpticalDepthTex))
+ {
+ PrecomputeOpticalDepthTexture(m_FrameAttribs.pDevice, m_FrameAttribs.pDeviceContext);
+ }
+
+
+ if ((m_PostProcessingAttribs.iMultipleScatteringMode > MULTIPLE_SCTR_MODE_NONE ||
+ PPAttribs.iSingleScatteringMode == SINGLE_SCTR_MODE_LUT) &&
+ !(m_uiUpToDateResourceFlags & UpToDateResourceFlags::PrecomputedIntegralsTex))
+ {
+ PrecomputeScatteringLUT(m_FrameAttribs.pDevice, m_FrameAttribs.pDeviceContext);
+ }
+
+ if (/*m_PostProcessingAttribs.ToneMapping.bAutoExposure &&*/ !m_ptex2DLowResLuminanceRTV)
+ {
+ CreateLowResLuminanceTexture(m_FrameAttribs.pDevice, m_FrameAttribs.pDeviceContext);
+ }
+
+ // clang-format off
+ //m_pResMapping->AddResource("g_tex2DDepthBuffer", FrameAttribs.ptex2DSrcDepthBufferSRV, false);
+ //m_pResMapping->AddResource("g_tex2DColorBuffer", FrameAttribs.ptex2DSrcColorBufferSRV, false);
+ m_pResMapping->AddResource("g_tex2DLightSpaceDepthMap", m_FrameAttribs.ptex2DShadowMapSRV, false);
+ m_pResMapping->AddResource("cbCameraAttribs", m_FrameAttribs.pcbCameraAttribs, false);
+ m_pResMapping->AddResource("cbLightParams", m_FrameAttribs.pcbLightAttribs, false);
+ // clang-format on
+
+ {
+ ITextureView* pRTVs[] = {m_FrameAttribs.ptex2DSrcColorBufferRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(_countof(pRTVs), pRTVs, m_FrameAttribs.ptex2DSrcDepthBufferDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ RenderSun();
+ }
+
+ ReconstructCameraSpaceZ();
+
+ if (m_PostProcessingAttribs.iLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_EPIPOLAR_SAMPLING)
+ {
+ RenderSliceEndpoints();
+
+ // Render coordinate texture and camera space z for epipolar location
+ RenderCoordinateTexture();
+
+ if (m_PostProcessingAttribs.iRefinementCriterion == REFINEMENT_CRITERION_INSCTR_DIFF ||
+ m_PostProcessingAttribs.iExtinctionEvalMode == EXTINCTION_EVAL_MODE_EPIPOLAR)
+ {
+ RenderCoarseUnshadowedInctr();
+ }
+
+ // Refine initial ray marching samples
+ RefineSampleLocations();
+
+ // Mark all ray marching samples in stencil
+ MarkRayMarchingSamples();
+
+ if (m_PostProcessingAttribs.bEnableLightShafts && m_PostProcessingAttribs.bUse1DMinMaxTree)
+ {
+ RenderSliceUVDirAndOrig();
+ }
+
+ ITextureView* ppRTVs[] = {m_ptex2DInitialScatteredLightRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(1, ppRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ const float Zero[] = {0, 0, 0, 0};
+ m_FrameAttribs.pDeviceContext->ClearRenderTarget(m_ptex2DInitialScatteredLightRTV, Zero, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ int iLastCascade = (m_PostProcessingAttribs.bEnableLightShafts && m_PostProcessingAttribs.iCascadeProcessingMode == CASCADE_PROCESSING_MODE_MULTI_PASS) ? m_PostProcessingAttribs.iNumCascades - 1 : m_PostProcessingAttribs.iFirstCascadeToRayMarch;
+ for (int iCascadeInd = m_PostProcessingAttribs.iFirstCascadeToRayMarch; iCascadeInd <= iLastCascade; ++iCascadeInd)
+ {
+ // Build min/max mip map
+ if (m_PostProcessingAttribs.bEnableLightShafts && m_PostProcessingAttribs.bUse1DMinMaxTree)
+ {
+ Build1DMinMaxMipMap(iCascadeInd);
+ }
+ // Perform ray marching for selected samples
+ DoRayMarching(m_PostProcessingAttribs.uiMaxSamplesOnTheRay, iCascadeInd);
+ }
+
+ // Interpolate ray marching samples onto the rest of samples
+ InterpolateInsctrIrradiance();
+
+ const Uint32 uiMaxStepsAlongRayAtDepthBreak0 = std::min(m_PostProcessingAttribs.uiMaxSamplesOnTheRay / 4, 256u);
+ //const Uint32 uiMaxStepsAlongRayAtDepthBreak1 = std::min(m_PostProcessingAttribs.uiMaxSamplesOnTheRay/8, 128u);
+
+ if (m_PostProcessingAttribs.ToneMapping.bAutoExposure)
+ {
+ // Render scene luminance to low-resolution texture
+ ITextureView* pRTVs[] = {m_ptex2DLowResLuminanceRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(_countof(pRTVs), pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ UnwarpEpipolarScattering(true);
+ m_FrameAttribs.pDeviceContext->GenerateMips(m_ptex2DLowResLuminanceSRV);
+
+ UpdateAverageLuminance();
+ }
+ // Set the main back & depth buffers
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ // Clear depth to 1.0.
+ m_FrameAttribs.pDeviceContext->ClearDepthStencil(nullptr, CLEAR_DEPTH_FLAG, 1.f, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ // Transform inscattering irradiance from epipolar coordinates back to rectangular
+ // The shader will write 0.0 to the depth buffer, but all pixel that require inscattering
+ // correction will be discarded and will keep 1.0
+ UnwarpEpipolarScattering(false);
+
+ // Correct inscattering for pixels, for which no suitable interpolation sources were found
+ if (m_PostProcessingAttribs.bCorrectScatteringAtDepthBreaks)
+ {
+ FixInscatteringAtDepthBreaks(uiMaxStepsAlongRayAtDepthBreak0, EFixInscatteringMode::FixInscattering);
+ }
+
+ if (m_PostProcessingAttribs.bShowSampling)
+ {
+ RenderSampleLocations();
+ }
+ }
+ else if (m_PostProcessingAttribs.iLightSctrTechnique == LIGHT_SCTR_TECHNIQUE_BRUTE_FORCE)
+ {
+ if (m_PostProcessingAttribs.ToneMapping.bAutoExposure)
+ {
+ // Render scene luminance to low-resolution texture
+ ITextureView* pRTVs[] = {m_ptex2DLowResLuminanceRTV};
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(_countof(pRTVs), pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ FixInscatteringAtDepthBreaks(m_PostProcessingAttribs.uiMaxSamplesOnTheRay, EFixInscatteringMode::LuminanceOnly);
+ m_FrameAttribs.pDeviceContext->GenerateMips(m_ptex2DLowResLuminanceSRV);
+
+ UpdateAverageLuminance();
+ }
+
+ // Set the main back & depth buffers
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ FixInscatteringAtDepthBreaks(m_PostProcessingAttribs.uiMaxSamplesOnTheRay, EFixInscatteringMode::FullScreenRayMarching);
+ }
+
+ m_FrameAttribs.pDeviceContext->SetRenderTargets(0, nullptr, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+}
+
+
+void EpipolarLightScattering ::CreateMinMaxShadowMap(IRenderDevice* pDevice)
+{
+ TextureDesc MinMaxShadowMapTexDesc;
+ MinMaxShadowMapTexDesc.Type = RESOURCE_DIM_TEX_2D;
+ MinMaxShadowMapTexDesc.Width = m_PostProcessingAttribs.uiMinMaxShadowMapResolution;
+ MinMaxShadowMapTexDesc.Height = m_PostProcessingAttribs.uiNumEpipolarSlices;
+ MinMaxShadowMapTexDesc.MipLevels = 1;
+ MinMaxShadowMapTexDesc.Format = m_PostProcessingAttribs.bIs32BitMinMaxMipMap ? TEX_FORMAT_RG32_FLOAT : TEX_FORMAT_RG16_UNORM;
+ MinMaxShadowMapTexDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_RENDER_TARGET;
+
+ if (m_bUseCombinedMinMaxTexture)
+ {
+ MinMaxShadowMapTexDesc.Height *= (m_PostProcessingAttribs.iNumCascades - m_PostProcessingAttribs.iFirstCascadeToRayMarch);
+ }
+
+ for (int i = 0; i < 2; ++i)
+ {
+ std::string name = "MinMaxShadowMap";
+ name.push_back('0' + char(i));
+ MinMaxShadowMapTexDesc.Name = name.c_str();
+ m_ptex2DMinMaxShadowMapSRV[i].Release();
+ m_ptex2DMinMaxShadowMapRTV[i].Release();
+ RefCntAutoPtr<ITexture> ptex2DMinMaxShadowMap;
+ // Create 2-D texture, shader resource and target view buffers on the device
+ pDevice->CreateTexture(MinMaxShadowMapTexDesc, nullptr, &ptex2DMinMaxShadowMap);
+ m_ptex2DMinMaxShadowMapSRV[i] = ptex2DMinMaxShadowMap->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE);
+ m_ptex2DMinMaxShadowMapSRV[i]->SetSampler(m_pLinearClampSampler);
+ m_ptex2DMinMaxShadowMapRTV[i] = ptex2DMinMaxShadowMap->GetDefaultView(TEXTURE_VIEW_RENDER_TARGET);
+
+ m_pResMapping->AddResource("g_tex2DMinMaxLightSpaceDepth", m_ptex2DMinMaxShadowMapSRV[0], false);
+ }
+}
+
+
+
+float2 exp(const float2& fX) { return float2(::exp(fX.x), ::exp(fX.y)); }
+float3 exp(const float3& fX) { return float3(::exp(fX.x), ::exp(fX.y), ::exp(fX.z)); }
+
+// fCosChi = Pi/2
+float2 ChapmanOrtho(const float2& f2x)
+{
+ static const float fConst = sqrt(PI_F / 2.f);
+ float2 f2SqrtX = float2(sqrt(f2x.x), sqrt(f2x.y));
+ return fConst * (float2(1.f, 1.f) / (2.f * f2SqrtX) + f2SqrtX);
+}
+
+// |fCosChi| < Pi/2
+float2 f2ChapmanRising(const float2& f2X, float fCosChi)
+{
+ float2 f2ChOrtho = ChapmanOrtho(f2X);
+ return f2ChOrtho / ((f2ChOrtho - float2(1, 1)) * fCosChi + float2(1, 1));
+}
+
+float2 GetDensityIntegralFromChapmanFunc(float fHeightAboveSurface,
+ const float3& f3EarthCentreToPointDir,
+ const float3& f3RayDir,
+ const AirScatteringAttribs& SctrMediaAttribs)
+{
+ // Note: there is no intersection test with the Earth. However,
+ // optical depth through the Earth is large, which effectively
+ // occludes the light
+ float fCosChi = dot(f3EarthCentreToPointDir, f3RayDir);
+ auto f2ParticleScaleHeight = float2(SctrMediaAttribs.f4ParticleScaleHeight.x, SctrMediaAttribs.f4ParticleScaleHeight.y);
+ auto f2ParticleScaleHeightInv = float2(SctrMediaAttribs.f4ParticleScaleHeight.z, SctrMediaAttribs.f4ParticleScaleHeight.w);
+ float2 f2x = (fHeightAboveSurface + SctrMediaAttribs.fEarthRadius) * f2ParticleScaleHeightInv;
+ float2 f2VerticalAirMass = f2ParticleScaleHeight * exp(-float2(fHeightAboveSurface, fHeightAboveSurface) * f2ParticleScaleHeightInv);
+ if (fCosChi >= 0.f)
+ {
+ return f2VerticalAirMass * f2ChapmanRising(f2x, fCosChi);
+ }
+ else
+ {
+ float fSinChi = sqrt(1.f - fCosChi * fCosChi);
+ float fh0 = (fHeightAboveSurface + SctrMediaAttribs.fEarthRadius) * fSinChi - SctrMediaAttribs.fEarthRadius;
+ float2 f2VerticalAirMass0 = f2ParticleScaleHeight * exp(-float2(fh0, fh0) * f2ParticleScaleHeightInv);
+ float2 f2x0 = float2(fh0 + SctrMediaAttribs.fEarthRadius, fh0 + SctrMediaAttribs.fEarthRadius) * f2ParticleScaleHeightInv;
+ float2 f2ChOrtho_x0 = ChapmanOrtho(f2x0);
+ float2 f2Ch = f2ChapmanRising(f2x, -fCosChi);
+ return f2VerticalAirMass0 * (2.f * f2ChOrtho_x0) - f2VerticalAirMass * f2Ch;
+ }
+}
+
+void EpipolarLightScattering ::ComputeSunColor(const float3& vDirectionOnSun,
+ const float4& f4ExtraterrestrialSunColor,
+ float4& f4SunColorAtGround,
+ float4& f4AmbientLight)
+{
+
+ // Compute the ambient light values
+ float zenithFactor = std::min(std::max(vDirectionOnSun.y, 0.0f), 1.0f);
+ f4AmbientLight.x = zenithFactor * 0.15f;
+ f4AmbientLight.y = zenithFactor * 0.1f;
+ f4AmbientLight.z = std::max(0.005f, zenithFactor * 0.25f);
+ f4AmbientLight.w = 0.0f;
+
+ float2 f2NetParticleDensityToAtmTop = GetDensityIntegralFromChapmanFunc(0, float3(0, 1, 0), vDirectionOnSun, m_MediaParams);
+
+
+ float3 f3RlghExtCoeff = std::max((float3&)m_MediaParams.f4RayleighExtinctionCoeff, float3(1e-8f, 1e-8f, 1e-8f));
+ float3 f3RlghOpticalDepth = f3RlghExtCoeff * f2NetParticleDensityToAtmTop.x;
+ float3 f3MieExtCoeff = std::max((float3&)m_MediaParams.f4MieExtinctionCoeff, float3(1e-8f, 1e-8f, 1e-8f));
+ float3 f3MieOpticalDepth = f3MieExtCoeff * f2NetParticleDensityToAtmTop.y;
+ float3 f3TotalExtinction = exp(-(f3RlghOpticalDepth + f3MieOpticalDepth));
+ const float fEarthReflectance = 0.1f; // See [BN08]
+ (float3&)f4SunColorAtGround = ((float3&)f4ExtraterrestrialSunColor) * f3TotalExtinction * fEarthReflectance;
+}
+
+void EpipolarLightScattering ::ComputeScatteringCoefficients(IDeviceContext* pDeviceCtx)
+{
+ // For details, see "A practical Analytic Model for Daylight" by Preetham & Hoffman, p.23
+
+ // Wave lengths
+ // [BN08] follows [REK04] and gives the following values for Rayleigh scattering coefficients:
+ // RayleighBetha(lambda = (680nm, 550nm, 440nm) ) = (5.8, 13.5, 33.1)e-6
+ static const double dWaveLengths[] =
+ {
+ 680e-9, // red
+ 550e-9, // green
+ 440e-9 // blue
+ };
+
+ // Calculate angular and total scattering coefficients for Rayleigh scattering:
+ {
+ float4& f4AngularRayleighSctrCoeff = m_MediaParams.f4AngularRayleighSctrCoeff;
+ float4& f4TotalRayleighSctrCoeff = m_MediaParams.f4TotalRayleighSctrCoeff;
+ float4& f4RayleighExtinctionCoeff = m_MediaParams.f4RayleighExtinctionCoeff;
+
+ constexpr double n = 1.0003; // - Refractive index of air in the visible spectrum
+ constexpr double N = 2.545e+25; // - Number of molecules per unit volume
+ constexpr double Pn = 0.035; // - Depolarization factor for air which exoresses corrections
+ // due to anisotropy of air molecules
+
+ constexpr double dRayleighConst = 8.0 * PI * PI * PI * (n * n - 1.0) * (n * n - 1.0) / (3.0 * N) * (6.0 + 3.0 * Pn) / (6.0 - 7.0 * Pn);
+ for (int WaveNum = 0; WaveNum < 3; WaveNum++)
+ {
+ double dSctrCoeff;
+ if (m_PostProcessingAttribs.bUseCustomSctrCoeffs)
+ dSctrCoeff = f4TotalRayleighSctrCoeff[WaveNum] = m_PostProcessingAttribs.f4CustomRlghBeta[WaveNum];
+ else
+ {
+ double Lambda2 = dWaveLengths[WaveNum] * dWaveLengths[WaveNum];
+ double Lambda4 = Lambda2 * Lambda2;
+ dSctrCoeff = dRayleighConst / Lambda4;
+ // Total Rayleigh scattering coefficient is the integral of angular scattering coefficient in all directions
+ f4TotalRayleighSctrCoeff[WaveNum] = static_cast<float>(dSctrCoeff);
+ }
+ // Angular scattering coefficient is essentially volumetric scattering coefficient multiplied by the
+ // normalized phase function
+ // p(Theta) = 3/(16*Pi) * (1 + cos^2(Theta))
+ // f4AngularRayleighSctrCoeff contains all the terms exepting 1 + cos^2(Theta):
+ f4AngularRayleighSctrCoeff[WaveNum] = static_cast<float>(3.0 / (16.0 * PI) * dSctrCoeff);
+ // f4AngularRayleighSctrCoeff[WaveNum] = f4TotalRayleighSctrCoeff[WaveNum] * p(Theta)
+ }
+ // Air molecules do not absorb light, so extinction coefficient is only caused by out-scattering
+ f4RayleighExtinctionCoeff = f4TotalRayleighSctrCoeff;
+ }
+
+ // Calculate angular and total scattering coefficients for Mie scattering:
+ {
+ float4& f4AngularMieSctrCoeff = m_MediaParams.f4AngularMieSctrCoeff;
+ float4& f4TotalMieSctrCoeff = m_MediaParams.f4TotalMieSctrCoeff;
+ float4& f4MieExtinctionCoeff = m_MediaParams.f4MieExtinctionCoeff;
+
+ if (m_PostProcessingAttribs.bUseCustomSctrCoeffs)
+ {
+ f4TotalMieSctrCoeff = m_PostProcessingAttribs.f4CustomMieBeta * m_PostProcessingAttribs.fAerosolDensityScale;
+ }
+ else
+ {
+ const bool bUsePreethamMethod = false;
+ if (bUsePreethamMethod)
+ {
+ // Values for K came from the table 2 in the "A practical Analytic Model
+ // for Daylight" by Preetham & Hoffman, p.28
+ constexpr double K[] =
+ {
+ 0.68455, // K[650nm]
+ 0.678781, // K[570nm]
+ (0.668532 + 0.669765) / 2.0 // (K[470nm]+K[480nm])/2
+ };
+
+ VERIFY_EXPR(m_MediaParams.fTurbidity >= 1.f);
+
+ // Beta is an Angstrom's turbidity coefficient and is approximated by:
+ //float beta = 0.04608365822050f * m_fTurbidity - 0.04586025928522f; ???????
+
+ const double c = (0.6544 * m_MediaParams.fTurbidity - 0.6510) * 1E-16; // concentration factor
+ constexpr double v = 4; // Junge's exponent
+
+ const double dTotalMieBetaTerm = 0.434 * c * PI * pow(2.0 * PI, v - 2);
+
+ for (int WaveNum = 0; WaveNum < 3; WaveNum++)
+ {
+ double Lambdav_minus_2 = pow(dWaveLengths[WaveNum], v - 2);
+ double dTotalMieSctrCoeff = dTotalMieBetaTerm * K[WaveNum] / Lambdav_minus_2;
+ f4TotalMieSctrCoeff[WaveNum] = static_cast<float>(dTotalMieSctrCoeff);
+ }
+
+ //AtmScatteringAttribs.f4AngularMieSctrCoeff *= 0.02f;
+ //AtmScatteringAttribs.f4TotalMieSctrCoeff *= 0.02f;
+ }
+ else
+ {
+ // [BN08] uses the following value (independent of wavelength) for Mie scattering coefficient: 2e-5
+ // For g=0.76 and MieBetha=2e-5 [BN08] was able to reproduce the same luminance as given by the
+ // reference CIE sky light model
+ const float fMieBethaBN08 = 2e-5f * m_PostProcessingAttribs.fAerosolDensityScale;
+ m_MediaParams.f4TotalMieSctrCoeff = float4(fMieBethaBN08, fMieBethaBN08, fMieBethaBN08, 0);
+ }
+ }
+
+ for (int WaveNum = 0; WaveNum < 3; WaveNum++)
+ {
+ // Normalized to unity Cornette-Shanks phase function has the following form:
+ // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2)
+ // The angular scattering coefficient is the volumetric scattering coefficient multiplied by the phase
+ // function. 1/(4*PI) is baked into the f4AngularMieSctrCoeff, the other terms are baked into f4CS_g
+ f4AngularMieSctrCoeff[WaveNum] = f4TotalMieSctrCoeff[WaveNum] / (4.f * PI_F);
+ // [BN08] also uses slight absorption factor which is 10% of scattering
+ f4MieExtinctionCoeff[WaveNum] = f4TotalMieSctrCoeff[WaveNum] * (1.f + m_PostProcessingAttribs.fAerosolAbsorbtionScale);
+ }
+ }
+
+ {
+ // For g=0.76 and MieBetha=2e-5 [BN08] was able to reproduce the same luminance as is given by the
+ // reference CIE sky light model
+ // Cornette phase function (see Nishita et al. 93):
+ // F(theta) = 1/(4*PI) * 3*(1-g^2) / (2*(2+g^2)) * (1+cos^2(theta)) / (1 + g^2 - 2g*cos(theta))^(3/2)
+ // 1/(4*PI) is baked into the f4AngularMieSctrCoeff
+ float4& f4CS_g = m_MediaParams.f4CS_g;
+ float f_g = m_MediaParams.fAerosolPhaseFuncG;
+ f4CS_g.x = 3 * (1.f - f_g * f_g) / (2 * (2.f + f_g * f_g));
+ f4CS_g.y = 1.f + f_g * f_g;
+ f4CS_g.z = -2.f * f_g;
+ f4CS_g.w = 1.f;
+ }
+
+ m_MediaParams.f4TotalExtinctionCoeff = m_MediaParams.f4RayleighExtinctionCoeff + m_MediaParams.f4MieExtinctionCoeff;
+
+ if (pDeviceCtx && m_pcbMediaAttribs)
+ {
+ pDeviceCtx->UpdateBuffer(m_pcbMediaAttribs, 0, sizeof(m_MediaParams), &m_MediaParams, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+ }
+}
+
+
+void EpipolarLightScattering ::RenderSun()
+{
+ if (m_PostProcessingAttribs.f4LightScreenPos.w <= 0)
+ return;
+
+ auto& RenderSunTech = m_RenderTech[RENDER_TECH_RENDER_SUN];
+ if (!RenderSunTech.PSO)
+ {
+ RefCntAutoPtr<IShader> pSunVS = CreateShader(m_FrameAttribs.pDevice, "Sun.fx", "SunVS", SHADER_TYPE_VERTEX);
+ RefCntAutoPtr<IShader> pSunPS = CreateShader(m_FrameAttribs.pDevice, "Sun.fx", "SunPS", SHADER_TYPE_PIXEL);
+
+ PipelineStateDesc PSODesc;
+ PSODesc.ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_MUTABLE;
+ // clang-format off
+ ShaderResourceVariableDesc Vars[] =
+ {
+ {SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, "cbPostProcessingAttribs", SHADER_RESOURCE_VARIABLE_TYPE_STATIC}
+ };
+ // clang-format on
+ PSODesc.ResourceLayout.Variables = Vars;
+ PSODesc.ResourceLayout.NumVariables = _countof(Vars);
+
+ PSODesc.Name = "Render Sun";
+ auto& GraphicsPipeline = PSODesc.GraphicsPipeline;
+ GraphicsPipeline.RasterizerDesc.FillMode = FILL_MODE_SOLID;
+ GraphicsPipeline.RasterizerDesc.CullMode = CULL_MODE_NONE;
+ GraphicsPipeline.RasterizerDesc.FrontCounterClockwise = true;
+ GraphicsPipeline.DepthStencilDesc = DSS_CmpEqNoWrites;
+ GraphicsPipeline.pVS = pSunVS;
+ GraphicsPipeline.pPS = pSunPS;
+ GraphicsPipeline.NumRenderTargets = 1;
+ GraphicsPipeline.RTVFormats[0] = m_OffscreenBackBufferFmt;
+ GraphicsPipeline.DSVFormat = m_DepthBufferFmt;
+ GraphicsPipeline.PrimitiveTopology = PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP;
+ m_FrameAttribs.pDevice->CreatePipelineState(PSODesc, &RenderSunTech.PSO);
+ RenderSunTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+
+ RenderSunTech.SRBDependencyFlags = SRB_DEPENDENCY_CAMERA_ATTRIBS;
+ }
+
+ if (!RenderSunTech.SRB)
+ {
+ RenderSunTech.PSO->CreateShaderResourceBinding(&RenderSunTech.SRB, true);
+ RenderSunTech.SRB->BindResources(SHADER_TYPE_PIXEL | SHADER_TYPE_VERTEX, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ }
+ m_FrameAttribs.pDeviceContext->SetPipelineState(RenderSunTech.PSO);
+ m_FrameAttribs.pDeviceContext->CommitShaderResources(RenderSunTech.SRB, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ DrawAttribs DrawAttrs;
+ DrawAttrs.NumVertices = 4;
+ m_FrameAttribs.pDeviceContext->Draw(DrawAttrs);
+}
+
+void EpipolarLightScattering ::ComputeAmbientSkyLightTexture(IRenderDevice* pDevice, IDeviceContext* pContext)
+{
+ if (!(m_uiUpToDateResourceFlags & UpToDateResourceFlags::PrecomputedOpticalDepthTex))
+ {
+ PrecomputeOpticalDepthTexture(pDevice, pContext);
+ }
+
+ if (!(m_uiUpToDateResourceFlags & UpToDateResourceFlags::PrecomputedIntegralsTex))
+ {
+ PrecomputeScatteringLUT(pDevice, pContext);
+ }
+
+ auto& PrecomputeAmbientSkyLightTech = m_RenderTech[RENDER_TECH_PRECOMPUTE_AMBIENT_SKY_LIGHT];
+ if (!PrecomputeAmbientSkyLightTech.PSO)
+ {
+ ShaderMacroHelper Macros;
+ Macros.AddShaderMacro("NUM_RANDOM_SPHERE_SAMPLES", static_cast<Int32>(m_uiNumRandomSamplesOnSphere));
+ Macros.Finalize();
+ auto pPrecomputeAmbientSkyLightPS = CreateShader(pDevice, "PrecomputeAmbientSkyLight.fx", "PrecomputeAmbientSkyLightPS",
+ SHADER_TYPE_PIXEL, Macros);
+
+ PipelineResourceLayoutDesc ResourceLayout;
+ ResourceLayout.DefaultVariableType = SHADER_RESOURCE_VARIABLE_TYPE_STATIC;
+ PrecomputeAmbientSkyLightTech.InitializeFullScreenTriangleTechnique(pDevice, "PrecomputeAmbientSkyLight",
+ m_pFullScreenTriangleVS, pPrecomputeAmbientSkyLightPS,
+ ResourceLayout, AmbientSkyLightTexFmt);
+ PrecomputeAmbientSkyLightTech.PSO->BindStaticResources(SHADER_TYPE_VERTEX | SHADER_TYPE_PIXEL, m_pResMapping, BIND_SHADER_RESOURCES_VERIFY_ALL_RESOLVED);
+ }
+
+ // Create 2-D texture, shader resource and target view buffers on the device
+ ITextureView* pRTVs[] = {m_ptex2DAmbientSkyLightRTV};
+ pContext->SetRenderTargets(1, pRTVs, nullptr, RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
+
+ PrecomputeAmbientSkyLightTech.PrepareSRB(pDevice, m_pResMapping);
+ PrecomputeAmbientSkyLightTech.Render(pContext);
+ m_uiUpToDateResourceFlags |= UpToDateResourceFlags::AmbientSkyLightTex;
+}
+
+
+ITextureView* EpipolarLightScattering ::GetAmbientSkyLightSRV(IRenderDevice* pDevice, IDeviceContext* pContext)
+{
+ if (!(m_uiUpToDateResourceFlags & UpToDateResourceFlags::AmbientSkyLightTex))
+ {
+ ComputeAmbientSkyLightTexture(pDevice, pContext);
+ }
+
+ return m_ptex2DAmbientSkyLightSRV;
+}
+
+} // namespace Diligent