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.

423 lines
16 KiB

  1. //========= Copyright Valve Corporation, All rights reserved. ============//
  2. #ifndef COMMON_VERTEXLITGENERIC_DX9_H_
  3. #define COMMON_VERTEXLITGENERIC_DX9_H_
  4. #include "common_ps_fxc.h"
  5. // We store four light colors and positions in an
  6. // array of three of these structures like so:
  7. //
  8. // x y z w
  9. // +------+------+------+------+
  10. // | L0.rgb | |
  11. // +------+------+------+ |
  12. // | L0.pos | L3 |
  13. // +------+------+------+ rgb |
  14. // | L1.rgb | |
  15. // +------+------+------+------+
  16. // | L1.pos | |
  17. // +------+------+------+ |
  18. // | L2.rgb | L3 |
  19. // +------+------+------+ pos |
  20. // | L2.pos | |
  21. // +------+------+------+------+
  22. //
  23. struct PixelShaderLightInfo
  24. {
  25. float4 color;
  26. float4 pos;
  27. };
  28. #define cOverbright 2.0f
  29. #define cOOOverbright 0.5f
  30. #define LIGHTTYPE_NONE 0
  31. #define LIGHTTYPE_SPOT 1
  32. #define LIGHTTYPE_POINT 2
  33. #define LIGHTTYPE_DIRECTIONAL 3
  34. // Better suited to Pixel shader models, 11 instructions in pixel shader
  35. // ... actually, now only 9: mul, cmp, cmp, mul, mad, mad, mad, mad, mad
  36. float3 PixelShaderAmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] )
  37. {
  38. float3 linearColor, nSquared = worldNormal * worldNormal;
  39. float3 isNegative = ( worldNormal >= 0.0 ) ? 0 : nSquared;
  40. float3 isPositive = ( worldNormal >= 0.0 ) ? nSquared : 0;
  41. linearColor = isPositive.x * cAmbientCube[0] + isNegative.x * cAmbientCube[1] +
  42. isPositive.y * cAmbientCube[2] + isNegative.y * cAmbientCube[3] +
  43. isPositive.z * cAmbientCube[4] + isNegative.z * cAmbientCube[5];
  44. return linearColor;
  45. }
  46. // Better suited to Vertex shader models
  47. // Six VS instructions due to use of constant indexing (slt, mova, mul, mul, mad, mad)
  48. float3 VertexShaderAmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] )
  49. {
  50. float3 nSquared = worldNormal * worldNormal;
  51. int3 isNegative = ( worldNormal < 0.0 );
  52. float3 linearColor;
  53. linearColor = nSquared.x * cAmbientCube[isNegative.x] +
  54. nSquared.y * cAmbientCube[isNegative.y+2] +
  55. nSquared.z * cAmbientCube[isNegative.z+4];
  56. return linearColor;
  57. }
  58. float3 AmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] )
  59. {
  60. // Vertex shader cases
  61. #ifdef SHADER_MODEL_VS_1_0
  62. return VertexShaderAmbientLight( worldNormal, cAmbientCube );
  63. #elif SHADER_MODEL_VS_1_1
  64. return VertexShaderAmbientLight( worldNormal, cAmbientCube );
  65. #elif SHADER_MODEL_VS_2_0
  66. return VertexShaderAmbientLight( worldNormal, cAmbientCube );
  67. #elif SHADER_MODEL_VS_3_0
  68. return VertexShaderAmbientLight( worldNormal, cAmbientCube );
  69. #else
  70. // Pixel shader case
  71. return PixelShaderAmbientLight( worldNormal, cAmbientCube );
  72. #endif
  73. }
  74. //-----------------------------------------------------------------------------
  75. // Purpose: Compute scalar diffuse term with various optional tweaks such as
  76. // Half Lambert and ambient occlusion
  77. //-----------------------------------------------------------------------------
  78. float3 DiffuseTerm(const bool bHalfLambert, const float3 worldNormal, const float3 lightDir,
  79. const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  80. const bool bDoLightingWarp, in sampler lightWarpSampler )
  81. {
  82. float fResult;
  83. float NDotL = dot( worldNormal, lightDir ); // Unsaturated dot (-1 to 1 range)
  84. if ( bHalfLambert )
  85. {
  86. fResult = saturate(NDotL * 0.5 + 0.5); // Scale and bias to 0 to 1 range
  87. if ( !bDoLightingWarp )
  88. {
  89. fResult *= fResult; // Square
  90. }
  91. }
  92. else
  93. {
  94. fResult = saturate( NDotL ); // Saturate pure Lambertian term
  95. }
  96. if ( bDoAmbientOcclusion )
  97. {
  98. // Raise to higher powers for darker AO values
  99. // float fAOPower = lerp( 4.0f, 1.0f, fAmbientOcclusion );
  100. // result *= pow( NDotL * 0.5 + 0.5, fAOPower );
  101. fResult *= fAmbientOcclusion;
  102. }
  103. float3 fOut = float3( fResult, fResult, fResult );
  104. if ( bDoLightingWarp )
  105. {
  106. fOut = 2.0f * tex1D( lightWarpSampler, fResult );
  107. }
  108. return fOut;
  109. }
  110. float3 PixelShaderDoGeneralDiffuseLight( const float fAtten, const float3 worldPos, const float3 worldNormal,
  111. in sampler NormalizeSampler,
  112. const float3 vPosition, const float3 vColor, const bool bHalfLambert,
  113. const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  114. const bool bDoLightingWarp, in sampler lightWarpSampler )
  115. {
  116. #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))
  117. float3 lightDir = normalize( vPosition - worldPos );
  118. #else
  119. float3 lightDir = NormalizeWithCubemap( NormalizeSampler, vPosition - worldPos );
  120. #endif
  121. return vColor * fAtten * DiffuseTerm( bHalfLambert, worldNormal, lightDir, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler );
  122. }
  123. float3 PixelShaderGetLightVector( const float3 worldPos, PixelShaderLightInfo cLightInfo[3], int nLightIndex )
  124. {
  125. if ( nLightIndex == 3 )
  126. {
  127. // Unpack light 3 from w components...
  128. float3 vLight3Pos = float3( cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w );
  129. return normalize( vLight3Pos - worldPos );
  130. }
  131. else
  132. {
  133. return normalize( cLightInfo[nLightIndex].pos - worldPos );
  134. }
  135. }
  136. float3 PixelShaderGetLightColor( PixelShaderLightInfo cLightInfo[3], int nLightIndex )
  137. {
  138. if ( nLightIndex == 3 )
  139. {
  140. // Unpack light 3 from w components...
  141. return float3( cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w );
  142. }
  143. else
  144. {
  145. return cLightInfo[nLightIndex].color.rgb;
  146. }
  147. }
  148. void SpecularAndRimTerms( const float3 vWorldNormal, const float3 vLightDir, const float fSpecularExponent,
  149. const float3 vEyeDir, const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  150. const bool bDoSpecularWarp, in sampler specularWarpSampler, const float fFresnel,
  151. const float3 color, const bool bDoRimLighting, const float fRimExponent,
  152. // Outputs
  153. out float3 specularLighting, out float3 rimLighting )
  154. {
  155. rimLighting = float3(0.0f, 0.0f, 0.0f);
  156. //float3 vReflect = reflect( -vEyeDir, vWorldNormal ); // Reflect view through normal
  157. float3 vReflect = 2 * vWorldNormal * dot( vWorldNormal , vEyeDir ) - vEyeDir; // Reflect view through normal
  158. float LdotR = saturate(dot( vReflect, vLightDir )); // L.R (use half-angle instead?)
  159. specularLighting = pow( LdotR, fSpecularExponent ); // Raise to specular exponent
  160. // Optionally warp as function of scalar specular and fresnel
  161. if ( bDoSpecularWarp )
  162. specularLighting *= tex2D( specularWarpSampler, float2(specularLighting.x, fFresnel) ); // Sample at { (L.R)^k, fresnel }
  163. specularLighting *= saturate(dot( vWorldNormal, vLightDir )); // Mask with N.L
  164. specularLighting *= color; // Modulate with light color
  165. if ( bDoAmbientOcclusion ) // Optionally modulate with ambient occlusion
  166. specularLighting *= fAmbientOcclusion;
  167. if ( bDoRimLighting ) // Optionally do rim lighting
  168. {
  169. rimLighting = pow( LdotR, fRimExponent ); // Raise to rim exponent
  170. rimLighting *= saturate(dot( vWorldNormal, vLightDir )); // Mask with N.L
  171. rimLighting *= color; // Modulate with light color
  172. }
  173. }
  174. // Traditional fresnel term approximation
  175. float Fresnel( const float3 vNormal, const float3 vEyeDir )
  176. {
  177. float fresnel = saturate( 1 - dot( vNormal, vEyeDir ) ); // 1-(N.V) for Fresnel term
  178. return fresnel * fresnel; // Square for a more subtle look
  179. }
  180. // Traditional fresnel term approximation which uses 4th power (square twice)
  181. float Fresnel4( const float3 vNormal, const float3 vEyeDir )
  182. {
  183. float fresnel = saturate( 1 - dot( vNormal, vEyeDir ) ); // 1-(N.V) for Fresnel term
  184. fresnel = fresnel * fresnel; // Square
  185. return fresnel * fresnel; // Square again for a more subtle look
  186. }
  187. //
  188. // Custom Fresnel with low, mid and high parameters defining a piecewise continuous function
  189. // with traditional fresnel (0 to 1 range) as input. The 0 to 0.5 range blends between
  190. // low and mid while the 0.5 to 1 range blends between mid and high
  191. //
  192. // |
  193. // | . M . . . H
  194. // | .
  195. // L
  196. // |
  197. // +----------------
  198. // 0 1
  199. //
  200. float Fresnel( const float3 vNormal, const float3 vEyeDir, float3 vRanges )
  201. {
  202. //float result, f = Fresnel( vNormal, vEyeDir ); // Traditional Fresnel
  203. //if ( f > 0.5f )
  204. // result = lerp( vRanges.y, vRanges.z, (2*f)-1 ); // Blend between mid and high values
  205. //else
  206. // result = lerp( vRanges.x, vRanges.y, 2*f ); // Blend between low and mid values
  207. // note: vRanges is now encoded as ((mid-min)*2, mid, (max-mid)*2) to optimize math
  208. float f = saturate( 1 - dot( vNormal, vEyeDir ) );
  209. f = f*f - 0.5;
  210. return vRanges.y + (f >= 0.0 ? vRanges.z : vRanges.x) * f;
  211. }
  212. void PixelShaderDoSpecularLight( const float3 vWorldPos, const float3 vWorldNormal, const float fSpecularExponent, const float3 vEyeDir,
  213. const float fAtten, const float3 vLightColor, const float3 vLightDir,
  214. const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  215. const bool bDoSpecularWarp, in sampler specularWarpSampler, float fFresnel,
  216. const bool bDoRimLighting, const float fRimExponent,
  217. // Outputs
  218. out float3 specularLighting, out float3 rimLighting )
  219. {
  220. // Compute Specular and rim terms
  221. SpecularAndRimTerms( vWorldNormal, vLightDir, fSpecularExponent,
  222. vEyeDir, bDoAmbientOcclusion, fAmbientOcclusion,
  223. bDoSpecularWarp, specularWarpSampler, fFresnel, vLightColor * fAtten,
  224. bDoRimLighting, fRimExponent, specularLighting, rimLighting );
  225. }
  226. float3 PixelShaderDoLightingLinear( const float3 worldPos, const float3 worldNormal,
  227. const float3 staticLightingColor, const bool bStaticLight,
  228. const bool bAmbientLight, const float4 lightAtten, const float3 cAmbientCube[6],
  229. in sampler NormalizeSampler, const int nNumLights, PixelShaderLightInfo cLightInfo[3],
  230. const bool bHalfLambert, const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  231. const bool bDoLightingWarp, in sampler lightWarpSampler )
  232. {
  233. float3 linearColor = 0.0f;
  234. if ( bStaticLight )
  235. {
  236. // The static lighting comes in in gamma space and has also been premultiplied by $cOOOverbright
  237. // need to get it into
  238. // linear space so that we can do adds.
  239. linearColor += GammaToLinear( staticLightingColor * cOverbright );
  240. }
  241. if ( bAmbientLight )
  242. {
  243. float3 ambient = AmbientLight( worldNormal, cAmbientCube );
  244. if ( bDoAmbientOcclusion )
  245. ambient *= fAmbientOcclusion * fAmbientOcclusion; // Note squaring...
  246. linearColor += ambient;
  247. }
  248. if ( nNumLights > 0 )
  249. {
  250. linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.x, worldPos, worldNormal, NormalizeSampler,
  251. cLightInfo[0].pos, cLightInfo[0].color, bHalfLambert,
  252. bDoAmbientOcclusion, fAmbientOcclusion,
  253. bDoLightingWarp, lightWarpSampler );
  254. if ( nNumLights > 1 )
  255. {
  256. linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.y, worldPos, worldNormal, NormalizeSampler,
  257. cLightInfo[1].pos, cLightInfo[1].color, bHalfLambert,
  258. bDoAmbientOcclusion, fAmbientOcclusion,
  259. bDoLightingWarp, lightWarpSampler );
  260. if ( nNumLights > 2 )
  261. {
  262. linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.z, worldPos, worldNormal, NormalizeSampler,
  263. cLightInfo[2].pos, cLightInfo[2].color, bHalfLambert,
  264. bDoAmbientOcclusion, fAmbientOcclusion,
  265. bDoLightingWarp, lightWarpSampler );
  266. if ( nNumLights > 3 )
  267. {
  268. // Unpack the 4th light's data from tight constant packing
  269. float3 vLight3Color = float3( cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w );
  270. float3 vLight3Pos = float3( cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w );
  271. linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.w, worldPos, worldNormal, NormalizeSampler,
  272. vLight3Pos, vLight3Color, bHalfLambert,
  273. bDoAmbientOcclusion, fAmbientOcclusion,
  274. bDoLightingWarp, lightWarpSampler );
  275. }
  276. }
  277. }
  278. }
  279. return linearColor;
  280. }
  281. void PixelShaderDoSpecularLighting( const float3 worldPos, const float3 worldNormal, const float fSpecularExponent, const float3 vEyeDir,
  282. const float4 lightAtten, const int nNumLights, PixelShaderLightInfo cLightInfo[3],
  283. const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  284. const bool bDoSpecularWarp, in sampler specularWarpSampler, float fFresnel,
  285. const bool bDoRimLighting, const float fRimExponent,
  286. // Outputs
  287. out float3 specularLighting, out float3 rimLighting )
  288. {
  289. specularLighting = rimLighting = float3( 0.0f, 0.0f, 0.0f );
  290. float3 localSpecularTerm, localRimTerm;
  291. if( nNumLights > 0 )
  292. {
  293. PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir,
  294. lightAtten.x, PixelShaderGetLightColor( cLightInfo, 0 ),
  295. PixelShaderGetLightVector( worldPos, cLightInfo, 0 ),
  296. bDoAmbientOcclusion, fAmbientOcclusion,
  297. bDoSpecularWarp, specularWarpSampler, fFresnel,
  298. bDoRimLighting, fRimExponent,
  299. localSpecularTerm, localRimTerm );
  300. specularLighting += localSpecularTerm; // Accumulate specular and rim terms
  301. rimLighting += localRimTerm;
  302. }
  303. if( nNumLights > 1 )
  304. {
  305. PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir,
  306. lightAtten.y, PixelShaderGetLightColor( cLightInfo, 1 ),
  307. PixelShaderGetLightVector( worldPos, cLightInfo, 1 ),
  308. bDoAmbientOcclusion, fAmbientOcclusion,
  309. bDoSpecularWarp, specularWarpSampler, fFresnel,
  310. bDoRimLighting, fRimExponent,
  311. localSpecularTerm, localRimTerm );
  312. specularLighting += localSpecularTerm; // Accumulate specular and rim terms
  313. rimLighting += localRimTerm;
  314. }
  315. if( nNumLights > 2 )
  316. {
  317. PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir,
  318. lightAtten.z, PixelShaderGetLightColor( cLightInfo, 2 ),
  319. PixelShaderGetLightVector( worldPos, cLightInfo, 2 ),
  320. bDoAmbientOcclusion, fAmbientOcclusion,
  321. bDoSpecularWarp, specularWarpSampler, fFresnel,
  322. bDoRimLighting, fRimExponent,
  323. localSpecularTerm, localRimTerm );
  324. specularLighting += localSpecularTerm; // Accumulate specular and rim terms
  325. rimLighting += localRimTerm;
  326. }
  327. if( nNumLights > 3 )
  328. {
  329. PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir,
  330. lightAtten.w, PixelShaderGetLightColor( cLightInfo, 3 ),
  331. PixelShaderGetLightVector( worldPos, cLightInfo, 3 ),
  332. bDoAmbientOcclusion, fAmbientOcclusion,
  333. bDoSpecularWarp, specularWarpSampler, fFresnel,
  334. bDoRimLighting, fRimExponent,
  335. localSpecularTerm, localRimTerm );
  336. specularLighting += localSpecularTerm; // Accumulate specular and rim terms
  337. rimLighting += localRimTerm;
  338. }
  339. }
  340. float3 PixelShaderDoRimLighting( const float3 worldNormal, const float3 vEyeDir, const float3 cAmbientCube[6], float fFresnel )
  341. {
  342. float3 vReflect = reflect( -vEyeDir, worldNormal ); // Reflect view through normal
  343. return fFresnel * PixelShaderAmbientLight( vEyeDir, cAmbientCube );
  344. }
  345. // Called directly by newer shaders or through the following wrapper for older shaders
  346. float3 PixelShaderDoLighting( const float3 worldPos, const float3 worldNormal,
  347. const float3 staticLightingColor, const bool bStaticLight,
  348. const bool bAmbientLight, const float4 lightAtten, const float3 cAmbientCube[6],
  349. in sampler NormalizeSampler, const int nNumLights, PixelShaderLightInfo cLightInfo[3],
  350. const bool bHalfLambert,
  351. // New optional/experimental parameters
  352. const bool bDoAmbientOcclusion, const float fAmbientOcclusion,
  353. const bool bDoLightingWarp, in sampler lightWarpSampler )
  354. {
  355. float3 linearColor = PixelShaderDoLightingLinear( worldPos, worldNormal, staticLightingColor,
  356. bStaticLight, bAmbientLight, lightAtten,
  357. cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, bHalfLambert,
  358. bDoAmbientOcclusion, fAmbientOcclusion,
  359. bDoLightingWarp, lightWarpSampler );
  360. // go ahead and clamp to the linear space equivalent of overbright 2 so that we match
  361. // everything else.
  362. // linearColor = HuePreservingColorClamp( linearColor, pow( 2.0f, 2.2 ) );
  363. return linearColor;
  364. }
  365. #endif //#ifndef COMMON_VERTEXLITGENERIC_DX9_H_