RenderVarianceScenePS.hlsl 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424
  1. // RUN: %dxc -E main -T ps_6_0 %s | FileCheck %s
  2. // CHECK: sample
  3. // CHECK: DerivCoarseX
  4. // CHECK: DerivCoarseY
  5. // CHECK: sampleGrad
  6. // CHECK: FMax
  7. // CHECK: FMin
  8. // CHECK: Log
  9. // CHECK: Exp
  10. // CHECK: Saturate
  11. // CHECK: dot3
  12. // CHECK: storeOutput
  13. //--------------------------------------------------------------------------------------
  14. // File: RenderCascadeScene.hlsl
  15. //
  16. // This is the main shader file. This shader is compiled with several different flags
  17. // to provide different customizations based on user controls.
  18. //
  19. //
  20. // Copyright (c) Microsoft Corporation. All rights reserved.
  21. //--------------------------------------------------------------------------------------
  22. //--------------------------------------------------------------------------------------
  23. // Globals
  24. //--------------------------------------------------------------------------------------
  25. // This flag enables the shadow to blend between cascades. This is most useful when the
  26. // the shadow maps are small and artifact can be seen between the various cascade layers.
  27. #ifndef BLEND_BETWEEN_CASCADE_LAYERS_FLAG
  28. #define BLEND_BETWEEN_CASCADE_LAYERS_FLAG 0
  29. #endif
  30. // There are two methods for selecting the proper cascade a fragment lies in. Interval selection
  31. // compares the depth of the fragment against the frustum's depth partition.
  32. // Map based selection compares the texture coordinates against the acutal cascade maps.
  33. // Map based selection gives better coverage.
  34. // Interval based selection is easier to extend and understand.
  35. #ifndef SELECT_CASCADE_BY_INTERVAL_FLAG
  36. #define SELECT_CASCADE_BY_INTERVAL_FLAG 0
  37. #endif
  38. // The number of cascades
  39. #ifndef CASCADE_COUNT_FLAG
  40. #define CASCADE_COUNT_FLAG 3
  41. #endif
  42. // Most titles will find that 3-4 cascades with
  43. // BLEND_BETWEEN_CASCADE_LAYERS_FLAG, is good for lower end PCs.
  44. cbuffer cbAllShadowData : register( b0 )
  45. {
  46. matrix m_mWorldViewProjection;
  47. matrix m_mWorld;
  48. matrix m_mWorldView;
  49. matrix m_mShadow;
  50. float4 m_vCascadeOffset[8];
  51. float4 m_vCascadeScale[8];
  52. int m_nCascadeLevels; // Number of Cascades
  53. int m_iVisualizeCascades; // 1 is to visualize the cascades in different colors. 0 is to just draw the scene
  54. // For Map based selection scheme, this keeps the pixels inside of the the valid range.
  55. // When there is no boarder, these values are 0 and 1 respectivley.
  56. float m_fMinBorderPadding;
  57. float m_fMaxBorderPadding;
  58. float m_fCascadeBlendArea; // Amount to overlap when blending between cascades.
  59. float m_fTexelSize; // Padding variables exist because CBs must be a multiple of 16 bytes.
  60. float m_fNativeTexelSizeInX;
  61. float4 m_fCascadeFrustumsEyeSpaceDepthsData[2]; // The values along Z that seperate the cascades.
  62. // This code creates an array based pointer that points towards the vectorized input data.
  63. // This is the only way to index arbitrary arrays of data.
  64. // If the array is used at run time, the compiler will generate code that uses logic to index the correct component.
  65. static float m_fCascadeFrustumsEyeSpaceDepths[8] = (float[8])m_fCascadeFrustumsEyeSpaceDepthsData;
  66. float3 m_vLightDir;
  67. float m_fPaddingCB4;
  68. };
  69. //--------------------------------------------------------------------------------------
  70. // Textures and Samplers
  71. //--------------------------------------------------------------------------------------
  72. Texture2D g_txDiffuse : register( t0 );
  73. Texture2DArray g_txShadow : register( t5 );
  74. SamplerState g_samLinear : register( s0 );
  75. SamplerState g_samShadow : register( s5 );
  76. //--------------------------------------------------------------------------------------
  77. // Input / Output structures
  78. //--------------------------------------------------------------------------------------
  79. struct VS_INPUT
  80. {
  81. float4 vPosition : POSITION;
  82. float3 vNormal : NORMAL;
  83. float2 vTexcoord : TEXCOORD0;
  84. };
  85. struct VS_OUTPUT
  86. {
  87. float3 vNormal : NORMAL;
  88. float2 vTexcoord : COLOR0;
  89. float4 vTexShadow : TEXCOORD1;
  90. float4 vPosition : SV_POSITION;
  91. float4 vInterpPos : TEXCOORD2;
  92. float vDepth : TEXCOORD3;
  93. };
  94. //--------------------------------------------------------------------------------------
  95. // Vertex Shader
  96. //--------------------------------------------------------------------------------------
  97. VS_OUTPUT VSMain( VS_INPUT Input )
  98. {
  99. VS_OUTPUT Output;
  100. Output.vPosition = mul( Input.vPosition, m_mWorldViewProjection );
  101. Output.vNormal = mul( Input.vNormal, (float3x3)m_mWorld );
  102. Output.vTexcoord = Input.vTexcoord;
  103. Output.vInterpPos = Input.vPosition;
  104. Output.vDepth = mul( Input.vPosition, m_mWorldView ).z ;
  105. // Transform the shadow texture coordinates for all the cascades.
  106. Output.vTexShadow = mul( Input.vPosition, m_mShadow );
  107. return Output;
  108. }
  109. static const float4 vCascadeColorsMultiplier[8] =
  110. {
  111. float4 ( 1.5f, 0.0f, 0.0f, 1.0f ),
  112. float4 ( 0.0f, 1.5f, 0.0f, 1.0f ),
  113. float4 ( 0.0f, 0.0f, 5.5f, 1.0f ),
  114. float4 ( 1.5f, 0.0f, 5.5f, 1.0f ),
  115. float4 ( 1.5f, 1.5f, 0.0f, 1.0f ),
  116. float4 ( 1.0f, 1.0f, 1.0f, 1.0f ),
  117. float4 ( 0.0f, 1.0f, 5.5f, 1.0f ),
  118. float4 ( 0.5f, 3.5f, 0.75f, 1.0f )
  119. };
  120. void ComputeCoordinatesTransform( in int iCascadeIndex,
  121. in float4 InterpolatedPosition,
  122. in out float4 vShadowTexCoord,
  123. in out float4 vShadowTexCoordViewSpace )
  124. {
  125. // Now that we know the correct map, we can transform the world space position of the current fragment
  126. if( SELECT_CASCADE_BY_INTERVAL_FLAG )
  127. {
  128. vShadowTexCoord = vShadowTexCoordViewSpace * m_vCascadeScale[iCascadeIndex];
  129. vShadowTexCoord += m_vCascadeOffset[iCascadeIndex];
  130. }
  131. vShadowTexCoord.w = vShadowTexCoord.z; // We put the z value in w so that we can index the texture array with Z.
  132. vShadowTexCoord.z = iCascadeIndex;
  133. }
  134. //--------------------------------------------------------------------------------------
  135. // Use PCF to sample the depth map and return a percent lit value.
  136. //--------------------------------------------------------------------------------------
  137. void CalculateVarianceShadow ( in float4 vShadowTexCoord, in float4 vShadowMapTextureCoordViewSpace, int iCascade, out float fPercentLit )
  138. {
  139. fPercentLit = 0.0f;
  140. // This loop could be unrolled, and texture immediate offsets could be used if the kernel size were fixed.
  141. // This would be a performance improvment.
  142. float2 mapDepth = 0;
  143. // In orderto pull the derivative out of divergent flow control we calculate the
  144. // derivative off of the view space coordinates an then scale the deriviative.
  145. float3 vShadowTexCoordDDX =
  146. ddx(vShadowMapTextureCoordViewSpace );
  147. vShadowTexCoordDDX *= m_vCascadeScale[iCascade].xyz;
  148. float3 vShadowTexCoordDDY =
  149. ddy(vShadowMapTextureCoordViewSpace );
  150. vShadowTexCoordDDY *= m_vCascadeScale[iCascade].xyz;
  151. mapDepth += g_txShadow.SampleGrad( g_samShadow, vShadowTexCoord.xyz,
  152. vShadowTexCoordDDX,
  153. vShadowTexCoordDDY);
  154. // The sample instruction uses gradients for some filters.
  155. float fAvgZ = mapDepth.x; // Filtered z
  156. float fAvgZ2 = mapDepth.y; // Filtered z-squared
  157. if ( vShadowTexCoord.w <= fAvgZ ) // We put the z value in w so that we can index the texture array with Z.
  158. {
  159. fPercentLit = 1;
  160. }
  161. else
  162. {
  163. float variance = ( fAvgZ2 ) - ( fAvgZ * fAvgZ );
  164. variance = min( 1.0f, max( 0.0f, variance + 0.00001f ) );
  165. float mean = fAvgZ;
  166. float d = vShadowTexCoord.w - mean; // We put the z value in w so that we can index the texture array with Z.
  167. float p_max = variance / ( variance + d*d );
  168. // To combat light-bleeding, experiment with raising p_max to some power
  169. // (Try values from 0.1 to 100.0, if you like.)
  170. fPercentLit = pow( p_max, 4 );
  171. }
  172. }
  173. //--------------------------------------------------------------------------------------
  174. // Calculate amount to blend between two cascades and the band where blending will occure.
  175. //--------------------------------------------------------------------------------------
  176. void CalculateBlendAmountForInterval ( in int iNextCascadeIndex,
  177. in out float fPixelDepth,
  178. in out float fCurrentPixelsBlendBandLocation,
  179. out float fBlendBetweenCascadesAmount
  180. )
  181. {
  182. // We need to calculate the band of the current shadow map where it will fade into the next cascade.
  183. // We can then early out of the expensive PCF for loop.
  184. //
  185. float fBlendInterval = m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex - 1 ];
  186. if( iNextCascadeIndex > 1 )
  187. {
  188. fPixelDepth -= m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex-2 ];
  189. fBlendInterval -= m_fCascadeFrustumsEyeSpaceDepths[ iNextCascadeIndex-2 ];
  190. }
  191. // The current pixel's blend band location will be used to determine when we need to blend and by how much.
  192. fCurrentPixelsBlendBandLocation = fPixelDepth / fBlendInterval;
  193. fCurrentPixelsBlendBandLocation = 1.0f - fCurrentPixelsBlendBandLocation;
  194. // The fBlendBetweenCascadesAmount is our location in the blend band.
  195. fBlendBetweenCascadesAmount = fCurrentPixelsBlendBandLocation / m_fCascadeBlendArea;
  196. }
  197. //--------------------------------------------------------------------------------------
  198. // Calculate amount to blend between two cascades and the band where blending will occure.
  199. //--------------------------------------------------------------------------------------
  200. void CalculateBlendAmountForMap ( in float4 vShadowMapTextureCoord,
  201. in out float fCurrentPixelsBlendBandLocation,
  202. out float fBlendBetweenCascadesAmount )
  203. {
  204. // Calcaulte the blend band for the map based selection.
  205. float2 distanceToOne = float2 ( 1.0f - vShadowMapTextureCoord.x, 1.0f - vShadowMapTextureCoord.y );
  206. fCurrentPixelsBlendBandLocation = min( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y );
  207. float fCurrentPixelsBlendBandLocation2 = min( distanceToOne.x, distanceToOne.y );
  208. fCurrentPixelsBlendBandLocation =
  209. min( fCurrentPixelsBlendBandLocation, fCurrentPixelsBlendBandLocation2 );
  210. fBlendBetweenCascadesAmount = fCurrentPixelsBlendBandLocation / m_fCascadeBlendArea;
  211. }
  212. //--------------------------------------------------------------------------------------
  213. // Calculate the shadow based on several options and rende the scene.
  214. //--------------------------------------------------------------------------------------
  215. float4 main( VS_OUTPUT Input ) : SV_TARGET
  216. {
  217. float4 vDiffuse = g_txDiffuse.Sample( g_samLinear, Input.vTexcoord );
  218. float4 vShadowMapTextureCoordViewSpace = 0.0f;
  219. float4 vShadowMapTextureCoord = 0.0f;
  220. float4 vShadowMapTextureCoord_blend = 0.0f;
  221. float4 vVisualizeCascadeColor = float4(0.0f,0.0f,0.0f,1.0f);
  222. float fPercentLit = 0.0f;
  223. float fPercentLit_blend = 0.0f;
  224. int iCascadeFound = 0;
  225. int iCurrentCascadeIndex=1;
  226. int iNextCascadeIndex = 0;
  227. float fCurrentPixelDepth;
  228. // The interval based selection technique compares the pixel's depth against the frustum's cascade divisions.
  229. fCurrentPixelDepth = Input.vDepth;
  230. // This for loop is not necessary when the frustum is uniformaly divided and interval based selection is used.
  231. // In this case fCurrentPixelDepth could be used as an array lookup into the correct frustum.
  232. vShadowMapTextureCoordViewSpace = Input.vTexShadow;
  233. if( SELECT_CASCADE_BY_INTERVAL_FLAG )
  234. {
  235. iCurrentCascadeIndex = 0;
  236. if (CASCADE_COUNT_FLAG > 1 )
  237. {
  238. float4 vCurrentPixelDepth = Input.vDepth;
  239. float4 fComparison = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepthsData[0]);
  240. float4 fComparison2 = ( vCurrentPixelDepth > m_fCascadeFrustumsEyeSpaceDepthsData[1]);
  241. float fIndex = dot(
  242. float4( CASCADE_COUNT_FLAG > 0,
  243. CASCADE_COUNT_FLAG > 1,
  244. CASCADE_COUNT_FLAG > 2,
  245. CASCADE_COUNT_FLAG > 3)
  246. , fComparison )
  247. + dot(
  248. float4(
  249. CASCADE_COUNT_FLAG > 4,
  250. CASCADE_COUNT_FLAG > 5,
  251. CASCADE_COUNT_FLAG > 6,
  252. CASCADE_COUNT_FLAG > 7)
  253. , fComparison2 ) ;
  254. fIndex = min( fIndex, CASCADE_COUNT_FLAG - 1 );
  255. iCurrentCascadeIndex = (int)fIndex;
  256. }
  257. }
  258. if ( !SELECT_CASCADE_BY_INTERVAL_FLAG )
  259. {
  260. iCurrentCascadeIndex = 0;
  261. if ( CASCADE_COUNT_FLAG == 1 )
  262. {
  263. vShadowMapTextureCoord = vShadowMapTextureCoordViewSpace * m_vCascadeScale[0];
  264. vShadowMapTextureCoord += m_vCascadeOffset[0];
  265. }
  266. if ( CASCADE_COUNT_FLAG > 1 ) {
  267. for( int iCascadeIndex = 0; iCascadeIndex < CASCADE_COUNT_FLAG && iCascadeFound == 0; ++iCascadeIndex )
  268. {
  269. vShadowMapTextureCoord = vShadowMapTextureCoordViewSpace * m_vCascadeScale[iCascadeIndex];
  270. vShadowMapTextureCoord += m_vCascadeOffset[iCascadeIndex];
  271. if ( min( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y ) > m_fMinBorderPadding
  272. && max( vShadowMapTextureCoord.x, vShadowMapTextureCoord.y ) < m_fMaxBorderPadding )
  273. {
  274. iCurrentCascadeIndex = iCascadeIndex;
  275. iCascadeFound = 1;
  276. }
  277. }
  278. }
  279. }
  280. // Found the correct map.
  281. vVisualizeCascadeColor = vCascadeColorsMultiplier[iCurrentCascadeIndex];
  282. ComputeCoordinatesTransform( iCurrentCascadeIndex, Input.vInterpPos, vShadowMapTextureCoord, vShadowMapTextureCoordViewSpace );
  283. if( BLEND_BETWEEN_CASCADE_LAYERS_FLAG && CASCADE_COUNT_FLAG > 1 )
  284. {
  285. // Repeat text coord calculations for the next cascade.
  286. // The next cascade index is used for blurring between maps.
  287. iNextCascadeIndex = min ( CASCADE_COUNT_FLAG - 1, iCurrentCascadeIndex + 1 );
  288. if( !SELECT_CASCADE_BY_INTERVAL_FLAG )
  289. {
  290. vShadowMapTextureCoord_blend = vShadowMapTextureCoordViewSpace * m_vCascadeScale[iNextCascadeIndex];
  291. vShadowMapTextureCoord_blend += m_vCascadeOffset[iNextCascadeIndex];
  292. }
  293. ComputeCoordinatesTransform( iNextCascadeIndex, Input.vInterpPos, vShadowMapTextureCoord_blend, vShadowMapTextureCoordViewSpace );
  294. }
  295. float fBlendBetweenCascadesAmount = 1.0f;
  296. float fCurrentPixelsBlendBandLocation = 1.0f;
  297. if( SELECT_CASCADE_BY_INTERVAL_FLAG )
  298. {
  299. if( CASCADE_COUNT_FLAG > 1 && BLEND_BETWEEN_CASCADE_LAYERS_FLAG )
  300. {
  301. CalculateBlendAmountForInterval ( iNextCascadeIndex, fCurrentPixelDepth,
  302. fCurrentPixelsBlendBandLocation, fBlendBetweenCascadesAmount );
  303. }
  304. }
  305. else
  306. {
  307. if( CASCADE_COUNT_FLAG > 1 && BLEND_BETWEEN_CASCADE_LAYERS_FLAG )
  308. {
  309. CalculateBlendAmountForMap ( vShadowMapTextureCoord,
  310. fCurrentPixelsBlendBandLocation, fBlendBetweenCascadesAmount );
  311. }
  312. }
  313. // Because the Z coordinate specifies the texture array,
  314. // the derivative will be 0 when there is no divergence
  315. //float fDivergence = abs( ddy( vShadowMapTextureCoord.z ) ) + abs( ddx( vShadowMapTextureCoord.z ) );
  316. CalculateVarianceShadow ( vShadowMapTextureCoord, vShadowMapTextureCoordViewSpace,
  317. iCurrentCascadeIndex, fPercentLit);
  318. // We repeat the calcuation for the next cascade layer, when blending between maps.
  319. if( BLEND_BETWEEN_CASCADE_LAYERS_FLAG && CASCADE_COUNT_FLAG > 1 )
  320. {
  321. if( fCurrentPixelsBlendBandLocation < m_fCascadeBlendArea )
  322. { // the current pixel is within the blend band.
  323. // Because the Z coordinate species the texture array,
  324. // the derivative will be 0 when there is no divergence
  325. float fDivergence = abs( ddy( vShadowMapTextureCoord_blend.z ) ) +
  326. abs( ddx( vShadowMapTextureCoord_blend.z) );
  327. CalculateVarianceShadow ( vShadowMapTextureCoord_blend, vShadowMapTextureCoordViewSpace,
  328. iNextCascadeIndex, fPercentLit_blend );
  329. // Blend the two calculated shadows by the blend amount.
  330. fPercentLit = lerp( fPercentLit_blend, fPercentLit, fBlendBetweenCascadesAmount );
  331. }
  332. }
  333. if( !m_iVisualizeCascades ) vVisualizeCascadeColor = float4( 1.0f, 1.0f, 1.0f, 1.0f );
  334. float3 vLightDir1 = float3( -1.0f, 1.0f, -1.0f );
  335. float3 vLightDir2 = float3( 1.0f, 1.0f, -1.0f );
  336. float3 vLightDir3 = float3( 0.0f, -1.0f, 0.0f );
  337. float3 vLightDir4 = float3( 1.0f, 1.0f, 1.0f );
  338. // Some ambient-like lighting.
  339. float fLighting =
  340. saturate( dot( vLightDir1 , Input.vNormal ) )*0.05f +
  341. saturate( dot( vLightDir2 , Input.vNormal ) )*0.05f +
  342. saturate( dot( vLightDir3 , Input.vNormal ) )*0.05f +
  343. saturate( dot( vLightDir4 , Input.vNormal ) )*0.05f ;
  344. float4 vShadowLighting = fLighting * 0.5f;
  345. fLighting += saturate( dot( m_vLightDir , Input.vNormal ) );
  346. fLighting = lerp( vShadowLighting, fLighting, fPercentLit );
  347. return fLighting * vVisualizeCascadeColor * vDiffuse;
  348. }