summaryrefslogtreecommitdiff
path: root/tests/hlsl/dxsdk/VarianceShadows11/RenderVarianceScene.hlsl
blob: 0b2e43b5c90207ba5a4045d367c0232b793a7657 (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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
//TEST_IGNORE_FILE: Currently failing due to Spire compiler issues.
//TEST:COMPARE_HLSL: -target dxbc-assembly -profile vs_4_0 -entry VSMain -profile ps_4_0 -entry PSBlurX -entry PSBlurY
//--------------------------------------------------------------------------------------
// File: RenderCascadeScene.hlsl
//
// This is the main shader file.  This shader is compiled with several different flags 
// to provide different customizations based on user controls.
// 
// 
// Copyright (c) Microsoft Corporation. All rights reserved.
//--------------------------------------------------------------------------------------

//--------------------------------------------------------------------------------------
// Globals
//--------------------------------------------------------------------------------------

// This flag enables the shadow to blend between cascades.  This is most useful when the 
// the shadow maps are small and artifact can be seen between the various cascade layers.
#ifndef BLEND_BETWEEN_CASCADE_LAYERS_FLAG
#define BLEND_BETWEEN_CASCADE_LAYERS_FLAG 0
#endif

// There are two methods for selecting the proper cascade a fragment lies in.  Interval selection
// compares the depth of the fragment against the frustum's depth partition.
// Map based selection compares the texture coordinates against the acutal cascade maps.
// Map based selection gives better coverage.  
// Interval based selection is easier to extend and understand.
#ifndef SELECT_CASCADE_BY_INTERVAL_FLAG
#define SELECT_CASCADE_BY_INTERVAL_FLAG 0
#endif

// The number of cascades 
#ifndef CASCADE_COUNT_FLAG
#define CASCADE_COUNT_FLAG 3
#endif


// Most titles will find that 3-4 cascades with 
// BLEND_BETWEEN_CASCADE_LAYERS_FLAG, is good for lower end PCs.

cbuffer cbAllShadowData : register( b0 )
{
    matrix          m_mWorldViewProjection;
    matrix          m_mWorld;
    matrix          m_mWorldView;
    matrix          m_mShadow;
    float4          m_vCascadeOffset[8];
    float4          m_vCascadeScale[8];
    int             m_nCascadeLevels; // Number of Cascades
    int             m_iVisualizeCascades; // 1 is to visualize the cascades in different colors. 0 is to just draw the scene

    // For Map based selection scheme, this keeps the pixels inside of the the valid range.
    // When there is no boarder, these values are 0 and 1 respectivley.
    float           m_fMinBorderPadding;     
    float           m_fMaxBorderPadding;
                                          
    float           m_fCascadeBlendArea; // Amount to overlap when blending between cascades.
    float           m_fTexelSize; // Padding variables exist because CBs must be a multiple of 16 bytes.
    float           m_fNativeTexelSizeInX;
    float4          m_fCascadeFrustumsEyeSpaceDepthsData[2];  // The values along Z that seperate the cascades.
    // This code creates an array based pointer that points towards the vectorized input data.
    // This is the only way to index arbitrary arrays of data.
    // If the array is used at run time, the compiler will generate code that uses logic to index the correct component.

    static float    m_fCascadeFrustumsEyeSpaceDepths[8] = (float[8])m_fCascadeFrustumsEyeSpaceDepthsData;
    
    float3          m_vLightDir;
    float           m_fPaddingCB4;

};



//--------------------------------------------------------------------------------------
// Textures and Samplers
//--------------------------------------------------------------------------------------
Texture2D           g_txDiffuse             : register( t0 );
Texture2DArray      g_txShadow              : register( t5 );

SamplerState g_samLinear                    : register( s0 );
SamplerState g_samShadow                    : register( s5 );

//--------------------------------------------------------------------------------------
// Input / Output structures
//--------------------------------------------------------------------------------------
struct VS_INPUT
{
    float4 vPosition                        : POSITION;
    float3 vNormal                          : NORMAL;
    float2 vTexcoord                        : TEXCOORD0;
};

struct VS_OUTPUT
{
    float3 vNormal                          : NORMAL;
    float2 vTexcoord                        : COLOR0;
    float4 vTexShadow						: TEXCOORD1;
    float4 vPosition                        : SV_POSITION;
    float4 vInterpPos                       : TEXCOORD2;
    float  vDepth                           : TEXCOORD3;
};

//--------------------------------------------------------------------------------------
// Vertex Shader
//--------------------------------------------------------------------------------------
VS_OUTPUT VSMain( VS_INPUT Input )
{
    VS_OUTPUT Output;

    Output.vPosition = mul( Input.vPosition, m_mWorldViewProjection );
    Output.vNormal = mul( Input.vNormal, (float3x3)m_mWorld );
    Output.vTexcoord = Input.vTexcoord;
    Output.vInterpPos = Input.vPosition;   
    Output.vDepth = mul( Input.vPosition, m_mWorldView ).z ; 
       
    // Transform the shadow texture coordinates for all the cascades.
    Output.vTexShadow = mul( Input.vPosition, m_mShadow );
        
    return Output;
}



static const float4 vCascadeColorsMultiplier[8] = 
{
    float4 ( 1.5f, 0.0f, 0.0f, 1.0f ),
    float4 ( 0.0f, 1.5f, 0.0f, 1.0f ),
    float4 ( 0.0f, 0.0f, 5.5f, 1.0f ),
    float4 ( 1.5f, 0.0f, 5.5f, 1.0f ),
    float4 ( 1.5f, 1.5f, 0.0f, 1.0f ),
    float4 ( 1.0f, 1.0f, 1.0f, 1.0f ),
    float4 ( 0.0f, 1.0f, 5.5f, 1.0f ),
    float4 ( 0.5f, 3.5f, 0.75f, 1.0f )
};


void ComputeCoordinatesTransform( in int iCascadeIndex,
                                  in float4 InterpolatedPosition, 
                                  in out float4 vShadowTexCoord,
                                  in out float4 vShadowTexCoordViewSpace ) 
{
    // Now that we know the correct map, we can transform the world space position of the current fragment                
    if( SELECT_CASCADE_BY_INTERVAL_FLAG ) 
    {
        vShadowTexCoord = vShadowTexCoordViewSpace * m_vCascadeScale[iCascadeIndex];
        vShadowTexCoord += m_vCascadeOffset[iCascadeIndex];
    }  
    vShadowTexCoord.w = vShadowTexCoord.z; // We put the z value in w so that we can index the texture array with Z.
    vShadowTexCoord.z = iCascadeIndex;
    
} 

//--------------------------------------------------------------------------------------
// Use PCF to sample the depth map and return a percent lit value.
//--------------------------------------------------------------------------------------
void CalculateVarianceShadow ( in float4 vShadowTexCoord, in float4 vShadowMapTextureCoordViewSpace, int iCascade, out float fPercentLit ) 
{
    fPercentLit = 0.0f;
    // This loop could be unrolled, and texture immediate offsets could be used if the kernel size were fixed.
    // This would be a performance improvment.
	        
    float2 mapDepth = 0;


    // In orderto pull the derivative out of divergent flow control we calculate the 
    // derivative off of the view space coordinates an then scale the deriviative.
    
    float3 vShadowTexCoordDDX = 
		ddx(vShadowMapTextureCoordViewSpace );
    vShadowTexCoordDDX *= m_vCascadeScale[iCascade].xyz; 
    float3 vShadowTexCoordDDY = 
		ddy(vShadowMapTextureCoordViewSpace );
    vShadowTexCoordDDY *= m_vCascadeScale[iCascade].xyz; 
    
    mapDepth += g_txShadow.SampleGrad( g_samShadow, vShadowTexCoord.xyz, 
									   vShadowTexCoordDDX,
									   vShadowTexCoordDDY);
    // The sample instruction uses gradients for some filters.
		        
    float  fAvgZ  = mapDepth.x; // Filtered z
    float  fAvgZ2 = mapDepth.y; // Filtered z-squared
    
    if ( vShadowTexCoord.w <= fAvgZ ) // We put the z value in w so that we can index the texture array with Z.
    {
        fPercentLit = 1;
	}
	else 
	{
	    float variance = ( fAvgZ2 ) - ( fAvgZ * fAvgZ );
        variance       = min( 1.0f, max( 0.0f, variance + 0.00001f ) );
    
        float mean     = fAvgZ;
        float d        = vShadowTexCoord.w - mean; // We put the z value in w so that we can index the texture array with Z.
        float p_max    = variance / ( variance + d*d );

        // To combat light-bleeding, experiment with raising p_max to some power
        // (Try values from 0.1 to 100.0, if you like.)
        fPercentLit = pow( p_max, 4 );
	    
	}
    
}

//--------------------------------------------------------------------------------------
// Calculate amount to blend between two cascades and the band where blending will occure.
//--------------------------------------------------------------------------------------
void CalculateBlendAmountForInterval ( in int iNextCascadeIndex, 
                                       in out float fPixelDepth, 
                                       in out float fCurrentPixelsBlendBandLocation,
                                       out float fBlendBetweenCascadesAmount
                                       ) 
{

    // We need to calculate the band of the current shadow map where it will fade into the next cascade.
    // We can then early out of the expensive PCF for loop.
    // 
    float fBlendInterval = m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex - 1 ];
    if( iNextCascadeIndex > 1 ) 
    {
        fPixelDepth -= m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex-2 ];
        fBlendInterval -= m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex-2 ];
    } 
    // The current pixel's blend band location will be used to determine when we need to blend and by how much.
    fCurrentPixelsBlendBandLocation = fPixelDepth / fBlendInterval;
    fCurrentPixelsBlendBandLocation = 1.0f - fCurrentPixelsBlendBandLocation;
    // The fBlendBetweenCascadesAmount is our location in the blend band.
    fBlendBetweenCascadesAmount = fCurrentPixelsBlendBandLocation / m_fCascadeBlendArea;
}


//--------------------------------------------------------------------------------------
// Calculate amount to blend between two cascades and the band where blending will occure.
//--------------------------------------------------------------------------------------
void CalculateBlendAmountForMap ( in float4 vShadowMapTextureCoord, 
                                  in out float fCurrentPixelsBlendBandLocation,
                                  out float fBlendBetweenCascadesAmount ) 
{
    // Calcaulte the blend band for the map based selection.
    float2 distanceToOne = float2 ( 1.0f - vShadowMapTextureCoord.x, 1.0f - vShadowMapTextureCoord.y );
    fCurrentPixelsBlendBandLocation = min( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y );
    float fCurrentPixelsBlendBandLocation2 = min( distanceToOne.x, distanceToOne.y );
    fCurrentPixelsBlendBandLocation = 
        min( fCurrentPixelsBlendBandLocation, fCurrentPixelsBlendBandLocation2 );
    fBlendBetweenCascadesAmount = fCurrentPixelsBlendBandLocation / m_fCascadeBlendArea;
}

//--------------------------------------------------------------------------------------
// Calculate the shadow based on several options and rende the scene.
//--------------------------------------------------------------------------------------

float4 PSMain( VS_OUTPUT Input ) : SV_TARGET
{
    float4 vDiffuse = g_txDiffuse.Sample( g_samLinear, Input.vTexcoord );
    
    
    float4 vShadowMapTextureCoordViewSpace = 0.0f;
    float4 vShadowMapTextureCoord = 0.0f;
    float4 vShadowMapTextureCoord_blend = 0.0f;
    
    float4 vVisualizeCascadeColor = float4(0.0f,0.0f,0.0f,1.0f);
    
    float fPercentLit = 0.0f;
    float fPercentLit_blend = 0.0f;

    int iCascadeFound = 0;
    int iCurrentCascadeIndex=1;
    int iNextCascadeIndex = 0;

    float fCurrentPixelDepth;

    // The interval based selection technique compares the pixel's depth against the frustum's cascade divisions.
    fCurrentPixelDepth = Input.vDepth;
    
    // This for loop is not necessary when the frustum is uniformaly divided and interval based selection is used.
    // In this case fCurrentPixelDepth could be used as an array lookup into the correct frustum. 
    vShadowMapTextureCoordViewSpace = Input.vTexShadow;
    
    
    if( SELECT_CASCADE_BY_INTERVAL_FLAG ) 
    {
        iCurrentCascadeIndex = 0;
        if (CASCADE_COUNT_FLAG > 1 ) 
        {
            float4 vCurrentPixelDepth = Input.vDepth;
            float4 fComparison = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepthsData[0]);
            float4 fComparison2 = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepthsData[1]);
            float fIndex = dot( 
                            float4( CASCADE_COUNT_FLAG > 0,
                                    CASCADE_COUNT_FLAG > 1, 
                                    CASCADE_COUNT_FLAG > 2, 
                                    CASCADE_COUNT_FLAG > 3)
                            , fComparison )
                         + dot( 
                            float4(
                                    CASCADE_COUNT_FLAG > 4,
                                    CASCADE_COUNT_FLAG > 5,
                                    CASCADE_COUNT_FLAG > 6,
                                    CASCADE_COUNT_FLAG > 7)
                            , fComparison2 ) ;
                                    
            fIndex = min( fIndex, CASCADE_COUNT_FLAG - 1 );
            iCurrentCascadeIndex = (int)fIndex;
        }
    }
    
    if ( !SELECT_CASCADE_BY_INTERVAL_FLAG ) 
    {
        iCurrentCascadeIndex = 0;
        if ( CASCADE_COUNT_FLAG == 1 ) 
        {
            vShadowMapTextureCoord = vShadowMapTextureCoordViewSpace * m_vCascadeScale[0];
            vShadowMapTextureCoord += m_vCascadeOffset[0];
        }
        if ( CASCADE_COUNT_FLAG > 1 ) {
            for( int iCascadeIndex = 0; iCascadeIndex < CASCADE_COUNT_FLAG && iCascadeFound == 0; ++iCascadeIndex ) 
            {
                vShadowMapTextureCoord = vShadowMapTextureCoordViewSpace * m_vCascadeScale[iCascadeIndex];
                vShadowMapTextureCoord += m_vCascadeOffset[iCascadeIndex];

                if ( min( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y ) > m_fMinBorderPadding
                  && max( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y ) < m_fMaxBorderPadding )
                { 
                    iCurrentCascadeIndex = iCascadeIndex;   
                    iCascadeFound = 1; 
                }
            }
        }
    }    
    // Found the correct map.
    vVisualizeCascadeColor = vCascadeColorsMultiplier[iCurrentCascadeIndex];
    
    ComputeCoordinatesTransform( iCurrentCascadeIndex, Input.vInterpPos, vShadowMapTextureCoord, vShadowMapTextureCoordViewSpace  );    
                                             
    if( BLEND_BETWEEN_CASCADE_LAYERS_FLAG && CASCADE_COUNT_FLAG > 1 ) 
    {
        // Repeat text coord calculations for the next cascade. 
        // The next cascade index is used for blurring between maps.
        iNextCascadeIndex = min ( CASCADE_COUNT_FLAG - 1, iCurrentCascadeIndex + 1 ); 
        if( !SELECT_CASCADE_BY_INTERVAL_FLAG ) 
        {
            vShadowMapTextureCoord_blend = vShadowMapTextureCoordViewSpace * m_vCascadeScale[iNextCascadeIndex];
            vShadowMapTextureCoord_blend += m_vCascadeOffset[iNextCascadeIndex];
        }
        ComputeCoordinatesTransform( iNextCascadeIndex, Input.vInterpPos, vShadowMapTextureCoord_blend, vShadowMapTextureCoordViewSpace );  
    }            
    float fBlendBetweenCascadesAmount = 1.0f;
    float fCurrentPixelsBlendBandLocation = 1.0f;
    
    if( SELECT_CASCADE_BY_INTERVAL_FLAG ) 
    {
        if( CASCADE_COUNT_FLAG > 1 && BLEND_BETWEEN_CASCADE_LAYERS_FLAG ) 
        {
            CalculateBlendAmountForInterval ( iNextCascadeIndex, fCurrentPixelDepth, 
                fCurrentPixelsBlendBandLocation, fBlendBetweenCascadesAmount );
            
        }   
    }
    else 
    {
        if( CASCADE_COUNT_FLAG > 1 && BLEND_BETWEEN_CASCADE_LAYERS_FLAG ) 
        {
            CalculateBlendAmountForMap ( vShadowMapTextureCoord, 
                fCurrentPixelsBlendBandLocation, fBlendBetweenCascadesAmount );
        }   
    }
    
    // Because the Z coordinate specifies the texture array,
    // the derivative will be 0 when there is no divergence
    //float fDivergence = abs( ddy( vShadowMapTextureCoord.z ) ) +  abs( ddx( vShadowMapTextureCoord.z ) );
    CalculateVarianceShadow ( vShadowMapTextureCoord, vShadowMapTextureCoordViewSpace, 
								iCurrentCascadeIndex, fPercentLit);
								
    // We repeat the calcuation for the next cascade layer, when blending between maps.
    if( BLEND_BETWEEN_CASCADE_LAYERS_FLAG  && CASCADE_COUNT_FLAG > 1 ) 
    {
        if( fCurrentPixelsBlendBandLocation < m_fCascadeBlendArea ) 
        {  // the current pixel is within the blend band.

			// Because the Z coordinate species the texture array,
			// the derivative will be 0 when there is no divergence
			float fDivergence = abs( ddy( vShadowMapTextureCoord_blend.z ) ) +  
				abs( ddx( vShadowMapTextureCoord_blend.z) );
            CalculateVarianceShadow ( vShadowMapTextureCoord_blend, vShadowMapTextureCoordViewSpace, 
										iNextCascadeIndex, fPercentLit_blend );

            // Blend the two calculated shadows by the blend amount.
            fPercentLit = lerp( fPercentLit_blend, fPercentLit, fBlendBetweenCascadesAmount ); 

        }   
    }    
  
    if( !m_iVisualizeCascades ) vVisualizeCascadeColor = float4( 1.0f, 1.0f, 1.0f, 1.0f );
    
    float3 vLightDir1 = float3( -1.0f, 1.0f, -1.0f ); 
    float3 vLightDir2 = float3( 1.0f, 1.0f, -1.0f ); 
    float3 vLightDir3 = float3( 0.0f, -1.0f, 0.0f );
    float3 vLightDir4 = float3( 1.0f, 1.0f, 1.0f );     
    // Some ambient-like lighting.
    float fLighting = 
                      saturate( dot( vLightDir1 , Input.vNormal ) )*0.05f +
                      saturate( dot( vLightDir2 , Input.vNormal ) )*0.05f +
                      saturate( dot( vLightDir3 , Input.vNormal ) )*0.05f +
                      saturate( dot( vLightDir4 , Input.vNormal ) )*0.05f ;
    
    float4 vShadowLighting = fLighting * 0.5f;
    fLighting += saturate( dot( m_vLightDir , Input.vNormal ) );
    fLighting = lerp( vShadowLighting, fLighting, fPercentLit );
    
    return fLighting * vVisualizeCascadeColor * vDiffuse;

}