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.

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