summaryrefslogtreecommitdiffstats
path: root/Components/README.md
blob: f917ce1a72d5eac9b8931c6816a873dbd4bc1b38 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
# 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
- PCF
- Variance shadow maps
- Two and four-component exponential variance shadow maps
- Optimized fixed-size or world-sized filter kernels
- 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 sampler 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 representations, 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` function:

```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 macros that should be defined when creating the shader:

```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 the shadowing 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)