Counter Strike : Global Offensive Source Code
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.

599 lines
26 KiB

  1. //========== Copyright (c) Valve Corporation, All rights reserved. ==========//
  2. // STATIC: "BACK_SURFACE" "0..1"
  3. // STATIC: "LIGHT_WARP" "0..1"
  4. // STATIC: "FRESNEL_WARP" "0..1"
  5. // STATIC: "HIGH_PRECISION_DEPTH" "0..1"
  6. // STATIC: "INTERIOR_LAYER" "0..1"
  7. // STATIC: "SELF_ILLUM_FRESNEL" "0..1"
  8. // STATIC: "OPACITY_TEXTURE" "0..1"
  9. // STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..3" [ps20b] [PC]
  10. // STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..2" [ps30] [PC]
  11. // STATIC: "FLASHLIGHTDEPTHFILTERMODE" "0..0" [ps20b] [XBOX]
  12. // STATIC: "CONTACT_SHADOW" "0..1"
  13. // STATIC: "SELF_ILLUM_PULSE" "0..1"
  14. // STATIC: "VOLUME_TEXTURE" "0..1"
  15. // DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b]
  16. // DYNAMIC: "NUM_LIGHTS" "0..4" [ps30]
  17. // DYNAMIC: "FLASHLIGHT" "0..1"
  18. // DYNAMIC: "FLASHLIGHTSHADOWS" "0..1"
  19. // SKIP: ( $CONTACT_SHADOW == 1 ) && ( $SELF_ILLUM_PULSE == 1 )
  20. // For now, 'BACK_SURFACE' is a dead-simple path (just writes depth into dest alpha), so skip all non-HW-config combos:
  21. // SKIP: ( $BACK_SURFACE == 1 ) && ( $LIGHT_WARP > 0 )
  22. // SKIP: ( $BACK_SURFACE == 1 ) && ( $FRESNEL_WARP > 0 )
  23. // SKIP: ( $BACK_SURFACE == 1 ) && ( $INTERIOR_LAYER > 0 )
  24. // SKIP: ( $BACK_SURFACE == 1 ) && ( $SELF_ILLUM_FRESNEL > 0 )
  25. // SKIP: ( $BACK_SURFACE == 1 ) && ( $OPACITY_TEXTURE > 0 )
  26. // SKIP: ( $BACK_SURFACE == 1 ) && ( $CONTACT_SHADOW > 0 )
  27. // SKIP: ( $BACK_SURFACE == 1 ) && ( $SELF_ILLUM_PULSE > 0 )
  28. // SKIP: ( $BACK_SURFACE == 1 ) && ( $VOLUME_TEXTURE > 0 )
  29. #include "common_ps_fxc.h"
  30. #include "common_vertexlitgeneric_dx9.h"
  31. #include "common_flashlight_fxc.h"
  32. #include "shader_constant_register_map.h"
  33. // SAMPLERS
  34. sampler g_tBase : register( s0 );
  35. sampler g_tBump : register( s1 );
  36. sampler g_tScreen : register( s2 );
  37. sampler g_tSpecMask : register( s3 );
  38. sampler g_tLightWarp : register( s4 );
  39. sampler g_tFresnelWarp : register( s5 );
  40. sampler g_tOpacity : register( s6 );
  41. sampler g_tShadowDepth : register( s7 ); // Flashlight shadow depth map sampler
  42. sampler g_tNormalizeRandRot : register( s8 ); // Normalization / RandomRotation samplers
  43. sampler g_tFlashlightCookie : register( s9 ); // Flashlight cookie
  44. // SHADER CONSTANTS
  45. const float4 g_vMisc : register( c0 );
  46. #define g_flBumpStrength g_vMisc.x
  47. #define g_flDepthScale g_vMisc.y
  48. #define g_flInnerFogStrength g_vMisc.z
  49. #define g_flRefractStrength g_vMisc.w
  50. const float4 g_vTranslucentFresnelParams_InteriorBoost : register( c1 );
  51. #define g_vTranslucentFresnelParams g_vTranslucentFresnelParams_InteriorBoost.xyz
  52. #define g_flInteriorBoost g_vTranslucentFresnelParams_InteriorBoost.w
  53. const float4 g_vMisc2 : register( c3 );
  54. #define g_flRimLightExp g_vMisc2.x
  55. #define g_flRimLightScale g_vMisc2.y
  56. #define g_flSpecScale g_vMisc2.z
  57. #define g_flSpecExp2 g_vMisc2.w
  58. const float4 g_vMisc3 : register( c10 );
  59. #define g_flSpecScale2 g_vMisc3.x
  60. #define g_flFresnelBumpStrength g_vMisc3.y
  61. #define g_flDiffuseScale g_vMisc3.z
  62. #define g_flInteriorLightScale g_vMisc3.w
  63. const float4 g_vEyePos_SpecExp : register( PSREG_EYEPOS_SPEC_EXPONENT );
  64. #define g_vEyePos g_vEyePos_SpecExp.xyz
  65. #define g_flSpecExp g_vEyePos_SpecExp.w
  66. const float4 g_ShaderControls : register( c12 );
  67. #define g_fWriteDepthToAlpha g_ShaderControls.x
  68. const float4 g_vBaseTint_InteriorBackLightScale : register( c19 );
  69. #define g_cBaseTint g_vBaseTint_InteriorBackLightScale.rgb
  70. #define g_flInteriorBackLightScale g_vBaseTint_InteriorBackLightScale.w
  71. const float4 g_vSelfIllumFresnelParams : register( c26 );
  72. #define g_flSelfIllumScale g_vSelfIllumFresnelParams.x
  73. #define g_flSelfIllumBias g_vSelfIllumFresnelParams.y
  74. #define g_flSelfIllumExp g_vSelfIllumFresnelParams.z
  75. #define g_flSelfIllumBrightness g_vSelfIllumFresnelParams.w
  76. // TODO: pass in FOV so that we can account for it properly
  77. #define g_flHalfWindowWidth 1 /* tan(fov/2) */
  78. const float4 g_cSelfIllumTint : register( c27 );
  79. //#define UNUSED g_cSelfIllumTint.w
  80. const float4 g_vInteriorColor_RefractBlur : register( c32 );
  81. #define g_cInteriorColor g_vInteriorColor_RefractBlur.rgb
  82. #define g_flRefractBlur g_vInteriorColor_RefractBlur.w
  83. const float3 cAmbientCube[6] : register( PSREG_AMBIENT_CUBE );
  84. const PixelShaderLightInfo g_sLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total
  85. const float4 g_vFlashlightAttenuationFactors_FarZ : register( PSREG_FLASHLIGHT_ATTENUATION );
  86. #define g_vFlashlightAttenuationFactors g_vFlashlightAttenuationFactors_FarZ.xyz
  87. #define g_flFlashlightFarZ g_vFlashlightAttenuationFactors_FarZ.w
  88. const float4 g_vFlashlightPos_RimBoost : register( PSREG_FLASHLIGHT_POSITION_RIM_BOOST );
  89. #define g_vFlashlightPos g_vFlashlightPos_RimBoost.xyz
  90. const float4x4 g_mFlashlightWorldToTexture : register( PSREG_FLASHLIGHT_TO_WORLD_TEXTURE );
  91. const float4 g_vShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS );
  92. const float3x3 g_mView : register( c33 );
  93. const float4 g_vMisc4 : register( c36 );
  94. #define g_flLimitFogAtten g_vMisc4.x
  95. #define g_flFogNormalBoost g_vMisc4.y
  96. #define g_flGlowScale g_vMisc4.z
  97. //#define UNUSED g_vMisc4.w
  98. // COMBO-DERIVED CONSTANTS
  99. static const bool bLightWarp = LIGHT_WARP ? true : false;
  100. static const bool bFlashLight = FLASHLIGHT ? true : false;
  101. // How we use vertex colour (TEXCOORD2) varies based on static combos:
  102. #if ( CONTACT_SHADOW == 1 )
  103. #define i_ClosestSurfaceDir i.vClosestSurfaceDir
  104. #define i_PulseColor 0
  105. #define i_VertexAlpha 1
  106. #elif ( SELF_ILLUM_PULSE == 1 )
  107. #define i_ClosestSurfaceDir 0
  108. #define i_PulseColor i.vPulseColor.rgb
  109. #define i_VertexAlpha i.vPulseColor.a
  110. #else
  111. #define i_ClosestSurfaceDir 0
  112. #define i_PulseColor 0
  113. #define i_VertexAlpha 1
  114. #endif
  115. // INPUT STRUCT
  116. struct PS_INPUT
  117. {
  118. float4 vWorldNormal : TEXCOORD0; // w is proj. z coord (for depth stuff)
  119. #if CONTACT_SHADOW == 0
  120. float4 vPulseColor : TEXCOORD1;
  121. #else
  122. float4 vClosestSurfaceDir : TEXCOORD1;
  123. #endif
  124. float4 vWorldPos : TEXCOORD2; // w is proj. w coord
  125. float4 vUV0 : TEXCOORD3;
  126. float4 vUV1 : TEXCOORD4;
  127. float4 vLightAtten : TEXCOORD5;
  128. float3 vLightCube : TEXCOORD6;
  129. };
  130. //==============================================================================================================================================================
  131. float Luminance( const float3 colour )
  132. {
  133. return dot( colour, float3( 0.3, 0.59, 0.11 ) );
  134. }
  135. //==============================================================================================================================================================
  136. float3 ComputeTextureBlendWeights( float3 vWorldNormal )
  137. {
  138. float3 vBlendWeights = max( ( abs( vWorldNormal.xyz ) - 0.2 ) * 7.0, 0.0 );
  139. vBlendWeights /= dot( vBlendWeights, float3(1, 1, 1) ); // normalize
  140. return vBlendWeights;
  141. }
  142. //==============================================================================================================================================================
  143. float4 BlendedTexFetchMAX( sampler s, float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights )
  144. {
  145. // This 'max' blend is supposed to reduce the 'multiple layers sliding over one another'
  146. // appearance of the blob skin. NOTE: it doesn't preserve texture luminance.
  147. float4 vFetch0 = tex2D( s, vUV0 );
  148. float4 vFetch1 = tex2D( s, vUV1 );
  149. float4 vFetch2 = tex2D( s, vUV2 );
  150. //return vBlendWeights.x * vFetch0 + vBlendWeights.y * vFetch1 + vBlendWeights.z * vFetch2;
  151. return max( vBlendWeights.x * vFetch0, max( vBlendWeights.y * vFetch1, vBlendWeights.z * vFetch2 ) );
  152. }
  153. //==============================================================================================================================================================
  154. float3 BumpedToWorldNormal( float3 vBumpedNormal,
  155. float3 vVertexNormal, // should be normalized
  156. float3 vTangentDir )
  157. {
  158. float3x3 mTanToWorld;
  159. mTanToWorld[2] = vVertexNormal;
  160. mTanToWorld[0] = vTangentDir - dot( vTangentDir, vVertexNormal ) * vVertexNormal;
  161. mTanToWorld[0] = normalize( mTanToWorld[0] );
  162. mTanToWorld[1] = cross( mTanToWorld[0], mTanToWorld[2] );
  163. return normalize( mul( vBumpedNormal, mTanToWorld ) );
  164. }
  165. //==============================================================================================================================================================
  166. void BlendedTexFetchNormal( sampler s, float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights, float3 vWorldNormal,
  167. // Function outputs:
  168. out float2 vBumpedTSNormal, out float3 vBumpedWorldNormal, out float3 vFresnelWorldNormal )
  169. {
  170. float3 vNormalTS1 = 2.0 * tex2D( g_tBump, vUV0 ) - 1.0;
  171. float3 vNormalTS2 = 2.0 * tex2D( g_tBump, vUV1 ) - 1.0;
  172. float3 vNormalTS3 = 2.0 * tex2D( g_tBump, vUV2 ) - 1.0;
  173. vBumpedTSNormal = vBlendWeights.x * vNormalTS1.xy + vBlendWeights.y * vNormalTS2.xy + vBlendWeights.z * vNormalTS3.xy;
  174. float3 vBumpedNormal1 = BumpedToWorldNormal( vNormalTS1, vWorldNormal, float3( 0, 1, 0 ) );
  175. float3 vBumpedNormal2 = BumpedToWorldNormal( vNormalTS2, vWorldNormal, float3( 1, 0, 0 ) );
  176. float3 vBumpedNormal3 = BumpedToWorldNormal( vNormalTS3, vWorldNormal, float3( 1, 0, 0 ) );
  177. vBumpedWorldNormal = vBlendWeights.x * vBumpedNormal1 + vBlendWeights.y * vBumpedNormal2 + vBlendWeights.z * vBumpedNormal3;
  178. // Apply bump strength in world space (this is cheaper because we have to do it twice, for normal and fresnel bumpstrength)
  179. float3 vBumpStrengthDir = vBumpedWorldNormal - dot( vBumpedWorldNormal, vWorldNormal ) * vWorldNormal;
  180. vFresnelWorldNormal = normalize( vBumpedWorldNormal + ( g_flFresnelBumpStrength - 1.0 ) * vBumpStrengthDir );
  181. vBumpedWorldNormal = normalize( vBumpedWorldNormal + ( g_flBumpStrength - 1.0 ) * vBumpStrengthDir );
  182. }
  183. //==============================================================================================================================================================
  184. void ComputeOpacityAndFresnel( float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights,
  185. float3 vEyeDir, float3 vWorldNormal, float flVertexAlpha,
  186. // Function outputs:
  187. out float flSkinOpacity, out float flBlobOpacity, out float flFresnel )
  188. {
  189. flSkinOpacity = 1;
  190. #if OPACITY_TEXTURE == 1
  191. flSkinOpacity = BlendedTexFetchMAX( g_tOpacity, vUV0, vUV1, vUV2, vBlendWeights );
  192. #endif
  193. flFresnel = saturate( 1.0 - dot( vEyeDir.xyz, vWorldNormal.xyz ) );
  194. #if FRESNEL_WARP == 1
  195. float fTranslucentFresnel = tex1D( g_tFresnelWarp, flFresnel );
  196. #else
  197. float fTranslucentFresnel = lerp( g_vTranslucentFresnelParams.x, g_vTranslucentFresnelParams.y, pow( flFresnel, g_vTranslucentFresnelParams.z ) );
  198. #endif
  199. // TODO: should decouple fresnel from the opacity map, this kills base texture alpha
  200. flSkinOpacity = max( flSkinOpacity, fTranslucentFresnel );
  201. flBlobOpacity = flVertexAlpha;
  202. }
  203. //==============================================================================================================================================================
  204. float3 BlobAmbientLight( float3 vVertexAmbient, float3 vEyeDir, float3 vWorldNormal, float flFresnel )
  205. {
  206. // Ambient lighting now comes from VS
  207. float3 cAmbient = vVertexAmbient;
  208. // TODO: Replace lambert diffuse with pixelshader-ambient term of full lighting env.
  209. //float3 cAmbient = PixelShaderAmbientLight( vBumpedWorldNormal, cAmbientCube );
  210. //float3 cAmbientSheen = PixelShaderAmbientLight( reflect( -vEyeDir, vBumpedWorldNormal ), cAmbientCube );
  211. //cAmbient = lerp( cAmbient, cAmbientSheen, flFresnel );
  212. return cAmbient;
  213. }
  214. //==============================================================================================================================================================
  215. void BlobDynamicLight( float3 vWorldPos, float3 vEyeDir, float3 vBumpedWorldNormal, float4 vLightAtten, float flFresnel,
  216. // Function outputs:
  217. out float3 cDiffuse, out float3 cSpec, out float3 cSpec2, out float3 cRim )
  218. {
  219. cDiffuse = cSpec = cSpec2 = cRim = 0;
  220. for ( int l = 0; l < NUM_LIGHTS; l++ )
  221. {
  222. cDiffuse.rgb += vLightAtten[l] * PixelShaderGetLightColor( g_sLightInfo, l ) *
  223. DiffuseTerm( true, vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ),
  224. bLightWarp, g_tLightWarp );
  225. // spec 1
  226. float3 cCurrSpec, cCurrRim;
  227. bool bYesRimLight = true;
  228. SpecularAndRimTerms( vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ), g_flSpecExp, vEyeDir,
  229. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  230. PixelShaderGetLightColor( g_sLightInfo, l ),
  231. bYesRimLight, g_flRimLightExp,
  232. cCurrSpec, cCurrRim );
  233. cSpec += vLightAtten[l] * cCurrSpec;
  234. cRim += vLightAtten[l] * cCurrRim;
  235. // spec 2
  236. float3 cCurrSpec2, cDummy;
  237. bool bNoRimLight = false;
  238. SpecularAndRimTerms( vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ), g_flSpecExp2, vEyeDir,
  239. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  240. PixelShaderGetLightColor( g_sLightInfo, l ),
  241. bNoRimLight, 0,
  242. cCurrSpec2, cDummy );
  243. cSpec2 += vLightAtten[l] * cCurrSpec2;
  244. // FIXME: no rim2 term?
  245. }
  246. cRim *= flFresnel * flFresnel * flFresnel * flFresnel;
  247. }
  248. //==============================================================================================================================================================
  249. void BlobFlashlight( float3 vWorldPos, float3 vEyeDir, float3 vWorldNormal, float2 vScreenPos, float flSpecularExponent,
  250. // Function outputs:
  251. out float3 cOutFlashlightDiffuse, out float3 cOutFlashlightSpec, out float3 cOutFlashlightColor )
  252. {
  253. float3 delta = g_vFlashlightPos - vWorldPos;
  254. float3 vLightVec = normalize( delta );
  255. float distSquared = dot( delta, delta );
  256. float dist = sqrt( distSquared );
  257. // Attenuation for light and to fade out shadow over distance
  258. float fAtten = saturate( dot( g_vFlashlightAttenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) );
  259. float endFalloffFactor = RemapValClamped( dist, g_flFlashlightFarZ, 0.6f * g_flFlashlightFarZ, 0.0f, 1.0f );
  260. // Project into flashlight texture
  261. float4 flashlightSpacePosition = mul( float4( vWorldPos, 1.0f ), g_mFlashlightWorldToTexture ); // TODO: this can be moved to VS
  262. float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
  263. // Flashlight colour
  264. cOutFlashlightColor = tex2D( g_tFlashlightCookie, vProjCoords );
  265. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  266. cOutFlashlightColor *= cFlashlightColor.xyz;
  267. #endif
  268. cOutFlashlightColor *= endFalloffFactor * fAtten;
  269. // Flashlight shadow
  270. #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))
  271. if ( FLASHLIGHTSHADOWS )
  272. {
  273. float flShadow = DoFlashlightShadow( g_tShadowDepth, g_tNormalizeRandRot, vProjCoords, vScreenPos,
  274. FLASHLIGHTDEPTHFILTERMODE, g_vShadowTweaks );
  275. float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated
  276. flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation
  277. cOutFlashlightColor *= flShadow; // Shadow term
  278. }
  279. #endif
  280. // Flashlight diffuse term
  281. cOutFlashlightDiffuse = cOutFlashlightColor * DiffuseTerm( true, vWorldNormal, vLightVec, bLightWarp, g_tLightWarp );
  282. // Flashlight specular term
  283. float3 cDummy;
  284. SpecularAndRimTerms( vWorldNormal, vLightVec, flSpecularExponent, -vEyeDir,
  285. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  286. cOutFlashlightColor,
  287. false, g_flRimLightExp,
  288. cOutFlashlightSpec, cDummy );
  289. }
  290. //==============================================================================================================================================================
  291. float4 SampleBackgroundBlurred( float2 vBumpedTSNormal, float3 vWorldNormal, float2 vScreenPos, float flCamDist, float flBlobOpacity )
  292. {
  293. static const float2 vPoissonOffset[8] = { float2( 0.3475f, 0.0042f ), float2( 0.8806f, 0.3430f ),
  294. float2( -0.0041f, -0.6197f ), float2( 0.0472f, 0.4964f ),
  295. float2( -0.3730f, 0.0874f ), float2( -0.9217f, -0.3177f ),
  296. float2( -0.6289f, 0.7388f ), float2( 0.5744f, -0.7741f ) };
  297. // TODO: get framebuffer res from constants
  298. float2 vScreenRes = float2( 1600, 1200 );
  299. float2 g_vInvScreenRes = 1.0 / vScreenRes;
  300. // Project world-space blur radius into screen space.
  301. float flBlurRadius = flBlobOpacity * g_flRefractBlur * ( vScreenRes.x / ( flCamDist * g_flHalfWindowWidth ) );
  302. // Bumped refract
  303. float flRefractStrength = flBlobOpacity * 80.0 * g_flRefractStrength / ( flCamDist * g_flHalfWindowWidth );
  304. float2 vBackgroundUV = flRefractStrength * vBumpedTSNormal + vScreenPos;
  305. /* // This gives the blob a more crystal-bally refractive look, which looks cool up-close, but looks weird when
  306. // it pulls foreground pixels in. It could work well if the innards were rendered into their own texture.
  307. float3 vOffset = mul( g_mView, normalize( -vWorldNormal ) );
  308. float2 vBackgroundUV = 0.07 * vOffset.xy + 0.03 * vBumpedTSNormal + vScreenPos; */
  309. float4 cOut = 0;
  310. for( int i = 0; i < 8; i++ )
  311. {
  312. cOut += 1.0/8.0 * tex2D( g_tScreen, vBackgroundUV + flBlurRadius * g_vInvScreenRes.xy * vPoissonOffset[i] );
  313. }
  314. return cOut;
  315. }
  316. float3 CubeAverage( void )
  317. {
  318. // TODO: Pass this average light color in as a const
  319. float3 cAvgLight = 0.0;
  320. for( int j = 0; j < 6; j++ )
  321. {
  322. cAvgLight += cAmbientCube[j] / 6.0;
  323. }
  324. return cAvgLight;
  325. }
  326. float3 BlobInterior( float3 vWorldNormal, float2 vBumpedTSNormal, float3 vEyeDir,
  327. float2 vScreenPos, float flPixelDepth, float flCamDist, float flBlobOpacity, float3 cFlashlightColor )
  328. {
  329. float3 cInterior = 0;
  330. // Sample the background (and inner blob brain/tentacles)
  331. float4 cBackground = SampleBackgroundBlurred( vBumpedTSNormal, vWorldNormal, vScreenPos, flCamDist, flBlobOpacity );
  332. // Boost bright background pixels
  333. float flLuminance = Luminance( cBackground.rgb );
  334. cBackground.rgb *= 1.0 + g_flInteriorBoost * flLuminance * flLuminance;
  335. // Fake refract-like vector without any total internal reflection crappiness
  336. float3 vRefract = normalize( -( vEyeDir + vWorldNormal ) );
  337. // Interior lighting through ambient cube
  338. float3 cBackLight = PixelShaderAmbientLight( vRefract, cAmbientCube );
  339. float3 cAvgLight = CubeAverage() + ( 0.6 * cFlashlightColor );
  340. cBackLight = max( g_flInteriorLightScale * cAvgLight, g_flInteriorBackLightScale * cBackLight );
  341. cBackLight *= g_cInteriorColor;
  342. // Get eye ray travel distance through blob
  343. float flBackgroundDepth = cBackground.a * g_flDepthScale;
  344. float flDistThroughBlob = flBackgroundDepth - flPixelDepth;
  345. flDistThroughBlob = max( 0.0, flDistThroughBlob );
  346. // Compute the opacity ('fog') of the blob interior volume, based on its thickness
  347. float flFogAtten = pow( 2.0, -flDistThroughBlob * g_flInnerFogStrength ); // exponential falloff
  348. //float flFogAtten = saturate( 0.5 - 0.011 * flDistThroughBlob ); // linear falloff
  349. #if HIGH_PRECISION_DEPTH == 0
  350. // fade to constant interior fogginess as we run against the low-precision destalpha depth limit
  351. flFogAtten = lerp( flFogAtten, g_flLimitFogAtten, saturate( cBackground.a*cBackground.a*cBackground.a ) );
  352. #endif
  353. flFogAtten = lerp( 1.0, flFogAtten, flBlobOpacity );
  354. // Composite the fog interior lighting/fog over the background
  355. cInterior = lerp( cBackLight, cBackground.rgb, flFogAtten );
  356. //float flInScattering = flDistThroughBlob * 0.002;
  357. //cInterior.rgb += flInScattering * i.vLightCube.rgb;
  358. return cInterior;
  359. }
  360. //==============================================================================================================================================================
  361. void BlobSelfIllumFresnel( float3 vFresnelWorldNormal, float3 vEyeDir,
  362. // Function-modified inputs:
  363. inout float3 cDiffuse )
  364. {
  365. float flSelfIllumFresnel = pow( saturate( dot( vEyeDir.xyz, vFresnelWorldNormal.xyz ) ), g_flSelfIllumExp );
  366. flSelfIllumFresnel = ( flSelfIllumFresnel * g_flSelfIllumScale ) + g_flSelfIllumBias;
  367. float3 cSelfIllumComponent = g_cSelfIllumTint * g_flSelfIllumBrightness;
  368. cDiffuse.rgb = lerp( cDiffuse.rgb, cSelfIllumComponent.rgb, saturate( flSelfIllumFresnel ) );
  369. }
  370. //==============================================================================================================================================================
  371. float BlobContactShadow( float3 vWorldNormal, float4 vClosestSurfaceDir )
  372. {
  373. // contact darkening for blob regions that touch a surface
  374. float3 vSurfaceDir = normalize( vClosestSurfaceDir.xyz ) * vClosestSurfaceDir.w;
  375. float flContactShadow = saturate( 0.8 * ( 1.0 - dot( vSurfaceDir, vWorldNormal ) ) + 0.2 );
  376. flContactShadow = lerp ( 0.3, 1.0, flContactShadow * flContactShadow * flContactShadow );
  377. return flContactShadow;
  378. }
  379. //==============================================================================================================================================================
  380. float3 BlobSelfIllumPulse( float3 cPulseColor, float3 cBase, float flSkinOpacity )
  381. {
  382. float flBaseLuminance = length( cBase.rgb );
  383. float flPulseLuminance = length( cPulseColor );
  384. float3 cSelfIllumPulse = sqrt( flSkinOpacity ) * g_flGlowScale * flBaseLuminance * flPulseLuminance * cPulseColor;
  385. return cSelfIllumPulse;
  386. }
  387. //==============================================================================================================================================================
  388. float3 noise( float3 coord )
  389. {
  390. coord.z *= 50.0;
  391. float zfrac = frac( coord.z );
  392. float2 zhash = float2( coord.z, 1 + coord.z );
  393. zhash -= frac( zhash );
  394. zhash = ( zhash * zhash );
  395. zhash -= 31.0 * floor( zhash / 31.0 );
  396. zhash = ( zhash * zhash );
  397. zhash -= 31.0 * floor( zhash / 31.0 );
  398. zhash *= 1.0/31.0;
  399. float3 c0 = tex2D( g_tBase, float4( coord.xy + float2( 0, zhash.x ), 0, 0 ) ).rgb;
  400. float3 c1 = tex2D( g_tBase, float4( coord.xy + float2( 0, zhash.y ), 0, 0 ) ).rgb;
  401. float3 rslt = lerp( c0, c1, zfrac );
  402. return rslt;
  403. }
  404. void PerformVolumeTexturing( float3 vUVW, float3 vWorldNormal,
  405. // Function outputs:
  406. out float3 cOut )
  407. {
  408. // volumetric texturing test code
  409. float fNoiseScale = 0.002;
  410. float fStrengthScale = 1.0;
  411. cOut.rgb = 0;
  412. for ( int k = 0; k < 6; k++ )
  413. {
  414. cOut.rgb += fStrengthScale * noise( fNoiseScale * vUVW ).rgb;
  415. fNoiseScale *= 2.0; fStrengthScale /= 1.8;
  416. }
  417. cOut.rgb /= 2.0;
  418. cOut.rgb *= PixelShaderAmbientLight( vWorldNormal, cAmbientCube );
  419. }
  420. //==============================================================================================================================================================
  421. float4 main( PS_INPUT i ) : COLOR
  422. {
  423. float4 cOut = { 0, 0, 0, 1 };
  424. // Set up misc camera variables
  425. float flPixelDepth = i.vWorldNormal.w;
  426. float2 vScreenPos = i.vUV1.wz / i.vWorldPos.w;
  427. float3 vEyeDir = g_vEyePos.xyz - i.vWorldPos.xyz;
  428. float flCamDist = length( vEyeDir );
  429. vEyeDir /= flCamDist;
  430. // Set up contact shadowing or self-illum values (or neither), based on static combo
  431. float4 vClosestSurfaceDir = i_ClosestSurfaceDir;
  432. float3 cPulseColor = i_PulseColor;
  433. float flVertexAlpha = i_VertexAlpha;
  434. // For now, 'BACK_SURFACE' is a dead-simple path (just writes depth into dest alpha)
  435. #if ( BACK_SURFACE == 1 )
  436. cOut = tex2D( g_tScreen, vScreenPos );
  437. return FinalOutput( cOut, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, flPixelDepth );
  438. #endif
  439. // Blend weights for 3 blended planar projections
  440. float3 vWorldNormal = normalize( i.vWorldNormal.xyz );
  441. float3 vBlendWeights = ComputeTextureBlendWeights( vWorldNormal );
  442. // Base+spec maps
  443. float4 cBase = BlendedTexFetchMAX( g_tBase, i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights );
  444. float flSpecMask = BlendedTexFetchMAX( g_tSpecMask, i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights );
  445. // Use base tex alpha channel as a tint mask
  446. cBase.rgb = lerp( cBase.rgb, Luminance( cBase.rgb ) * g_cBaseTint.rgb, cBase.a );
  447. // Normal mapping
  448. float3 vBumpedWorldNormal, vFresnelWorldNormal;
  449. float2 vBumpedTSNormal;
  450. BlendedTexFetchNormal( g_tBump, i.vUV0.xy, i.vUV0.zw, i.vUV1.xy, vBlendWeights, vWorldNormal,
  451. vBumpedTSNormal, vBumpedWorldNormal, vFresnelWorldNormal );
  452. // Opacity and fresnel
  453. float flSkinOpacity, flBlobOpacity, flFresnel;
  454. ComputeOpacityAndFresnel( i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights, vEyeDir, vFresnelWorldNormal, flVertexAlpha,
  455. flSkinOpacity, flBlobOpacity, flFresnel );
  456. // Ambient light
  457. float3 cAmbient = BlobAmbientLight( i.vLightCube, vEyeDir, vBumpedWorldNormal, flFresnel );
  458. // Dynamic lights
  459. float3 cDiffuse, cSpec, cSpec2, cRim;
  460. BlobDynamicLight( i.vWorldPos.xyz, vEyeDir, vBumpedWorldNormal, i.vLightAtten, flFresnel,
  461. cDiffuse, cSpec, cSpec2, cRim );
  462. // Flashlight
  463. float3 cFlashlightColor = 0;
  464. #if FLASHLIGHT == 1
  465. float3 cFlashlightDiffuse, cFlashlightSpec;
  466. BlobFlashlight( i.vWorldPos.xyz, vEyeDir, vBumpedWorldNormal, vScreenPos, g_flSpecExp2,
  467. cFlashlightDiffuse, cFlashlightSpec, cFlashlightColor );
  468. cDiffuse.rgb += cFlashlightDiffuse;
  469. cSpec2 += cFlashlightSpec;
  470. #endif
  471. // Scale light terms
  472. cDiffuse *= g_flDiffuseScale;
  473. cSpec *= g_flSpecScale;
  474. cSpec2 *= g_flSpecScale2;
  475. cRim *= g_flRimLightScale;
  476. // Compute blob interior/background colour
  477. float3 cInterior = 0;
  478. #if INTERIOR_LAYER == 1
  479. cInterior = BlobInterior( vWorldNormal, vBumpedTSNormal, vEyeDir,
  480. vScreenPos, flPixelDepth, flCamDist, flBlobOpacity, cFlashlightColor );
  481. #endif
  482. // Self-illum
  483. #if SELF_ILLUM_FRESNEL == 1
  484. BlobSelfIllumFresnel( vFresnelWorldNormal, vEyeDir,
  485. cDiffuse );
  486. #endif
  487. #if ( INTERIOR_LAYER == 0 )
  488. // Outer layer only
  489. cOut.rgb = cBase.rgb * ( cAmbient.rgb + cDiffuse.rgb ) + flSpecMask * ( cSpec + cSpec2 ) + cRim;
  490. #else
  491. // Outer layer blended over inner/background colour
  492. cInterior.rgb += flBlobOpacity * flSpecMask * cSpec;
  493. cOut.rgb = cBase.rgb * ( cAmbient.rgb + cDiffuse.rgb ) + flSpecMask * cSpec2 + cRim;
  494. cOut.rgb = lerp( cInterior.rgb, cOut.rgb, flBlobOpacity * flSkinOpacity );
  495. #endif
  496. #if CONTACT_SHADOW == 1
  497. cOut.rgb *= BlobContactShadow( vWorldNormal, vClosestSurfaceDir );
  498. #elif SELF_ILLUM_PULSE == 1
  499. cOut.rgb += BlobSelfIllumPulse( cPulseColor, cBase, flSkinOpacity );
  500. #endif
  501. #if VOLUME_TEXTURE == 1
  502. PerformVolumeTexturing( i.vUV0.xyz, vWorldNormal, cOut.rgb );
  503. #endif
  504. // TODO: Support fog
  505. cOut.a = 1;
  506. return FinalOutput( cOut, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, flPixelDepth );
  507. }