Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

821 lines
36 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Common pixel shader code specific to flashlights
  4. //
  5. // $NoKeywords: $
  6. //
  7. //=============================================================================//
  8. #ifndef COMMON_FLASHLIGHT_FXC_H_
  9. #define COMMON_FLASHLIGHT_FXC_H_
  10. #include "common_ps_fxc.h"
  11. // JasonM - TODO: remove this simpleton version
  12. float DoShadow( sampler DepthSampler, float4 texCoord )
  13. {
  14. const float g_flShadowBias = 0.0005f;
  15. float2 uoffset = float2( 0.5f/512.f, 0.0f );
  16. float2 voffset = float2( 0.0f, 0.5f/512.f );
  17. float3 projTexCoord = texCoord.xyz / texCoord.w;
  18. float4 flashlightDepth = float4( tex2D( DepthSampler, projTexCoord + uoffset + voffset ).x,
  19. tex2D( DepthSampler, projTexCoord + uoffset - voffset ).x,
  20. tex2D( DepthSampler, projTexCoord - uoffset + voffset ).x,
  21. tex2D( DepthSampler, projTexCoord - uoffset - voffset ).x );
  22. # if ( defined( REVERSE_DEPTH_ON_X360 ) )
  23. {
  24. flashlightDepth = 1.0f - flashlightDepth;
  25. }
  26. # endif
  27. float shadowed = 0.0f;
  28. float z = texCoord.z/texCoord.w;
  29. float4 dz = float4(z,z,z,z) - (flashlightDepth + float4( g_flShadowBias, g_flShadowBias, g_flShadowBias, g_flShadowBias));
  30. float4 shadow = float4(0.25f,0.25f,0.25f,0.25f);
  31. if( dz.x <= 0.0f )
  32. shadowed += shadow.x;
  33. if( dz.y <= 0.0f )
  34. shadowed += shadow.y;
  35. if( dz.z <= 0.0f )
  36. shadowed += shadow.z;
  37. if( dz.w <= 0.0f )
  38. shadowed += shadow.w;
  39. return shadowed;
  40. }
  41. float DoShadowNvidiaRAWZOneTap( sampler DepthSampler, const float4 shadowMapPos )
  42. {
  43. float ooW = 1.0f / shadowMapPos.w; // 1 / w
  44. float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once
  45. float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter
  46. float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space
  47. float fDepth = dot(tex2D(DepthSampler, shadowMapCenter).arg, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5));
  48. return fDepth > objDepth;
  49. }
  50. float DoShadowNvidiaRAWZ( sampler DepthSampler, const float4 shadowMapPos )
  51. {
  52. float fE = 1.0f / 512.0f; // Epsilon
  53. float ooW = 1.0f / shadowMapPos.w; // 1 / w
  54. float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once
  55. float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter
  56. float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space
  57. float4 vDepths;
  58. vDepths.x = dot(tex2D(DepthSampler, shadowMapCenter + float2( fE, fE )).arg, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5));
  59. vDepths.y = dot(tex2D(DepthSampler, shadowMapCenter + float2( -fE, fE )).arg, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5));
  60. vDepths.z = dot(tex2D(DepthSampler, shadowMapCenter + float2( fE, -fE )).arg, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5));
  61. vDepths.w = dot(tex2D(DepthSampler, shadowMapCenter + float2( -fE, -fE )).arg, float3(0.996093809371817670572857294849, 0.0038909914428586627756752238080039, 1.5199185323666651467481343000015e-5));
  62. return dot(vDepths > objDepth.xxxx, float4(0.25, 0.25, 0.25, 0.25));
  63. }
  64. float DoShadowNvidiaCheap( sampler DepthSampler, const float4 shadowMapPos )
  65. {
  66. float fTexelEpsilon = 1.0f / 1024.0f;
  67. float ooW = 1.0f / shadowMapPos.w; // 1 / w
  68. float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once
  69. float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter
  70. float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space
  71. float4 vTaps;
  72. vTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTexelEpsilon, fTexelEpsilon), objDepth, 1 ) ).x;
  73. vTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTexelEpsilon, fTexelEpsilon), objDepth, 1 ) ).x;
  74. vTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTexelEpsilon, -fTexelEpsilon), objDepth, 1 ) ).x;
  75. vTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTexelEpsilon, -fTexelEpsilon), objDepth, 1 ) ).x;
  76. return dot(vTaps, float4(0.25, 0.25, 0.25, 0.25));
  77. }
  78. float DoShadowNvidiaPCF3x3Box( sampler DepthSampler, const float4 shadowMapPos )
  79. {
  80. float fTexelEpsilon = 1.0f / 1024.0f;
  81. float ooW = 1.0f / shadowMapPos.w; // 1 / w
  82. float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once
  83. float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter
  84. float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space
  85. float4 vOneTaps;
  86. vOneTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTexelEpsilon, fTexelEpsilon ), objDepth, 1 ) ).x;
  87. vOneTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTexelEpsilon, fTexelEpsilon ), objDepth, 1 ) ).x;
  88. vOneTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTexelEpsilon, -fTexelEpsilon ), objDepth, 1 ) ).x;
  89. vOneTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTexelEpsilon, -fTexelEpsilon ), objDepth, 1 ) ).x;
  90. float flOneTaps = dot( vOneTaps, float4(1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f));
  91. float4 vTwoTaps;
  92. vTwoTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTexelEpsilon, 0 ), objDepth, 1 ) ).x;
  93. vTwoTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTexelEpsilon, 0 ), objDepth, 1 ) ).x;
  94. vTwoTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTexelEpsilon ), objDepth, 1 ) ).x;
  95. vTwoTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTexelEpsilon ), objDepth, 1 ) ).x;
  96. float flTwoTaps = dot( vTwoTaps, float4(1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f, 1.0f / 9.0f));
  97. float flCenterTap = tex2Dproj( DepthSampler, float4( shadowMapCenter, objDepth, 1 ) ).x * (1.0f / 9.0f);
  98. // Sum all 9 Taps
  99. return flOneTaps + flTwoTaps + flCenterTap;
  100. }
  101. //
  102. // 1 4 7 4 1
  103. // 4 20 33 20 4
  104. // 7 33 55 33 7
  105. // 4 20 33 20 4
  106. // 1 4 7 4 1
  107. //
  108. float DoShadowNvidiaPCF5x5Gaussian( sampler DepthSampler, const float4 shadowMapPos )
  109. {
  110. float fEpsilon = 1.0f / 512.0f;
  111. float fTwoEpsilon = 2.0f * fEpsilon;
  112. float ooW = 1.0f / shadowMapPos.w; // 1 / w
  113. float3 shadowMapCenter_objDepth = shadowMapPos.xyz * ooW; // Do both projections at once
  114. float2 shadowMapCenter = shadowMapCenter_objDepth.xy; // Center of shadow filter
  115. float objDepth = shadowMapCenter_objDepth.z; // Object depth in shadow space
  116. float4 vOneTaps;
  117. vOneTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x;
  118. vOneTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x;
  119. vOneTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x;
  120. vOneTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x;
  121. float flOneTaps = dot( vOneTaps, float4(1.0f / 331.0f, 1.0f / 331.0f, 1.0f / 331.0f, 1.0f / 331.0f));
  122. float4 vSevenTaps;
  123. vSevenTaps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, 0 ), objDepth, 1 ) ).x;
  124. vSevenTaps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, 0 ), objDepth, 1 ) ).x;
  125. vSevenTaps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTwoEpsilon ), objDepth, 1 ) ).x;
  126. vSevenTaps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fTwoEpsilon ), objDepth, 1 ) ).x;
  127. float flSevenTaps = dot( vSevenTaps, float4( 7.0f / 331.0f, 7.0f / 331.0f, 7.0f / 331.0f, 7.0f / 331.0f ) );
  128. float4 vFourTapsA, vFourTapsB;
  129. vFourTapsA.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, fEpsilon ), objDepth, 1 ) ).x;
  130. vFourTapsA.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x;
  131. vFourTapsA.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, fTwoEpsilon ), objDepth, 1 ) ).x;
  132. vFourTapsA.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, fEpsilon ), objDepth, 1 ) ).x;
  133. vFourTapsB.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fTwoEpsilon, -fEpsilon ), objDepth, 1 ) ).x;
  134. vFourTapsB.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x;
  135. vFourTapsB.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, -fTwoEpsilon ), objDepth, 1 ) ).x;
  136. vFourTapsB.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fTwoEpsilon, -fEpsilon ), objDepth, 1 ) ).x;
  137. float flFourTapsA = dot( vFourTapsA, float4( 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f ) );
  138. float flFourTapsB = dot( vFourTapsB, float4( 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f, 4.0f / 331.0f ) );
  139. float4 v20Taps;
  140. v20Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, fEpsilon ), objDepth, 1 ) ).x;
  141. v20Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, fEpsilon ), objDepth, 1 ) ).x;
  142. v20Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, -fEpsilon ), objDepth, 1 ) ).x;
  143. v20Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, -fEpsilon ), objDepth, 1 ) ).x;
  144. float fl20Taps = dot( v20Taps, float4(20.0f / 331.0f, 20.0f / 331.0f, 20.0f / 331.0f, 20.0f / 331.0f));
  145. float4 v33Taps;
  146. v33Taps.x = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( fEpsilon, 0 ), objDepth, 1 ) ).x;
  147. v33Taps.y = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( -fEpsilon, 0 ), objDepth, 1 ) ).x;
  148. v33Taps.z = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fEpsilon ), objDepth, 1 ) ).x;
  149. v33Taps.w = tex2Dproj( DepthSampler, float4( shadowMapCenter + float2( 0, -fEpsilon ), objDepth, 1 ) ).x;
  150. float fl33Taps = dot( v33Taps, float4(33.0f / 331.0f, 33.0f / 331.0f, 33.0f / 331.0f, 33.0f / 331.0f));
  151. float flCenterTap = tex2Dproj( DepthSampler, float4( shadowMapCenter, objDepth, 1 ) ).x * (55.0f / 331.0f);
  152. // Sum all 25 Taps
  153. return flOneTaps + flSevenTaps + +flFourTapsA + flFourTapsB + fl20Taps + fl33Taps + flCenterTap;
  154. }
  155. float DoShadowATICheap( sampler DepthSampler, const float4 shadowMapPos )
  156. {
  157. float2 shadowMapCenter = shadowMapPos.xy/shadowMapPos.w;
  158. float objDepth = shadowMapPos.z / shadowMapPos.w;
  159. float fSampleDepth = tex2D( DepthSampler, shadowMapCenter ).x;
  160. objDepth = min( objDepth, 0.99999 ); //HACKHACK: On 360, surfaces at or past the far flashlight plane have an abrupt cutoff. This is temp until a smooth falloff is implemented
  161. return fSampleDepth > objDepth;
  162. }
  163. // Poisson disc, randomly rotated at different UVs
  164. float DoShadowPoisson16Sample( sampler DepthSampler, sampler RandomRotationSampler, const float3 vProjCoords, const float2 vScreenPos, const float4 vShadowTweaks, bool bNvidiaHardwarePCF, bool bFetch4 )
  165. {
  166. float2 vPoissonOffset[8] = { float2( 0.3475f, 0.0042f ),
  167. float2( 0.8806f, 0.3430f ),
  168. float2( -0.0041f, -0.6197f ),
  169. float2( 0.0472f, 0.4964f ),
  170. float2( -0.3730f, 0.0874f ),
  171. float2( -0.9217f, -0.3177f ),
  172. float2( -0.6289f, 0.7388f ),
  173. float2( 0.5744f, -0.7741f ) };
  174. float flScaleOverMapSize = vShadowTweaks.x * 2; // Tweak parameters to shader
  175. float2 vNoiseOffset = vShadowTweaks.zw;
  176. float4 vLightDepths = 0, accum = 0.0f;
  177. float2 rotOffset = 0;
  178. float2 shadowMapCenter = vProjCoords.xy; // Center of shadow filter
  179. float objDepth = min( vProjCoords.z, 0.99999 ); // Object depth in shadow space
  180. // 2D Rotation Matrix setup
  181. float3 RMatTop = 0, RMatBottom = 0;
  182. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  183. RMatTop.xy = tex2D( RandomRotationSampler, cFlashlightScreenScale.xy * (vScreenPos * 0.5 + 0.5) + vNoiseOffset) * 2.0 - 1.0;
  184. RMatBottom.xy = float2(-1.0, 1.0) * RMatTop.yx; // 2x2 rotation matrix in 4-tuple
  185. #endif
  186. RMatTop *= flScaleOverMapSize; // Scale up kernel while accounting for texture resolution
  187. RMatBottom *= flScaleOverMapSize;
  188. RMatTop.z = shadowMapCenter.x; // To be added in d2adds generated below
  189. RMatBottom.z = shadowMapCenter.y;
  190. float fResult = 0.0f;
  191. if ( bNvidiaHardwarePCF )
  192. {
  193. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[0].xy) + RMatTop.z;
  194. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[0].xy) + RMatBottom.z;
  195. vLightDepths.x += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  196. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[1].xy) + RMatTop.z;
  197. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[1].xy) + RMatBottom.z;
  198. vLightDepths.y += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  199. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[2].xy) + RMatTop.z;
  200. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[2].xy) + RMatBottom.z;
  201. vLightDepths.z += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  202. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[3].xy) + RMatTop.z;
  203. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[3].xy) + RMatBottom.z;
  204. vLightDepths.w += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  205. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4].xy) + RMatTop.z;
  206. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4].xy) + RMatBottom.z;
  207. vLightDepths.x += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  208. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[5].xy) + RMatTop.z;
  209. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[5].xy) + RMatBottom.z;
  210. vLightDepths.y += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  211. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[6].xy) + RMatTop.z;
  212. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[6].xy) + RMatBottom.z;
  213. vLightDepths.z += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  214. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[7].xy) + RMatTop.z;
  215. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[7].xy) + RMatBottom.z;
  216. vLightDepths.w += tex2Dproj( DepthSampler, float4(rotOffset, objDepth, 1) ).x;
  217. fResult = dot( vLightDepths, float4( 0.25, 0.25, 0.25, 0.25) );
  218. }
  219. else if ( bFetch4 )
  220. {
  221. /*
  222. TODO: Fix this contact hardening stuff
  223. float flNumCloserSamples = 1;
  224. float flAccumulatedCloserSamples = objDepth;
  225. float4 vBlockerDepths;
  226. // First, search for blockers
  227. for( int j=0; j<8; j++ )
  228. {
  229. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[j].xy) + RMatTop.z;
  230. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[j].xy) + RMatBottom.z;
  231. vBlockerDepths = tex2D( DepthSampler, rotOffset.xy );
  232. // Which samples are closer than the pixel we're rendering?
  233. float4 vCloserSamples = (vBlockerDepths < objDepth.xxxx ); // Binary comparison results
  234. flNumCloserSamples += dot( vCloserSamples, float4(1, 1, 1, 1) ); // How many samples are closer than receiver?
  235. flAccumulatedCloserSamples += dot (vCloserSamples, vBlockerDepths ); // Total depths from samples closer than receiver
  236. }
  237. float flBlockerDepth = flAccumulatedCloserSamples / flNumCloserSamples;
  238. float flContactHardeningScale = (objDepth - flBlockerDepth) / flBlockerDepth;
  239. // Scale the kernel
  240. RMatTop.xy *= flContactHardeningScale;
  241. RMatBottom.xy *= flContactHardeningScale;
  242. */
  243. for( int i=0; i<8; i++ )
  244. {
  245. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[i].xy) + RMatTop.z;
  246. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[i].xy) + RMatBottom.z;
  247. vLightDepths = tex2D( DepthSampler, rotOffset.xy );
  248. accum += (vLightDepths > objDepth.xxxx);
  249. }
  250. fResult = dot( accum, float4( 1.0f/32.0f, 1.0f/32.0f, 1.0f/32.0f, 1.0f/32.0f) );
  251. }
  252. else // ATI vanilla hardware shadow mapping
  253. {
  254. for( int i=0; i<2; i++ )
  255. {
  256. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4*i+0].xy) + RMatTop.z;
  257. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4*i+0].xy) + RMatBottom.z;
  258. vLightDepths.x = tex2D( DepthSampler, rotOffset.xy ).x;
  259. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4*i+1].xy) + RMatTop.z;
  260. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4*i+1].xy) + RMatBottom.z;
  261. vLightDepths.y = tex2D( DepthSampler, rotOffset.xy ).x;
  262. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4*i+2].xy) + RMatTop.z;
  263. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4*i+2].xy) + RMatBottom.z;
  264. vLightDepths.z = tex2D( DepthSampler, rotOffset.xy ).x;
  265. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4*i+3].xy) + RMatTop.z;
  266. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4*i+3].xy) + RMatBottom.z;
  267. vLightDepths.w = tex2D( DepthSampler, rotOffset.xy ).x;
  268. accum += (vLightDepths > objDepth.xxxx);
  269. }
  270. fResult = dot( accum, float4( 0.125, 0.125, 0.125, 0.125) );
  271. }
  272. return fResult;
  273. }
  274. #if defined( _X360 )
  275. // Poisson disc, randomly rotated at different UVs
  276. float DoShadow360Simple( sampler DepthSampler, const float3 vProjCoords )
  277. {
  278. float fLOD;
  279. float2 shadowMapCenter = vProjCoords.xy; // Center of shadow filter
  280. float objDepth = min( vProjCoords.z, 0.99999 ); // Object depth in shadow space
  281. #if defined( REVERSE_DEPTH_ON_X360 )
  282. objDepth = 1.0f - objDepth;
  283. #endif
  284. float4 vSampledDepths, vWeights;
  285. asm {
  286. getCompTexLOD2D fLOD.x, shadowMapCenter.xy, DepthSampler, AnisoFilter=max16to1
  287. setTexLOD fLOD.x
  288. tfetch2D vSampledDepths.x___, shadowMapCenter, DepthSampler, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  289. tfetch2D vSampledDepths._x__, shadowMapCenter, DepthSampler, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  290. tfetch2D vSampledDepths.__x_, shadowMapCenter, DepthSampler, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  291. tfetch2D vSampledDepths.___x, shadowMapCenter, DepthSampler, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  292. getWeights2D vWeights, shadowMapCenter.xy, DepthSampler, MagFilter=linear, MinFilter=linear, UseComputedLOD=false, UseRegisterLOD=true
  293. };
  294. vWeights = float4( (1-vWeights.x)*(1-vWeights.y), vWeights.x*(1-vWeights.y), (1-vWeights.x)*vWeights.y, vWeights.x*vWeights.y );
  295. #if defined( REVERSE_DEPTH_ON_X360 )
  296. float4 vCompare = (vSampledDepths < objDepth.xxxx);
  297. #else
  298. float4 vCompare = (vSampledDepths > objDepth.xxxx);
  299. #endif
  300. return dot( vCompare, vWeights );
  301. }
  302. float Do360PCFFetch( sampler DepthSampler, float2 tc, float objDepth )
  303. {
  304. float fLOD;
  305. float4 vSampledDepths, vWeights;
  306. asm {
  307. getCompTexLOD2D fLOD.x, tc.xy, DepthSampler, AnisoFilter=max16to1
  308. setTexLOD fLOD.x
  309. tfetch2D vSampledDepths.x___, tc, DepthSampler, OffsetX = -0.5, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  310. tfetch2D vSampledDepths._x__, tc, DepthSampler, OffsetX = 0.5, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  311. tfetch2D vSampledDepths.__x_, tc, DepthSampler, OffsetX = -0.5, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  312. tfetch2D vSampledDepths.___x, tc, DepthSampler, OffsetX = 0.5, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  313. getWeights2D vWeights, tc.xy, DepthSampler, MagFilter=linear, MinFilter=linear, UseComputedLOD=false, UseRegisterLOD=true
  314. };
  315. vWeights = float4( (1-vWeights.x)*(1-vWeights.y), vWeights.x*(1-vWeights.y), (1-vWeights.x)*vWeights.y, vWeights.x*vWeights.y );
  316. #if defined( REVERSE_DEPTH_ON_X360 )
  317. float4 vCompare = (vSampledDepths < objDepth.xxxx);
  318. #else
  319. float4 vCompare = (vSampledDepths > objDepth.xxxx);
  320. #endif
  321. return dot( vCompare, vWeights );
  322. }
  323. float Do360NearestFetch( sampler DepthSampler, float2 tc, float objDepth )
  324. {
  325. float fLOD;
  326. float4 vSampledDepth;
  327. asm {
  328. getCompTexLOD2D fLOD.x, tc.xy, DepthSampler, AnisoFilter=max16to1
  329. setTexLOD fLOD.x
  330. tfetch2D vSampledDepth.x___, tc, DepthSampler, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  331. };
  332. #if defined( REVERSE_DEPTH_ON_X360 )
  333. return (vSampledDepth.x < objDepth.x);
  334. #else
  335. return (vSampledDepth.x > objDepth.x);
  336. #endif
  337. }
  338. float AmountShadowed_8Tap_360( sampler DepthSampler, float2 tc, float objDepth )
  339. {
  340. float fLOD;
  341. float4 vSampledDepthsA, vSampledDepthsB;
  342. // Optimal 8 rooks pattern to get an idea about whether we're at a penumbra or not
  343. // From [Kallio07] "Scanline Edge-Flag Algorithm for Antialiasing"
  344. //
  345. // +---+---+---+---+---+---+---+---+
  346. // | | | | | | o | | |
  347. // +---+---+---+---+---+---+---+---+
  348. // | o | | | | | | | |
  349. // +---+---+---+---+---+---+---+---+
  350. // | | | | o | | | | |
  351. // +---+---+---+---+---+---+---+---+
  352. // | | | | | | | o | |
  353. // +---+---+---+---+---+---+---+---+
  354. // | | o | | | | | | |
  355. // +---+---+---+---+---+---+---+---+
  356. // | | | | | o | | | |
  357. // +---+---+---+---+---+---+---+---+
  358. // | | | | | | | | o |
  359. // +---+---+---+---+---+---+---+---+
  360. // | | | o | | | | | |
  361. // +---+---+---+---+---+---+---+---+
  362. //
  363. asm {
  364. getCompTexLOD2D fLOD.x, tc.xy, DepthSampler, AnisoFilter=max16to1
  365. setTexLOD fLOD.x
  366. tfetch2D vSampledDepthsA.x___, tc, DepthSampler, OffsetX = -2.0, OffsetY = -1.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  367. tfetch2D vSampledDepthsA._x__, tc, DepthSampler, OffsetX = -1.5, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  368. tfetch2D vSampledDepthsA.__x_, tc, DepthSampler, OffsetX = -1.0, OffsetY = 2.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  369. tfetch2D vSampledDepthsA.___x, tc, DepthSampler, OffsetX = -0.5, OffsetY = -1.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  370. tfetch2D vSampledDepthsB.x___, tc, DepthSampler, OffsetX = 0.5, OffsetY = 1.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  371. tfetch2D vSampledDepthsB._x__, tc, DepthSampler, OffsetX = 1.0, OffsetY = -2.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  372. tfetch2D vSampledDepthsB.__x_, tc, DepthSampler, OffsetX = 1.5, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  373. tfetch2D vSampledDepthsB.___x, tc, DepthSampler, OffsetX = 2.0, OffsetY = 1.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  374. };
  375. #if defined( REVERSE_DEPTH_ON_X360 )
  376. float4 vCompareA = (vSampledDepthsA < objDepth.xxxx);
  377. float4 vCompareB = (vSampledDepthsB < objDepth.xxxx);
  378. #else
  379. float4 vCompareA = (vSampledDepthsA > objDepth.xxxx);
  380. float4 vCompareB = (vSampledDepthsB > objDepth.xxxx);
  381. #endif
  382. return dot( vCompareA, float4(0.125,0.125,0.125,0.125) ) + dot( vCompareB, float4(0.125,0.125,0.125,0.125) );
  383. }
  384. float AmountShadowed_4Tap_360( sampler DepthSampler, float2 tc, float objDepth )
  385. {
  386. float fLOD;
  387. float4 vSampledDepths;
  388. // Rotated grid pattern to get an idea about whether we're at a penumbra or not
  389. asm {
  390. getCompTexLOD2D fLOD.x, tc.xy, DepthSampler, AnisoFilter=max16to1
  391. setTexLOD fLOD.x
  392. tfetch2D vSampledDepths.x___, tc, DepthSampler, OffsetX = -1.0, OffsetY = 0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  393. tfetch2D vSampledDepths._x__, tc, DepthSampler, OffsetX = -0.5, OffsetY = -1.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  394. tfetch2D vSampledDepths.__x_, tc, DepthSampler, OffsetX = 0.5, OffsetY = 1.0, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  395. tfetch2D vSampledDepths.___x, tc, DepthSampler, OffsetX = 1.0, OffsetY = -0.5, UseComputedLOD=false, UseRegisterLOD=true, MagFilter = point, MinFilter = point
  396. };
  397. #if defined( REVERSE_DEPTH_ON_X360 )
  398. float4 vCompare = (vSampledDepths < objDepth.xxxx);
  399. #else
  400. float4 vCompare = (vSampledDepths > objDepth.xxxx);
  401. #endif
  402. return dot( vCompare, float4(0.25,0.25,0.25,0.25) );
  403. }
  404. // Poisson disc, randomly rotated at different UVs
  405. float DoShadowPoisson360( sampler DepthSampler, sampler RandomRotationSampler, const float3 vProjCoords, const float2 vScreenPos, const float4 vShadowTweaks )
  406. {
  407. float2 vPoissonOffset[8] = { float2( 0.3475f, 0.0042f ), float2( 0.8806f, 0.3430f ),
  408. float2( -0.0041f, -0.6197f ), float2( 0.0472f, 0.4964f ),
  409. float2( -0.3730f, 0.0874f ), float2( -0.9217f, -0.3177f ),
  410. float2( -0.6289f, 0.7388f ), float2( 0.5744f, -0.7741f ) };
  411. float2 shadowMapCenter = vProjCoords.xy; // Center of shadow filter
  412. float objDepth = min( vProjCoords.z, 0.99999 ); // Object depth in shadow space
  413. #if defined( REVERSE_DEPTH_ON_X360 )
  414. objDepth = 1.0f - objDepth;
  415. #endif
  416. float fAmountShadowed = AmountShadowed_4Tap_360( DepthSampler, shadowMapCenter, objDepth );
  417. if ( fAmountShadowed >= 1.0f ) // Fully in light
  418. {
  419. return 1.0f;
  420. }
  421. else // Do the expensive filtering since we're at least partially shadowed
  422. {
  423. float flScaleOverMapSize = 1.7f / 512.0f; // Tweak parameters to shader
  424. // 2D Rotation Matrix setup
  425. float3 RMatTop = 0, RMatBottom = 0;
  426. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  427. RMatTop.xy = tex2D( RandomRotationSampler, cFlashlightScreenScale.xy * (vScreenPos * 0.5 + 0.5)) * 2.0 - 1.0;
  428. RMatBottom.xy = float2(-1.0, 1.0) * RMatTop.yx; // 2x2 rotation matrix in 4-tuple
  429. #endif
  430. RMatTop *= flScaleOverMapSize; // Scale up kernel while accounting for texture resolution
  431. RMatBottom *= flScaleOverMapSize;
  432. RMatTop.z = shadowMapCenter.x; // To be added in d2adds generated below
  433. RMatBottom.z = shadowMapCenter.y;
  434. float2 rotOffset = float2(0,0);
  435. float4 vAccum = 0;
  436. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[0].xy) + RMatTop.z;
  437. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[0].xy) + RMatBottom.z;
  438. vAccum.x = Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  439. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[1].xy) + RMatTop.z;
  440. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[1].xy) + RMatBottom.z;
  441. vAccum.y = Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  442. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[2].xy) + RMatTop.z;
  443. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[2].xy) + RMatBottom.z;
  444. vAccum.z = Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  445. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[3].xy) + RMatTop.z;
  446. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[3].xy) + RMatBottom.z;
  447. vAccum.w = Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  448. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[4].xy) + RMatTop.z;
  449. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[4].xy) + RMatBottom.z;
  450. vAccum.x += Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  451. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[5].xy) + RMatTop.z;
  452. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[5].xy) + RMatBottom.z;
  453. vAccum.y += Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  454. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[6].xy) + RMatTop.z;
  455. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[6].xy) + RMatBottom.z;
  456. vAccum.z += Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  457. rotOffset.x = dot (RMatTop.xy, vPoissonOffset[7].xy) + RMatTop.z;
  458. rotOffset.y = dot (RMatBottom.xy, vPoissonOffset[7].xy) + RMatBottom.z;
  459. vAccum.w += Do360NearestFetch( DepthSampler, rotOffset, objDepth );
  460. return dot( vAccum, float4( 0.25, 0.25, 0.25, 0.25) );
  461. }
  462. }
  463. #endif // _X360
  464. float DoFlashlightShadow( sampler DepthSampler, sampler RandomRotationSampler, float3 vProjCoords, float2 vScreenPos, int nShadowLevel, float4 vShadowTweaks, bool bAllowHighQuality )
  465. {
  466. float flShadow = 1.0f;
  467. #if !defined( _X360 ) //PC
  468. if( nShadowLevel == NVIDIA_PCF_POISSON )
  469. flShadow = DoShadowPoisson16Sample( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, true, false );
  470. else if( nShadowLevel == ATI_NOPCF )
  471. flShadow = DoShadowPoisson16Sample( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, false );
  472. else if( nShadowLevel == ATI_NO_PCF_FETCH4 )
  473. flShadow = DoShadowPoisson16Sample( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks, false, true );
  474. return flShadow;
  475. #else
  476. // Compile-time switch for shaders which allow high quality modes on 360
  477. if ( bAllowHighQuality )
  478. {
  479. // Static control flow switch for shadow quality. Some non-interactive sequences use the high quality path
  480. if ( g_bHighQualityShadows )
  481. {
  482. flShadow = DoShadowPoisson360( DepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, vShadowTweaks );
  483. }
  484. else
  485. {
  486. flShadow = DoShadow360Simple( DepthSampler, vProjCoords );
  487. }
  488. }
  489. else
  490. {
  491. flShadow = DoShadow360Simple( DepthSampler, vProjCoords );
  492. }
  493. return flShadow;
  494. #endif
  495. }
  496. float3 SpecularLight( const float3 vWorldNormal, const float3 vLightDir, const float fSpecularExponent,
  497. const float3 vEyeDir, const bool bDoSpecularWarp, in sampler specularWarpSampler, float fFresnel )
  498. {
  499. float3 result = float3(0.0f, 0.0f, 0.0f);
  500. //float3 vReflect = reflect( -vEyeDir, vWorldNormal );
  501. float3 vReflect = 2 * vWorldNormal * dot( vWorldNormal , vEyeDir ) - vEyeDir; // Reflect view through normal
  502. float3 vSpecular = saturate(dot( vReflect, vLightDir )); // L.R (use half-angle instead?)
  503. vSpecular = pow( vSpecular.x, fSpecularExponent ); // Raise to specular power
  504. // Optionally warp as function of scalar specular and fresnel
  505. if ( bDoSpecularWarp )
  506. vSpecular *= tex2D( specularWarpSampler, float2(vSpecular.x, fFresnel) ); // Sample at { (L.R)^k, fresnel }
  507. return vSpecular;
  508. }
  509. void DoSpecularFlashlight( float3 flashlightPos, float3 worldPos, float4 flashlightSpacePosition, float3 worldNormal,
  510. float3 attenuationFactors, float farZ, sampler FlashlightSampler, sampler FlashlightDepthSampler, sampler RandomRotationSampler,
  511. int nShadowLevel, bool bDoShadows, bool bAllowHighQuality, const float2 vScreenPos, const float fSpecularExponent, const float3 vEyeDir,
  512. const bool bDoSpecularWarp, sampler specularWarpSampler, float fFresnel, float4 vShadowTweaks,
  513. // Outputs of this shader...separate shadowed diffuse and specular from the flashlight
  514. out float3 diffuseLighting, out float3 specularLighting )
  515. {
  516. float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
  517. float3 flashlightColor = float3(1,1,1);
  518. #if ( defined( _X360 ) )
  519. float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f );
  520. float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f );
  521. [branch]
  522. if ( dot(ltz + gto, float3(1,1,1)) > 0 )
  523. {
  524. clip(-1);
  525. diffuseLighting = specularLighting = float3(0,0,0);
  526. return;
  527. }
  528. else
  529. {
  530. flashlightColor = tex2D( FlashlightSampler, vProjCoords );
  531. [branch]
  532. if ( dot(flashlightColor.xyz, float3(1,1,1)) <= 0 )
  533. {
  534. clip(-1);
  535. diffuseLighting = specularLighting = float3(0,0,0);
  536. return;
  537. }
  538. }
  539. #else
  540. flashlightColor = tex2D( FlashlightSampler, vProjCoords );
  541. #endif
  542. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  543. flashlightColor *= cFlashlightColor.xyz; // Flashlight color
  544. #endif
  545. float3 delta = flashlightPos - worldPos;
  546. float3 L = normalize( delta );
  547. float distSquared = dot( delta, delta );
  548. float dist = sqrt( distSquared );
  549. float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f );
  550. // Attenuation for light and to fade out shadow over distance
  551. float fAtten = saturate( dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) );
  552. // Shadowing and coloring terms
  553. #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))
  554. if ( bDoShadows )
  555. {
  556. float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality );
  557. float flAttenuated = lerp( flShadow, 1.0f, vShadowTweaks.y ); // Blend between fully attenuated and not attenuated
  558. flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation
  559. flashlightColor *= flShadow; // Shadow term
  560. }
  561. #endif
  562. diffuseLighting = fAtten;
  563. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  564. diffuseLighting *= saturate( dot( L.xyz, worldNormal.xyz ) + flFlashlightNoLambertValue ); // Lambertian term
  565. #else
  566. diffuseLighting *= saturate( dot( L.xyz, worldNormal.xyz ) ); // Lambertian (not Half-Lambert) term
  567. #endif
  568. diffuseLighting *= flashlightColor;
  569. diffuseLighting *= endFalloffFactor;
  570. // Specular term (masked by diffuse)
  571. specularLighting = diffuseLighting * SpecularLight ( worldNormal, L, fSpecularExponent, vEyeDir, bDoSpecularWarp, specularWarpSampler, fFresnel );
  572. }
  573. // Diffuse only version
  574. float3 DoFlashlight( float3 flashlightPos, float3 worldPos, float4 flashlightSpacePosition, float3 worldNormal,
  575. float3 attenuationFactors, float farZ, sampler FlashlightSampler, sampler FlashlightDepthSampler,
  576. sampler RandomRotationSampler, int nShadowLevel, bool bDoShadows, bool bAllowHighQuality,
  577. const float2 vScreenPos, bool bClip, float4 vShadowTweaks = float4(3/1024.0f, 0.0005f, 0.0f, 0.0f), bool bHasNormal = true )
  578. {
  579. float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
  580. float3 flashlightColor = float3(1,1,1);
  581. #if ( defined( _X360 ) )
  582. float3 ltz = vProjCoords.xyz < float3( 0.0f, 0.0f, 0.0f );
  583. float3 gto = vProjCoords.xyz > float3( 1.0f, 1.0f, 1.0f );
  584. [branch]
  585. if ( dot(ltz + gto, float3(1,1,1)) > 0 )
  586. {
  587. if ( bClip )
  588. {
  589. clip(-1);
  590. }
  591. return float3(0,0,0);
  592. }
  593. else
  594. {
  595. flashlightColor = tex2D( FlashlightSampler, vProjCoords );
  596. [branch]
  597. if ( dot(flashlightColor.xyz, float3(1,1,1)) <= 0 )
  598. {
  599. if ( bClip )
  600. {
  601. clip(-1);
  602. }
  603. return float3(0,0,0);
  604. }
  605. }
  606. #else
  607. flashlightColor = tex2D( FlashlightSampler, vProjCoords );
  608. #endif
  609. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  610. flashlightColor *= cFlashlightColor.xyz; // Flashlight color
  611. #endif
  612. float3 delta = flashlightPos - worldPos;
  613. float3 L = normalize( delta );
  614. float distSquared = dot( delta, delta );
  615. float dist = sqrt( distSquared );
  616. float endFalloffFactor = RemapValClamped( dist, farZ, 0.6f * farZ, 0.0f, 1.0f );
  617. // Attenuation for light and to fade out shadow over distance
  618. float fAtten = saturate( dot( attenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) );
  619. // Shadowing and coloring terms
  620. #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))
  621. if ( bDoShadows )
  622. {
  623. float flShadow = DoFlashlightShadow( FlashlightDepthSampler, RandomRotationSampler, vProjCoords, vScreenPos, nShadowLevel, vShadowTweaks, bAllowHighQuality );
  624. float flAttenuated = lerp( flShadow, 1.0f, vShadowTweaks.y ); // Blend between fully attenuated and not attenuated
  625. flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation
  626. flashlightColor *= flShadow; // Shadow term
  627. }
  628. #endif
  629. float3 diffuseLighting = fAtten;
  630. float flLDotWorldNormal;
  631. if ( bHasNormal )
  632. {
  633. flLDotWorldNormal = dot( L.xyz, worldNormal.xyz );
  634. }
  635. else
  636. {
  637. flLDotWorldNormal = 1.0f;
  638. }
  639. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  640. diffuseLighting *= saturate( flLDotWorldNormal + flFlashlightNoLambertValue ); // Lambertian term
  641. #else
  642. diffuseLighting *= saturate( flLDotWorldNormal ); // Lambertian (not Half-Lambert) term
  643. #endif
  644. diffuseLighting *= flashlightColor;
  645. diffuseLighting *= endFalloffFactor;
  646. return diffuseLighting;
  647. }
  648. #endif //#ifndef COMMON_FLASHLIGHT_FXC_H_