diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2019-07-24 05:16:34 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2019-07-24 05:16:34 +0000 |
| commit | bf756bb54d1b87efda441fe593d9cc7c9fc4854f (patch) | |
| tree | 38746eeed2e46443a92c149791815e41f1dcc425 /Components | |
| parent | Shadows.fxh: using SampleCmpLevelZero in HLSL and SampleCmp in GLSL (diff) | |
| download | DiligentFX-bf756bb54d1b87efda441fe593d9cc7c9fc4854f.tar.gz DiligentFX-bf756bb54d1b87efda441fe593d9cc7c9fc4854f.zip | |
Updated Shadow map manager & added readme
Diffstat (limited to 'Components')
| -rw-r--r-- | Components/README.md | 156 | ||||
| -rw-r--r-- | Components/interface/ShadowMapManager.h | 42 | ||||
| -rw-r--r-- | Components/media/Powerplant-Shadows.jpg | bin | 0 -> 188043 bytes | |||
| -rw-r--r-- | Components/src/ShadowMapManager.cpp | 19 |
4 files changed, 198 insertions, 19 deletions
diff --git a/Components/README.md b/Components/README.md new file mode 100644 index 0000000..75173ef --- /dev/null +++ b/Components/README.md @@ -0,0 +1,156 @@ +# Rendering Components + +Rendering components are designed to be plug-and-play blocks of functionaliy that can be used by applications. + +## Shadows + + + +The shadowing component implements the following BKMs: + +- Cascaded shadow maps with cascade stabilization +- Optimizied fixed-size PCF or world-sized PCF kernels +- Variance shadow maps +- Two and four-component eponential variance shadow maps +- Best cascade search based on projection into light space +- Filtering across cascades +- Various artifact removal techniques + +### Integrating shadows into application + +The component is implemented by the following source files: + +- [ShadowMapManager.h](interface/ShadowMapManager.h)/[ShadowMapManager.cpp](src/ShadowMapManager.cpp) - implementation of the shadow map manager. +- [Shadows.fxh](../Shaders/Common/public/Shadows.fxh) - shader functionality. + +#### Initialization +The shadow map manager is responsible for creating required textures and views, cascade partitioning, converting shadow map to filterable +representations (VSM/EVSM) etc. + +To initialize the manager, prepare `ShadowMapManager::InitInfo` structure that defines initialization parameters +and call `ShadowMapManager::Initialize`, for example: + +```cpp +ShadowMapManager::InitInfo SMMgrInitInfo; +SMMgrInitInfo.Format = TEX_FORMAT_D16_UNORM; +SMMgrInitInfo.Resolution = 1024; +SMMgrInitInfo.NumCascades = 4; +SMMgrInitInfo.ShadowMode = SHADOW_MODE_PCF; +SMMgrInitInfo.pComparisonSampler = m_pComparisonSampler; +m_ShadowMapMgr.Initialize(m_pDevice, SMMgrInitInfo); +``` + +Most of the fields of `ShadowMapManager::InitInfo` structure are self-explanatory. `pComparisonSampler` defines +optional texture sample to be set in the shadow map resource view. If the sampler is null, the application is responsible +for setting appropriate sampler before using the shadow map in the shader. + +#### Cascade Partitioning + +To distribute shadow map cascades, populate `ShadowMapManager::DistributeCascadeInfo` that defines partitioning +parameters and call `ShadowMapManager::DistributeCascades`: + +```cpp +ShadowMapManager::DistributeCascadeInfo DistrInfo; +DistrInfo.pCameraView = &m_Camera.GetViewMatrix(); +DistrInfo.pCameraProj = &m_Camera.GetProjMatrix(); +DistrInfo.pLightDir = &m_f3LightDirection; +DistrInfo.fPartitioningFactor = 0.95f; +m_ShadowMapMgr.DistributeCascades(DistrInfo, m_LightAttribs.ShadowAttribs); +``` + +`fPartitioningFactor` member defines the ratio between fully linear (0.0) and +fully logarithmic (1.0) partitioning. The method populates the `ShadowMapAttribs` structure that +is part of the `LightAttribs` structure and should be made available to a shader via constant buffer. + +#### Rendering Shadow Cascades + +After cascades are distributed, use `ShadowMapManager::GetCascadeTranform` method to access +transform matrices and render every cascade: + +```cpp +auto iNumShadowCascades = m_LightAttribs.ShadowAttribs.iNumCascades; +for(int iCascade = 0; iCascade < iNumShadowCascades; ++iCascade) +{ + const auto CascadeProjMatr = m_ShadowMapMgr.GetCascadeTranform(iCascade).Proj; + + auto WorldToLightViewSpaceMatr = m_LightAttribs.ShadowAttribs.mWorldToLightViewT.Transpose(); + auto WorldToLightProjSpaceMatr = WorldToLightViewSpaceMatr * CascadeProjMatr; + CameraAttribs ShadowCameraAttribs = {}; + ShadowCameraAttribs.mViewT = m_LightAttribs.ShadowAttribs.mWorldToLightViewT; + ShadowCameraAttribs.mProjT = CascadeProjMatr.Transpose(); + ShadowCameraAttribs.mViewProjT = WorldToLightProjSpaceMatr.Transpose(); + + { + MapHelper<CameraAttribs> CameraData(m_pImmediateContext, m_CameraAttribsCB, MAP_WRITE, MAP_FLAG_DISCARD); + *CameraData = ShadowCameraAttribs; + } + + auto* pCascadeDSV = m_ShadowMapMgr.GetCascadeDSV(iCascade); + m_pImmediateContext->SetRenderTargets(0, nullptr, pCascadeDSV, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + m_pImmediateContext->ClearDepthStencil(pCascadeDSV, CLEAR_DEPTH_FLAG, 1.f, 0, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); + + DrawMesh(m_pImmediateContext); +} +``` + +When using filterable represenations, the shadow map must be post-processed before it can be used in a shader: + +```cpp +if (m_ShadowSettings.iShadowMode > SHADOW_MODE_PCF) + m_ShadowMapMgr.ConvertToFilterable(m_pImmediateContext, m_LightAttribs.ShadowAttribs); +``` + + +#### Rendering with Shadows + +To use shadowing functionality in the shader, include `BasicStructures.fxh` and `Shadows.fxh` files and +depending on the shadowing mode, define shadow map or filterable shadow map textures and corresponding samplers +(note that the names must be different to allow HLSL to GLSL conversion): + +```hlsl +#include "BasicStructures.fxh" +#include "Shadows.fxh" + +#if SHADOW_MODE == SHADOW_MODE_PCF + Texture2DArray<float> g_tex2DShadowMap; + SamplerComparisonState g_tex2DShadowMap_sampler; +#else + Texture2DArray<float4> g_tex2DFilterableShadowMap; + SamplerState g_tex2DFilterableShadowMap_sampler; +#endif +``` + +To filter shadow map, call `FilterShadowMap` or `SampleFilterableShadowMap` functions: + +```hlsl +FilteredShadow Shadow; +#if SHADOW_MODE == SHADOW_MODE_PCF + Shadow = FilterShadowMap(g_LightAttribs.ShadowAttribs, g_tex2DShadowMap, g_tex2DShadowMap_sampler, VSOut.PosInLightViewSpace, VSOut.CameraSpaceZ); +#else + Shadow = SampleFilterableShadowMap(g_LightAttribs.ShadowAttribs, g_tex2DFilterableShadowMap, g_tex2DFilterableShadowMap_sampler, VSOut.PosInLightViewSpace, VSOut.CameraSpaceZ); +#endif +DiffuseIllumination *= Shadow.fLightAmount; +``` + +Shadow filtering mode is controlled by a number of macroses: + +```cpp +ShaderCreateInfo ShaderCI; +ShaderMacroHelper Macros; +Macros.AddShaderMacro( "SHADOW_MODE", m_ShadowSettings.iShadowMode); +Macros.AddShaderMacro( "SHADOW_FILTER_SIZE", m_LightAttribs.ShadowAttribs.iFixedFilterSize); +Macros.AddShaderMacro( "FILTER_ACROSS_CASCADES", m_ShadowSettings.FilterAcrossCascades); +Macros.AddShaderMacro( "BEST_CASCADE_SEARCH", m_ShadowSettings.SearchBestCascade ); +ShaderCI.Macros = Macros; +``` + +[Shadows sample](https://github.com/DiligentGraphics/DiligentSamples/tree/master/Samples/Shadows) gives an example of using shadow component. + +### References + +- [Variance Shadow Maps](http://www.punkuser.net/vsm/) +- [Layered variance shadow maps](http://www.punkuser.net/lvsm/lvsm_web.pdf) +- [Shadow sample update by MJP](https://mynameismjp.wordpress.com/2015/02/18/shadow-sample-update/) +- [MJP's shadows sample source code](https://github.com/TheRealMJP/Shadows) +- [Shadow Explorer sample from Intel](https://software.intel.com/en-us/articles/shadow-explorer-sample) +- [Cascaded Shadow Maps technical article by Microsoft](https://docs.microsoft.com/en-us/windows/win32/dxtecharts/cascaded-shadow-maps) diff --git a/Components/interface/ShadowMapManager.h b/Components/interface/ShadowMapManager.h index 1da35b4..0ca8180 100644 --- a/Components/interface/ShadowMapManager.h +++ b/Components/interface/ShadowMapManager.h @@ -41,15 +41,29 @@ class ShadowMapManager public: ShadowMapManager(); + /// Shadow map manager initialization info struct InitInfo { - TEXTURE_FORMAT Fmt = TEX_FORMAT_UNKNOWN; + /// Shadow map format. This parameter must not be TEX_FORMAT_UNKNOWN. + TEXTURE_FORMAT Format = TEX_FORMAT_UNKNOWN; + + /// Shadow map resolution, must not be 0. Uint32 Resolution = 0; + + /// Number of shadow cascades, must not be 0. Uint32 NumCascades = 0; - ISampler* pComparisonSampler = nullptr; - ISampler* pFilterableShadowMapSampler = nullptr; + + /// Shadow mode (see SHADOW_MODE_* defines in BasicStructures.fxh), must not be 0. int ShadowMode = 0; + + /// Wether to use 32-bit or 16-bit filterable textures bool Is32BitFilterableFmt = false; + + /// Optional comparison sampler to be set in the shadow map resource view + ISampler* pComparisonSampler = nullptr; + + /// Optional sampler to be set in the filterable shadow map representation + ISampler* pFilterableShadowMapSampler = nullptr; }; void Initialize(IRenderDevice* pDevice, const InitInfo& initInfo); @@ -59,23 +73,33 @@ public: struct DistributeCascadeInfo { + /// Pointer to camera view matrix, must not be null. const float4x4* pCameraView = nullptr; + + /// Pointer to camera world matrix. const float4x4* pCameraWorld = nullptr; + + /// Pointer to camera projection matrix, must not be null. const float4x4* pCameraProj = nullptr; - const float3* pCameraPos = nullptr; + + /// Pointer to light direction, must not be null. const float3* pLightDir = nullptr; - // Snap cascades to texels in light view space + /// Wether to snap cascades to texels in light view space bool SnapCascades = true; - // Stabilize cascade extents in light view space + /// Wether to stabilize cascade extents in light view space bool StabilizeExtents = true; - // Use same extents for X and Y axis. Enabled automatically if StabilizeExtents == true + /// Wether to use same extents for X and Y axis. Enabled automatically if StabilizeExtents == true bool EqualizeExtents = true; - // Callback that allows the application to adjust z range of every cascade. - // The callback is also called with cascade value -1 to adjust that entire camera range. + /// Cascade partitioning factor that defines the ratio between fully linear (0.0) and + /// fully logarithmic (1.0) partitioning. + float fPartitioningFactor = 0.95f; + + /// Callback that allows the application to adjust z range of every cascade. + /// The callback is also called with cascade value -1 to adjust that entire camera range. std::function<void(int, float&, float&)> AdjustCascadeRange; }; diff --git a/Components/media/Powerplant-Shadows.jpg b/Components/media/Powerplant-Shadows.jpg Binary files differnew file mode 100644 index 0000000..63f1539 --- /dev/null +++ b/Components/media/Powerplant-Shadows.jpg diff --git a/Components/src/ShadowMapManager.cpp b/Components/src/ShadowMapManager.cpp index 6122822..7c1439a 100644 --- a/Components/src/ShadowMapManager.cpp +++ b/Components/src/ShadowMapManager.cpp @@ -39,10 +39,10 @@ ShadowMapManager::ShadowMapManager() void ShadowMapManager::Initialize(IRenderDevice* pDevice, const InitInfo& initInfo) { VERIFY_EXPR(pDevice != nullptr); - VERIFY(initInfo.Fmt != TEX_FORMAT_UNKNOWN, "Undefined shadow map format"); + VERIFY(initInfo.Format != TEX_FORMAT_UNKNOWN, "Undefined shadow map format"); VERIFY(initInfo.NumCascades != 0, "Number of cascades must not be zero"); - VERIFY(initInfo.Resolution != 0, "Shadow map resolution must not be zero"); - VERIFY(initInfo.ShadowMode != 0, "Shadow mode is not specified"); + VERIFY(initInfo.Resolution != 0, "Shadow map resolution must not be zero"); + VERIFY(initInfo.ShadowMode != 0, "Shadow mode is not specified"); m_pDevice = pDevice; m_ShadowMode = initInfo.ShadowMode; @@ -54,7 +54,7 @@ void ShadowMapManager::Initialize(IRenderDevice* pDevice, const InitInfo& initIn ShadowMapDesc.Height = initInfo.Resolution; ShadowMapDesc.MipLevels = 1; ShadowMapDesc.ArraySize = initInfo.NumCascades; - ShadowMapDesc.Format = initInfo.Fmt; + ShadowMapDesc.Format = initInfo.Format; ShadowMapDesc.BindFlags = BIND_SHADER_RESOURCE | BIND_DEPTH_STENCIL; RefCntAutoPtr<ITexture> ptex2DShadowMap; @@ -129,7 +129,6 @@ void ShadowMapManager::DistributeCascades(const DistributeCascadeInfo& Info, VERIFY(Info.pCameraView, "Camera view matrix must not be null"); VERIFY(Info.pCameraProj, "Camera projection matrix must not be null"); VERIFY(Info.pLightDir, "Light direction must not be null"); - VERIFY(Info.pCameraPos, "Camera position must not be null"); VERIFY(m_pDevice, "Shadow map manager is not initialized"); const auto& DevCaps = m_pDevice->GetDeviceCaps(); @@ -172,7 +171,9 @@ void ShadowMapManager::DistributeCascades(const DistributeCascadeInfo& Info, ShadowAttribs.mWorldToLightViewT = WorldToLightViewSpaceMatr.Transpose(); - float3 f3CameraPosInLightSpace = *Info.pCameraPos * WorldToLightViewSpaceMatr; + const auto& CameraWorld = Info.pCameraWorld != nullptr ? *Info.pCameraWorld : Info.pCameraView->Inverse(); + const float3 f3CameraPos = {CameraWorld._41, CameraWorld._42, CameraWorld._43}; + const float3 f3CameraPosInLightSpace = f3CameraPos * WorldToLightViewSpaceMatr; float fMainCamNearPlane, fMainCamFarPlane; Info.pCameraProj->GetNearFarClipPlanes(fMainCamNearPlane, fMainCamFarPlane, IsGL); @@ -183,9 +184,7 @@ void ShadowMapManager::DistributeCascades(const DistributeCascadeInfo& Info, for(int i=0; i < MAX_CASCADES; ++i) ShadowAttribs.fCascadeCamSpaceZEnd[i] = +FLT_MAX; - - const auto& CameraWorld = Info.pCameraWorld != nullptr ? *Info.pCameraWorld : Info.pCameraView->Inverse(); - + int iNumCascades = SMDesc.ArraySize; ShadowAttribs.iNumCascades = iNumCascades; ShadowAttribs.fNumCascades = static_cast<float>(iNumCascades); @@ -205,7 +204,7 @@ void ShadowMapManager::DistributeCascades(const DistributeCascadeInfo& Info, float range = fMainCamFarPlane - fMainCamNearPlane; float uniformZ = fMainCamNearPlane + range * power; - fCascadeFarZ = ShadowAttribs.fCascadePartitioningFactor * (logZ - uniformZ) + uniformZ; + fCascadeFarZ = Info.fPartitioningFactor * (logZ - uniformZ) + uniformZ; } else { |
