Shadows.hlsli 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331
  1. //=================================================================================================
  2. //
  3. // MJP's DX12 Sample Framework
  4. // http://mynameismjp.wordpress.com/
  5. //
  6. // All code licensed under the MIT license
  7. //
  8. //=================================================================================================
  9. // Options
  10. #ifndef UseGatherPCF_
  11. #define UseGatherPCF_ 1
  12. #endif
  13. //#ifndef UseReceiverPlaneBias_
  14. // #define UseReceiverPlaneBias_ 1
  15. //#endif
  16. //=================================================================================================
  17. // Constants
  18. //=================================================================================================
  19. static const uint NumCascades = 4;
  20. struct ShadowConstants
  21. {
  22. row_major float4x4 ShadowMatrix;
  23. float4 CascadeSplits;
  24. float4 CascadeOffsets[NumCascades];
  25. float4 CascadeScales[NumCascades];
  26. };
  27. //-------------------------------------------------------------------------------------------------
  28. // Computes the factor used to bias the depth comparison value based on the derivatives of the
  29. // shadow-space position with respect to screen-space X and Y.
  30. //-------------------------------------------------------------------------------------------------
  31. float2 ComputeReceiverPlaneDepthBias(in float3 uvDX, in float3 uvDY)
  32. {
  33. float2 biasUV;
  34. biasUV.x = uvDY.y * uvDX.z - uvDX.y * uvDY.z;
  35. biasUV.y = uvDX.x * uvDY.z - uvDY.x * uvDX.z;
  36. biasUV *= 1.0f / ((uvDX.x * uvDY.y) - (uvDX.y * uvDY.x));
  37. return biasUV;
  38. }
  39. #if UseGatherPCF_
  40. // 7x7 disc kernel
  41. static const uint ShadowFilterSize = 7;
  42. static const float W[ShadowFilterSize][ShadowFilterSize] =
  43. {
  44. { 0.0f, 0.0f, 0.5f, 1.0f, 0.5f, 0.0f, 0.0f },
  45. { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f },
  46. { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f },
  47. { 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f },
  48. { 0.5f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.5f },
  49. { 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 0.0f },
  50. { 0.0f, 0.0f, 0.5f, 1.0f, 0.5f, 0.0f, 0.0f }
  51. };
  52. //-------------------------------------------------------------------------------------------------
  53. // Samples the shadow map with a fixed-size PCF kernel optimized with GatherCmp. Uses code
  54. // from "Fast Conventional Shadow Filtering" by Holger Gruen, in GPU Pro.
  55. //-------------------------------------------------------------------------------------------------
  56. float SampleShadowMapGatherPCF(in float3 shadowPos, in float2 planeBias, in uint shadowMapIdx,
  57. in Texture2DArray<float> shadowMap, in SamplerComparisonState shadowSampler) {
  58. float2 shadowMapSize;
  59. float numSlices;
  60. shadowMap.GetDimensions(shadowMapSize.x, shadowMapSize.y, numSlices);
  61. float2 texelSize = 1.0f / shadowMapSize;
  62. float lightDepth = shadowPos.z;
  63. // Static depth biasing to make up for incorrect fractional sampling on the shadow map grid
  64. float fractionalError = dot(float2(1.0f, 1.0f) * texelSize, abs(planeBias));
  65. lightDepth -= min(fractionalError, 0.01f);
  66. const int FS_2 = int(ShadowFilterSize) / 2;
  67. float2 tc = shadowPos.xy;
  68. float4 s = 0.0f;
  69. float2 stc = (shadowMapSize * tc.xy) + float2(0.5f, 0.5f);
  70. float2 tcs = floor(stc);
  71. float2 fc = 0.0f;
  72. float w = 0.0f;
  73. float4 v1[FS_2 + 1];
  74. float2 v0[FS_2 + 1];
  75. fc.xy = stc - tcs;
  76. tc.xy = tcs / shadowMapSize;
  77. for(uint y = 0; y < ShadowFilterSize; ++y)
  78. for(uint x = 0; x < ShadowFilterSize; ++x)
  79. w += W[y][x];
  80. // -- loop over the rows
  81. [unroll]
  82. for(int row = -FS_2; row <= FS_2; row += 2)
  83. {
  84. [unroll]
  85. for(int col = -FS_2; col <= FS_2; col += 2)
  86. {
  87. float value = W[row + FS_2][col + FS_2];
  88. if(col > -FS_2)
  89. value += W[row + FS_2][col + FS_2 - 1];
  90. if(col < FS_2)
  91. value += W[row + FS_2][col + FS_2 + 1];
  92. if(row > -FS_2) {
  93. value += W[row + FS_2 - 1][col + FS_2];
  94. if(col < FS_2)
  95. value += W[row + FS_2 - 1][col + FS_2 + 1];
  96. if(col > -FS_2)
  97. value += W[row + FS_2 - 1][col + FS_2 - 1];
  98. }
  99. if(value != 0.0f)
  100. {
  101. float sampleDepth = lightDepth;
  102. // Compute offset and apply planar depth bias
  103. float2 offset = float2(col, row) * texelSize;
  104. sampleDepth -= saturate(-dot(offset, planeBias));
  105. v1[(col + FS_2) / 2] = shadowMap.GatherCmp(shadowSampler, float3(tc.xy, shadowMapIdx), sampleDepth, int2(col, row));
  106. }
  107. else
  108. v1[(col + FS_2) / 2] = 0.0f;
  109. if(col == -FS_2)
  110. {
  111. s.x += (1.0f - fc.y) * (v1[0].w * (W[row + FS_2][col + FS_2] - W[row + FS_2][col + FS_2] * fc.x) + v1[0].z * (fc.x * (W[row + FS_2][col + FS_2]
  112. - W[row + FS_2][col + FS_2 + 1.0f]) + W[row + FS_2][col + FS_2 + 1]));
  113. s.y += fc.y * (v1[0].x * (W[row + FS_2][col + FS_2] - W[row + FS_2][col + FS_2] * fc.x)
  114. + v1[0].y * (fc.x * (W[row + FS_2][col + FS_2] - W[row + FS_2][col + FS_2 + 1])
  115. + W[row + FS_2][col + FS_2 + 1]));
  116. if(row > -FS_2)
  117. {
  118. s.z += (1.0f - fc.y) * (v0[0].x * (W[row + FS_2 - 1][col + FS_2] - W[row + FS_2 - 1][col + FS_2] * fc.x)
  119. + v0[0].y * (fc.x * (W[row + FS_2 - 1][col + FS_2] - W[row + FS_2 - 1][col + FS_2 + 1])
  120. + W[row + FS_2 - 1][col + FS_2 + 1]));
  121. s.w += fc.y * (v1[0].w * (W[row + FS_2 - 1][col + FS_2] - W[row + FS_2 - 1][col + FS_2] * fc.x)
  122. + v1[0].z * (fc.x * (W[row + FS_2 - 1][col + FS_2] - W[row + FS_2 - 1][col + FS_2 + 1])
  123. + W[row + FS_2 - 1][col + FS_2 + 1]));
  124. }
  125. }
  126. else if(col == FS_2)
  127. {
  128. s.x += (1 - fc.y) * (v1[FS_2].w * (fc.x * (W[row + FS_2][col + FS_2 - 1] - W[row + FS_2][col + FS_2]) + W[row + FS_2][col + FS_2])
  129. + v1[FS_2].z * fc.x * W[row + FS_2][col + FS_2]);
  130. s.y += fc.y * (v1[FS_2].x * (fc.x * (W[row + FS_2][col + FS_2 - 1] - W[row + FS_2][col + FS_2] ) + W[row + FS_2][col + FS_2])
  131. + v1[FS_2].y * fc.x * W[row + FS_2][col + FS_2]);
  132. if(row > -FS_2) {
  133. s.z += (1 - fc.y) * (v0[FS_2].x * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 1] - W[row + FS_2 - 1][col + FS_2])
  134. + W[row + FS_2 - 1][col + FS_2]) + v0[FS_2].y * fc.x * W[row + FS_2 - 1][col + FS_2]);
  135. s.w += fc.y * (v1[FS_2].w * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 1] - W[row + FS_2 - 1][col + FS_2])
  136. + W[row + FS_2 - 1][col + FS_2]) + v1[FS_2].z * fc.x * W[row + FS_2 - 1][col + FS_2]);
  137. }
  138. }
  139. else
  140. {
  141. s.x += (1 - fc.y) * (v1[(col + FS_2) / 2].w * (fc.x * (W[row + FS_2][col + FS_2 - 1] - W[row + FS_2][col + FS_2 + 0] ) + W[row + FS_2][col + FS_2 + 0])
  142. + v1[(col + FS_2) / 2].z * (fc.x * (W[row + FS_2][col + FS_2 - 0] - W[row + FS_2][col + FS_2 + 1]) + W[row + FS_2][col + FS_2 + 1]));
  143. s.y += fc.y * (v1[(col + FS_2) / 2].x * (fc.x * (W[row + FS_2][col + FS_2-1] - W[row + FS_2][col + FS_2 + 0]) + W[row + FS_2][col + FS_2 + 0])
  144. + v1[(col + FS_2) / 2].y * (fc.x * (W[row + FS_2][col + FS_2 - 0] - W[row + FS_2][col + FS_2 + 1]) + W[row + FS_2][col + FS_2 + 1]));
  145. if(row > -FS_2) {
  146. s.z += (1 - fc.y) * (v0[(col + FS_2) / 2].x * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 1] - W[row + FS_2 - 1][col + FS_2 + 0]) + W[row + FS_2 - 1][col + FS_2 + 0])
  147. + v0[(col + FS_2) / 2].y * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 0] - W[row + FS_2 - 1][col + FS_2 + 1]) + W[row + FS_2 - 1][col + FS_2 + 1]));
  148. s.w += fc.y * (v1[(col + FS_2) / 2].w * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 1] - W[row + FS_2 - 1][col + FS_2 + 0]) + W[row + FS_2 - 1][col + FS_2 + 0])
  149. + v1[(col + FS_2) / 2].z * (fc.x * (W[row + FS_2 - 1][col + FS_2 - 0] - W[row + FS_2 - 1][col + FS_2 + 1]) + W[row + FS_2 - 1][col + FS_2 + 1]));
  150. }
  151. }
  152. if(row != FS_2)
  153. v0[(col + FS_2) / 2] = v1[(col + FS_2) / 2].xy;
  154. }
  155. }
  156. return dot(s, 1.0f) / w;
  157. }
  158. #endif
  159. //-------------------------------------------------------------------------------------------------
  160. // Samples the shadow map with a 2x2 hardware PCF kernel
  161. //-------------------------------------------------------------------------------------------------
  162. float SampleShadowMapSimplePCF(in float3 shadowPos, in float2 planeBias, in uint shadowMapIdx,
  163. in Texture2DArray<float> shadowMap, in SamplerComparisonState shadowSampler) {
  164. float2 shadowMapSize;
  165. float numSlices;
  166. shadowMap.GetDimensions(shadowMapSize.x, shadowMapSize.y, numSlices);
  167. float2 texelSize = 1.0f / shadowMapSize;
  168. float lightDepth = shadowPos.z;
  169. // Static depth biasing to make up for incorrect fractional sampling on the shadow map grid
  170. float fractionalError = dot(float2(1.0f, 1.0f) * texelSize, abs(planeBias));
  171. lightDepth -= min(fractionalError, 0.01f);
  172. return shadowMap.SampleCmpLevelZero(shadowSampler, float3(shadowPos.xy, shadowMapIdx), lightDepth);
  173. }
  174. //--------------------------------------------------------------------------------------
  175. // Computes the visibility for a directional light using implicit derivatives
  176. //--------------------------------------------------------------------------------------
  177. float SunShadows(in float3 shadowPos, in float3 shadowPosDX, in float3 shadowPosDY, in float depthVS,
  178. in Texture2DArray<float> sunShadowMap, in SamplerComparisonState shadowSampler,
  179. in ShadowConstants shadowConstants)
  180. {
  181. // Figure out which cascade to sample from
  182. uint cascadeIdx = 0;
  183. [unroll]
  184. for(uint i = 0; i < NumCascades - 1; ++i)
  185. {
  186. [flatten]
  187. if(depthVS > shadowConstants.CascadeSplits[i])
  188. cascadeIdx = i + 1;
  189. }
  190. shadowPos += shadowConstants.CascadeOffsets[cascadeIdx].xyz;
  191. shadowPos *= shadowConstants.CascadeScales[cascadeIdx].xyz;
  192. shadowPosDX *= shadowConstants.CascadeScales[cascadeIdx].xyz;
  193. shadowPosDY *= shadowConstants.CascadeScales[cascadeIdx].xyz;
  194. #if UseReceiverPlaneBias_
  195. float2 planeBias = ComputeReceiverPlaneDepthBias(shadowPosDX, shadowPosDY);
  196. #else
  197. shadowPos.z -= 0.005f;
  198. float2 planeBias = 0.0f;
  199. #endif
  200. #if UseGatherPCF_
  201. return SampleShadowMapGatherPCF(shadowPos, planeBias, cascadeIdx, sunShadowMap, shadowSampler);
  202. #else
  203. return SampleShadowMapSimplePCF(shadowPos, planeBias, cascadeIdx, sunShadowMap, shadowSampler);
  204. #endif
  205. }
  206. //--------------------------------------------------------------------------------------
  207. // Computes the visibility for a directional light using implicit derivatives
  208. //--------------------------------------------------------------------------------------
  209. float SunShadowVisibility(in float3 positionWS, in float depthVS,
  210. in Texture2DArray<float> sunShadowMap, in SamplerComparisonState shadowSampler,
  211. in ShadowConstants shadowConstants)
  212. {
  213. // Project into shadow space
  214. float3 shadowPos = mul(shadowConstants.ShadowMatrix, float4(positionWS, 1.0f)).xyz;
  215. float3 shadowPosDX = ddx(shadowPos);
  216. float3 shadowPosDY = ddy(shadowPos);
  217. return SunShadows(shadowPos, shadowPosDX, shadowPosDY, depthVS, sunShadowMap, shadowSampler, shadowConstants);
  218. }
  219. //--------------------------------------------------------------------------------------
  220. // Computes the visibility for a directional light using explicit position derivatives
  221. //--------------------------------------------------------------------------------------
  222. float SunShadowVisibility(in float3 positionWS, in float3 positionNeighborX, in float3 positionNeighborY, in float depthVS,
  223. in Texture2DArray<float> sunShadowMap, in SamplerComparisonState shadowSampler,
  224. in ShadowConstants shadowConstants)
  225. {
  226. // Project into shadow space
  227. float3 shadowPos = mul(shadowConstants.ShadowMatrix, float4(positionWS, 1.0f)).xyz;
  228. float3 shadowPosDX = mul(shadowConstants.ShadowMatrix, float4(positionNeighborX, 1.0f)).xyz - shadowPos;
  229. float3 shadowPosDY = mul(shadowConstants.ShadowMatrix, float4(positionNeighborY, 1.0f)).xyz - shadowPos;
  230. return SunShadows(shadowPos, shadowPosDX, shadowPosDY, depthVS, sunShadowMap, shadowSampler, shadowConstants);
  231. }
  232. //--------------------------------------------------------------------------------------
  233. // Computes the visibility for a spot light using explicit position derivatives
  234. //--------------------------------------------------------------------------------------
  235. float SpotLightShadows(in float3 shadowPos, in float3 shadowPosDX, in float3 shadowPosDY,
  236. in float4x4 shadowMatrix, in uint shadowMapIdx,
  237. in Texture2DArray<float> shadowMap, in SamplerComparisonState shadowSampler)
  238. {
  239. #if UseReceiverPlaneBias_
  240. shadowPos.z -= 0.00001f;
  241. float2 planeBias = ComputeReceiverPlaneDepthBias(shadowPosDX, shadowPosDY);
  242. #else
  243. shadowPos.z -= 0.001f;
  244. float2 planeBias = 0.0f;
  245. #endif
  246. #if UseGatherPCF_
  247. return SampleShadowMapGatherPCF(shadowPos, planeBias, shadowMapIdx, shadowMap, shadowSampler);
  248. #else
  249. return SampleShadowMapSimplePCF(shadowPos, planeBias, shadowMapIdx, shadowMap, shadowSampler);
  250. #endif
  251. }
  252. //--------------------------------------------------------------------------------------
  253. // Computes the visibility for a spot light using implicit derivatives
  254. //--------------------------------------------------------------------------------------
  255. float SpotLightShadowVisibility(in float3 positionWS, in float4x4 shadowMatrix, in uint shadowMapIdx,
  256. in Texture2DArray<float> shadowMap, in SamplerComparisonState shadowSampler)
  257. {
  258. // Project into shadow space
  259. float4 shadowPos = mul(float4(positionWS, 1.0f), shadowMatrix);
  260. shadowPos.xyz /= shadowPos.w;
  261. return SpotLightShadows(shadowPos.xyz, ddx(shadowPos.xyz), ddy(shadowPos.xyz), shadowMatrix, shadowMapIdx, shadowMap, shadowSampler);
  262. }
  263. //--------------------------------------------------------------------------------------
  264. // Computes the visibility for a spot light using explicit position derivatives
  265. //--------------------------------------------------------------------------------------
  266. float SpotLightShadowVisibility(in float3 positionWS, in float3 positionNeighborX, in float3 positionNeighborY,
  267. in float4x4 shadowMatrix, in uint shadowMapIdx,
  268. in Texture2DArray<float> shadowMap, in SamplerComparisonState shadowSampler)
  269. {
  270. // Project into shadow space
  271. float4 shadowPos = mul(float4(positionWS, 1.0f), shadowMatrix);
  272. shadowPos.xyz /= shadowPos.w;
  273. float4 shadowPosDX = mul(float4(positionNeighborX, 1.0f), shadowMatrix);
  274. shadowPosDX.xyz /= shadowPosDX.w;
  275. shadowPosDX.xyz -= shadowPos.xyz;
  276. float4 shadowPosDY = mul(float4(positionNeighborY, 1.0f), shadowMatrix);
  277. shadowPosDY.xyz /= shadowPosDY.w;
  278. shadowPosDY.xyz -= shadowPos.xyz;
  279. return SpotLightShadows(shadowPos.xyz, shadowPosDX.xyz, shadowPosDY.xyz, shadowMatrix, shadowMapIdx, shadowMap, shadowSampler);
  280. }