summaryrefslogtreecommitdiffstats
path: root/Components
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2019-07-24 05:16:34 +0000
committerEgor Yusov <egor.yusov@gmail.com>2019-07-24 05:16:34 +0000
commitbf756bb54d1b87efda441fe593d9cc7c9fc4854f (patch)
tree38746eeed2e46443a92c149791815e41f1dcc425 /Components
parentShadows.fxh: using SampleCmpLevelZero in HLSL and SampleCmp in GLSL (diff)
downloadDiligentFX-bf756bb54d1b87efda441fe593d9cc7c9fc4854f.tar.gz
DiligentFX-bf756bb54d1b87efda441fe593d9cc7c9fc4854f.zip
Updated Shadow map manager & added readme
Diffstat (limited to 'Components')
-rw-r--r--Components/README.md156
-rw-r--r--Components/interface/ShadowMapManager.h42
-rw-r--r--Components/media/Powerplant-Shadows.jpgbin0 -> 188043 bytes
-rw-r--r--Components/src/ShadowMapManager.cpp19
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
+
+![](media/Powerplant-Shadows.jpg)
+
+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
new file mode 100644
index 0000000..63f1539
--- /dev/null
+++ b/Components/media/Powerplant-Shadows.jpg
Binary files differ
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
{