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.

519 lines
23 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] [CONSOLE]
  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. samplerCUBE 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. #define g_cInteriorColor float3( 0.1f, 0.6f, 0.9f )
  66. #define g_flRefractBlur 0.4f
  67. #define g_cEnvMapTint float3( 1.0f, 1.0f, 1.0f )
  68. #define g_flFogNormalBoost 8.5f
  69. // TODO: unify this across pixel shaders (declare the constants in common_ps_fxc.h, as is already done for vertex shader ambient)
  70. const float3 cAmbientCube[6] : register( PSREG_AMBIENT_CUBE );
  71. const PixelShaderLightInfo g_sLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total
  72. const float4 g_vFlashlightAttenuationFactors_FarZ : register( PSREG_FLASHLIGHT_ATTENUATION );
  73. #define g_vFlashlightAttenuationFactors g_vFlashlightAttenuationFactors_FarZ.xyz
  74. #define g_flFlashlightFarZ g_vFlashlightAttenuationFactors_FarZ.w
  75. const float4 g_vFlashlightPos_RimBoost : register( PSREG_FLASHLIGHT_POSITION_RIM_BOOST );
  76. #define g_vFlashlightPos g_vFlashlightPos_RimBoost.xyz
  77. const float4x4 g_mFlashlightWorldToTexture : register( PSREG_FLASHLIGHT_TO_WORLD_TEXTURE );
  78. const float4 g_vShadowTweaks : register( PSREG_ENVMAP_TINT__SHADOW_TWEAKS );
  79. // COMBO-DERIVED CONSTANTS
  80. static const bool bLightWarp = LIGHT_WARP ? true : false;
  81. static const bool bFlashLight = FLASHLIGHT ? true : false;
  82. // INPUT STRUCT
  83. struct PS_INPUT
  84. {
  85. float4 vWorldNormal : TEXCOORD0; // w is proj. z coord (for depth stuff)
  86. float4 vClosestSurfaceDir : TEXCOORD1; // Used if CONTACT_SHADOW is on
  87. float4 vWorldPos : TEXCOORD2; // w is proj. w coord
  88. float4 vUV0 : TEXCOORD3;
  89. float4 vUV1 : TEXCOORD4;
  90. float4 vLightAtten : TEXCOORD5;
  91. float3 vLightCube : TEXCOORD6;
  92. };
  93. //==============================================================================================================================================================
  94. float Luminance( const float3 colour )
  95. {
  96. return dot( colour, float3( 0.3, 0.59, 0.11 ) );
  97. }
  98. //==============================================================================================================================================================
  99. float3 ComputeTextureBlendWeights( float3 vWorldNormal )
  100. {
  101. float3 vBlendWeights = max( ( abs( vWorldNormal.xyz ) - 0.2 ) * 7.0, 0.0 );
  102. vBlendWeights /= dot( vBlendWeights, float3(1, 1, 1) ); // normalize
  103. return vBlendWeights;
  104. }
  105. //==============================================================================================================================================================
  106. float4 BlendedTexFetch( sampler s, float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights )
  107. {
  108. float4 vFetch0 = tex2D( s, vUV0 );
  109. float4 vFetch1 = tex2D( s, vUV1 );
  110. float4 vFetch2 = tex2D( s, vUV2 );
  111. return vBlendWeights.x * vFetch0 + vBlendWeights.y * vFetch1 + vBlendWeights.z * vFetch2;
  112. }
  113. //==============================================================================================================================================================
  114. float3 BumpedToWorldNormal( float3 vBumpedNormal,
  115. float3 vVertexNormal, // should be normalized
  116. float3 vTangentDir )
  117. {
  118. float3x3 mTanToWorld;
  119. mTanToWorld[2] = vVertexNormal;
  120. mTanToWorld[0] = vTangentDir - dot( vTangentDir, vVertexNormal ) * vVertexNormal;
  121. mTanToWorld[0] = normalize( mTanToWorld[0] );
  122. mTanToWorld[1] = cross( mTanToWorld[0], mTanToWorld[2] );
  123. return normalize( mul( vBumpedNormal, mTanToWorld ) );
  124. }
  125. //==============================================================================================================================================================
  126. void BlendedTexFetchNormal( sampler s, float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights, float3 vWorldNormal,
  127. // Function outputs:
  128. out float2 vBumpedTSNormal, out float3 vBumpedWorldNormal, out float3 vFresnelWorldNormal )
  129. {
  130. // minimize instruction
  131. /*vBumpedTSNormal = float2(0,0);
  132. vBumpedWorldNormal = vFresnelWorldNormal = vWorldNormal;*/
  133. float3 vNormalTS1 = 2.0 * tex2D( g_tBump, vUV0 ).xyz - 1.0;
  134. float3 vNormalTS2 = 2.0 * tex2D( g_tBump, vUV1 ).xyz - 1.0;
  135. float3 vNormalTS3 = 2.0 * tex2D( g_tBump, vUV2 ).xyz - 1.0;
  136. vBumpedTSNormal = vBlendWeights.x * vNormalTS1.xy + vBlendWeights.y * vNormalTS2.xy + vBlendWeights.z * vNormalTS3.xy;
  137. float3 vBumpedNormal1 = BumpedToWorldNormal( vNormalTS1, vWorldNormal, float3( 0, 1, 0 ) );
  138. float3 vBumpedNormal2 = BumpedToWorldNormal( vNormalTS2, vWorldNormal, float3( 1, 0, 0 ) );
  139. float3 vBumpedNormal3 = BumpedToWorldNormal( vNormalTS3, vWorldNormal, float3( 1, 0, 0 ) );
  140. vBumpedWorldNormal = vBlendWeights.x * vBumpedNormal1 + vBlendWeights.y * vBumpedNormal2 + vBlendWeights.z * vBumpedNormal3;
  141. // Apply bump strength in world space (this is cheaper because we have to do it twice, for normal and fresnel bumpstrength)
  142. float3 vBumpStrengthDir = vBumpedWorldNormal - dot( vBumpedWorldNormal, vWorldNormal ) * vWorldNormal;
  143. vFresnelWorldNormal = normalize( vBumpedWorldNormal + ( g_flFresnelBumpStrength - 1.0 ) * vBumpStrengthDir );
  144. vBumpedWorldNormal = normalize( vBumpedWorldNormal + ( g_flBumpStrength - 1.0 ) * vBumpStrengthDir );
  145. }
  146. //==============================================================================================================================================================
  147. void ComputeOpacityAndFresnel( float2 vUV0, float2 vUV1, float2 vUV2, float3 vBlendWeights,
  148. float3 vEyeDir, float3 vWorldNormal,
  149. // Function outputs:
  150. out float flSkinOpacity, out float flFresnel )
  151. {
  152. flSkinOpacity = 1;
  153. #if OPACITY_TEXTURE == 1
  154. flSkinOpacity = BlendedTexFetch( g_tOpacity, vUV0, vUV1, vUV2, vBlendWeights ).x;
  155. #endif
  156. flFresnel = saturate( 1.0 - dot( vEyeDir.xyz, vWorldNormal.xyz ) );
  157. #if FRESNEL_WARP == 1
  158. float fTranslucentFresnel = tex1D( g_tFresnelWarp, flFresnel ).x;
  159. #else
  160. float fTranslucentFresnel = lerp( g_vTranslucentFresnelParams.x, g_vTranslucentFresnelParams.y, pow( flFresnel, g_vTranslucentFresnelParams.z ) );
  161. flSkinOpacity *= fTranslucentFresnel;
  162. #endif
  163. }
  164. //==============================================================================================================================================================
  165. float3 CubeAverage( void )
  166. {
  167. // FIXME: Pass this average light color in as a const
  168. float3 cAvgLight = 0.0;
  169. for( int j = 0; j < 6; j++ ) cAvgLight += cAmbientCube[j] / 6.0;
  170. return cAvgLight;
  171. }
  172. //==============================================================================================================================================================
  173. float3 IceAmbientLight( float3 vVertexAmbient, float3 vEyeDir, float3 vWorldNormal, float flFresnel,
  174. // Function outputs:
  175. out float3 cAmbient, out float3 cAvgAmbient )
  176. {
  177. // Ambient lighting now comes from VS
  178. cAmbient = vVertexAmbient;
  179. // TODO: Replace lambert diffuse with pixelshader-ambient term of full lighting env.
  180. //cAmbient = PixelShaderAmbientLight( vBumpedWorldNormal, cAmbientCube );
  181. // TODO: Ambient sheen on the outer layer
  182. //float3 cAmbientSheen = PixelShaderAmbientLight( reflect( -vEyeDir, vBumpedWorldNormal ), cAmbientCube );
  183. //cAmbient = lerp( cAmbient, cAmbientSheen, flFresnel );
  184. cAvgAmbient = CubeAverage();
  185. return cAmbient;
  186. }
  187. //==============================================================================================================================================================
  188. void IceDynamicLight( float3 vWorldPos, float3 vEyeDir, float3 vBumpedWorldNormal, float4 vLightAtten, float flFresnel,
  189. // Function outputs:
  190. out float3 cDiffuse, out float3 cSpec, out float3 cSpec2, out float3 cRim )
  191. {
  192. cDiffuse = cSpec = cSpec2 = cRim = 0;
  193. for ( int l = 0; l < NUM_LIGHTS; l++ )
  194. {
  195. cDiffuse.rgb += vLightAtten[l] * PixelShaderGetLightColor( g_sLightInfo, l ) *
  196. DiffuseTerm( true, vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ),
  197. bLightWarp, g_tLightWarp );
  198. // spec 1
  199. float3 cCurrSpec, cCurrRim;
  200. bool bYesRimLight = true;
  201. SpecularAndRimTerms( vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ), g_flSpecExp, vEyeDir,
  202. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  203. PixelShaderGetLightColor( g_sLightInfo, l ),
  204. bYesRimLight, g_flRimLightExp,
  205. cCurrSpec, cCurrRim );
  206. cSpec += vLightAtten[l] * cCurrSpec;
  207. cRim += vLightAtten[l] * cCurrRim;
  208. // spec 2
  209. float3 cCurrSpec2, cDummy;
  210. bool bNoRimLight = false;
  211. SpecularAndRimTerms( vBumpedWorldNormal, PixelShaderGetLightVector( vWorldPos, g_sLightInfo, l ), g_flSpecExp2, vEyeDir,
  212. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  213. PixelShaderGetLightColor( g_sLightInfo, l ),
  214. bNoRimLight, g_flRimLightExp,
  215. cCurrSpec2, cDummy );
  216. cSpec2 += vLightAtten[l] * cCurrSpec2;
  217. // FIXME: no rim2 term?
  218. }
  219. cRim *= flFresnel * flFresnel * flFresnel * flFresnel;
  220. }
  221. //==============================================================================================================================================================
  222. void IceFlashlight( float3 vWorldPos, float3 vEyeDir, float3 vWorldNormal, float2 vScreenPos, float flSpecularExponent,
  223. // Function outputs:
  224. out float3 cOutFlashlightDiffuse, out float3 cOutFlashlightSpec, out float3 cOutFlashlightColor )
  225. {
  226. float3 delta = g_vFlashlightPos - vWorldPos;
  227. float3 vLightVec = normalize( delta );
  228. float distSquared = dot( delta, delta );
  229. float dist = sqrt( distSquared );
  230. // Attenuation for light and to fade out shadow over distance
  231. float fAtten = saturate( dot( g_vFlashlightAttenuationFactors, float3( 1.0f, 1.0f/dist, 1.0f/distSquared ) ) );
  232. float endFalloffFactor = RemapValClamped( dist, g_flFlashlightFarZ, 0.6f * g_flFlashlightFarZ, 0.0f, 1.0f );
  233. // Project into flashlight texture
  234. float4 flashlightSpacePosition = mul( float4( vWorldPos, 1.0f ), g_mFlashlightWorldToTexture ); // TODO: this can be moved to VS
  235. float3 vProjCoords = flashlightSpacePosition.xyz / flashlightSpacePosition.w;
  236. // Flashlight colour
  237. cOutFlashlightColor = tex2D( g_tFlashlightCookie, vProjCoords );
  238. #if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
  239. cOutFlashlightColor *= cFlashlightColor.xyz;
  240. #endif
  241. cOutFlashlightColor *= endFalloffFactor * fAtten;
  242. // Flashlight shadow
  243. #if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0))
  244. if ( FLASHLIGHTSHADOWS )
  245. {
  246. float flShadow = DoFlashlightShadow( g_tShadowDepth, g_tNormalizeRandRot, vProjCoords, vScreenPos,
  247. FLASHLIGHTDEPTHFILTERMODE, g_vShadowTweaks );
  248. float flAttenuated = lerp( flShadow, 1.0f, g_vShadowTweaks.y ); // Blend between fully attenuated and not attenuated
  249. flShadow = saturate( lerp( flAttenuated, flShadow, fAtten ) ); // Blend between shadow and above, according to light attenuation
  250. cOutFlashlightColor *= flShadow; // Shadow term
  251. }
  252. #endif
  253. // Flashlight diffuse term
  254. cOutFlashlightDiffuse = cOutFlashlightColor * DiffuseTerm( true, vWorldNormal, vLightVec, bLightWarp, g_tLightWarp );
  255. // Flashlight specular term
  256. float3 cDummy;
  257. SpecularAndRimTerms( vWorldNormal, vLightVec, flSpecularExponent, -vEyeDir,
  258. false, g_tBump, 1.0, // dummy spec warp sampler & fresnel
  259. cOutFlashlightColor,
  260. false, g_flRimLightExp,
  261. cOutFlashlightSpec, cDummy );
  262. }
  263. //==============================================================================================================================================================
  264. float4 SampleBackgroundBlurred( float2 vBumpedTSNormal, float3 vWorldNormal, float2 vScreenPos, float flCamDist, float flFresnel )
  265. {
  266. // Bumped refract
  267. float flRefractStrength = (2.0f - flFresnel) * 80.0 * g_flRefractStrength / flCamDist;
  268. float2 vBackgroundUV = flRefractStrength * vBumpedTSNormal + vScreenPos;
  269. float4 cOut = /*1.0/8.0 * */tex2D( g_tScreen, vBackgroundUV );
  270. return cOut;
  271. }
  272. float3 IceInterior( float3 vWorldNormal, float2 vBumpedTSNormal, float3 vEyeDir, float flFresnel,
  273. float2 vScreenPos, float flPixelDepth, float flCamDist, float3 cAvgAmbient, float3 cFlashlightColor )
  274. {
  275. float3 cInterior = 0;
  276. // Sample the background (TODO: and inner ice geometry?)
  277. float4 cBackground = SampleBackgroundBlurred( vBumpedTSNormal, vWorldNormal, vScreenPos, flCamDist, flFresnel );
  278. // Boost bright background pixels
  279. float flLuminance = Luminance( cBackground.rgb );
  280. cBackground.rgb *= 1.0 + g_flInteriorBoost * flLuminance * flLuminance;
  281. // Fake refract-like vector without any total internal reflection crappiness
  282. float3 vRefract = normalize( -( vEyeDir + vWorldNormal ) );
  283. // Interior lighting through ambient cube
  284. float3 interiorColor = g_cBaseTint;
  285. float3 cBackLight = PixelShaderAmbientLight( vRefract, cAmbientCube );
  286. float3 cAvgLight = cAvgAmbient + ( 0.6 * cFlashlightColor );
  287. cBackLight = max( g_flInteriorLightScale * cAvgLight, g_flInteriorBackLightScale * cBackLight );
  288. #if 0 // Disabled because dest alpha depth isn't currently working
  289. // Get eye ray travel distance through the ice
  290. float flBackgroundDepth = cBackground.a * g_flDepthScale;
  291. float flDistThroughIce = flBackgroundDepth - flPixelDepth;
  292. #if HIGH_PRECISION_DEPTH == 0
  293. // Fade to constant interior fogginess as we run against the low-precision destalpha depth limit
  294. flDistThroughIce = lerp( flDistThroughIce, g_flDepthScale, saturate( cBackground.a*cBackground.a*cBackground.a ) );
  295. #endif
  296. // Modify thickness based on normals (assume edge-on ice is thicker w.r.t the camera)
  297. // TODO: this gives a bit of depth variation based on normals - draw ice back surfaces into depth to improve this
  298. float facing = saturate( dot( vEyeDir, vWorldNormal ) ) - 1;
  299. float flFogNormalBoost = g_flFogNormalBoost;
  300. flDistThroughIce *= 1 + flFogNormalBoost*facing*facing;
  301. // So that ice in dark areas doesn't glow, reduce thickness based on ambient/backlight luminance
  302. float flBackLightLuminance = Luminance( cBackLight );
  303. flDistThroughIce *= flBackLightLuminance;
  304. // Compute the opacity ('fog') of the ice interior volume, based on its thickness
  305. float flFogAtten = pow( 2.0, -flDistThroughIce * g_flInnerFogStrength ); // exponential falloff
  306. //float flFogAtten = saturate( 0.5 - 0.011 * flDistThroughIce ); // linear falloff
  307. // TODO: add a depth-based colour warp
  308. //interiorColor = saturate( interiorColor - 0.5*( 1 - flFogAtten)*(1-flFogAtten) );
  309. // Composite the fog interior lighting/fog over the background
  310. cBackLight *= interiorColor;
  311. cInterior = lerp( cBackLight, cBackground.rgb, flFogAtten );
  312. //float flInScattering = flDistThroughIce * 0.002;
  313. //cInterior.rgb += flInScattering * i.vLightCube.rgb;
  314. #endif // 0
  315. float flRimEffect = saturate( dot( vEyeDir, vWorldNormal ) );
  316. cInterior = lerp( cBackLight, cBackground.rgb, sqrt( flRimEffect ) );
  317. return cInterior;
  318. }
  319. //==============================================================================================================================================================
  320. float IceContactShadow( float3 vWorldNormal, float4 vClosestSurfaceDir )
  321. {
  322. // contact darkening for ice regions that touch a surface
  323. float3 vSurfaceDir = normalize( vClosestSurfaceDir.xyz ) * vClosestSurfaceDir.w;
  324. float flContactShadow = saturate( 0.8 * ( 1.0 - dot( vSurfaceDir, vWorldNormal ) ) + 0.2 );
  325. flContactShadow = lerp ( 0.3, 1.0, flContactShadow * flContactShadow * flContactShadow );
  326. return flContactShadow;
  327. }
  328. //==============================================================================================================================================================
  329. float3 noise( float3 coord )
  330. {
  331. coord.z *= 50.0;
  332. float zfrac = frac( coord.z );
  333. float2 zhash = float2( coord.z, 1 + coord.z );
  334. zhash -= frac( zhash );
  335. zhash = ( zhash * zhash );
  336. zhash -= 31.0 * floor( zhash / 31.0 );
  337. zhash = ( zhash * zhash );
  338. zhash -= 31.0 * floor( zhash / 31.0 );
  339. zhash *= 1.0/31.0;
  340. float3 c0 = tex2D( g_tBase, float4( coord.xy + float2( 0, zhash.x ), 0, 0 ) ).rgb;
  341. float3 c1 = tex2D( g_tBase, float4( coord.xy + float2( 0, zhash.y ), 0, 0 ) ).rgb;
  342. float3 rslt = lerp( c0, c1, zfrac );
  343. return rslt;
  344. }
  345. //==============================================================================================================================================================
  346. float4_color_return_type main( PS_INPUT i ) : COLOR
  347. {
  348. float4 cOut = { 0, 0, 0, 1 };
  349. // Set up misc camera variables
  350. float flPixelDepth = i.vWorldNormal.w;
  351. float2 vScreenPos = i.vUV1.wz / i.vWorldPos.w;
  352. float3 vEyeDir = g_vEyePos.xyz - i.vWorldPos.xyz;
  353. float flCamDist = length( vEyeDir );
  354. vEyeDir /= flCamDist;
  355. // For now, 'BACK_SURFACE' is a dead-simple path (just writes depth into dest alpha)
  356. #if ( BACK_SURFACE == 1 )
  357. cOut = tex2D( g_tScreen, vScreenPos );
  358. return FinalOutput( cOut, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, flPixelDepth );
  359. #endif
  360. // Blend weights for 3 blended planar projections
  361. float3 vWorldNormal = normalize( i.vWorldNormal.xyz );
  362. float3 vBlendWeights = ComputeTextureBlendWeights( vWorldNormal );
  363. // Base+spec maps
  364. // FIXME: the outer layer doesn't really need a base texture - it's just frost, so, it just needs opacity
  365. float4 cBase = BlendedTexFetch( g_tBase, i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights );
  366. float flSpecMask = BlendedTexFetch( g_tSpecMask, i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights ).x;
  367. // Normal mapping
  368. float3 vBumpedWorldNormal, vFresnelWorldNormal;
  369. float2 vBumpedTSNormal;
  370. BlendedTexFetchNormal( g_tBump, i.vUV0.xy, i.vUV0.zw, i.vUV1.xy, vBlendWeights, vWorldNormal,
  371. vBumpedTSNormal, vBumpedWorldNormal, vFresnelWorldNormal );
  372. // Opacity and fresnel
  373. float flSkinOpacity, flFresnel;
  374. ComputeOpacityAndFresnel( i.vUV0.xy, i.vUV0.wz, i.vUV1.xy, vBlendWeights, vEyeDir, vFresnelWorldNormal,
  375. flSkinOpacity, flFresnel );
  376. // Ambient light
  377. float3 cAmbient, cAvgAmbient;
  378. IceAmbientLight( i.vLightCube, vEyeDir, vBumpedWorldNormal, flFresnel,
  379. cAmbient, cAvgAmbient );
  380. // Dynamic lights
  381. float3 cDiffuse, cSpec, cSpec2, cRim;
  382. IceDynamicLight( i.vWorldPos.xyz, vEyeDir, vBumpedWorldNormal, i.vLightAtten, flFresnel,
  383. cDiffuse, cSpec, cSpec2, cRim );
  384. // Environment reflection
  385. float3 vViewDirReflected = reflect( -vEyeDir, vBumpedWorldNormal );
  386. // Must use environment map scale to get proper behavior across platforms with different texture formats (divide by 16 because code was written on PC where env_map_scale is 16.0)
  387. float3 cEnvironment = flFresnel * 0.67f * g_cEnvMapTint * texCUBE( g_tEnvironment, vViewDirReflected ).rgb * ( ENV_MAP_SCALE / 16.0f );
  388. // Scale envmap with diffuse light
  389. cEnvironment *= ( 1.0f + cAmbient.rgb + cDiffuse.rgb );
  390. // Flashlight
  391. float3 cFlashlightColor = 0;
  392. /*#if FLASHLIGHT == 1
  393. float3 cFlashlightDiffuse, cFlashlightSpec;
  394. IceFlashlight( i.vWorldPos.xyz, vEyeDir, vBumpedWorldNormal, vScreenPos, g_flSpecExp2,
  395. cFlashlightDiffuse, cFlashlightSpec, cFlashlightColor );
  396. cDiffuse.rgb += cFlashlightDiffuse;
  397. cSpec2 += cFlashlightSpec;
  398. #endif*/
  399. // Scale light terms
  400. cDiffuse *= g_flDiffuseScale;
  401. cSpec *= g_flSpecScale;
  402. cSpec2 *= g_flSpecScale2;
  403. cRim *= g_flRimLightScale;
  404. // Slight emissive term so we can see paint in the dark. Also needs to match lightmappedpaint.
  405. cDiffuse.rgb = ( ( cDiffuse.rgb * 0.82f ) + 0.04f );
  406. // FIXME: expose this as a materialvar (or I guess tweak actual scene lights)
  407. cDiffuse.rgb *= float3(0.8,0.85,1.0);
  408. #if ( INTERIOR_LAYER == 0 )
  409. // Outer layer only
  410. cOut.rgb = cBase.rgb * ( cAmbient.rgb + cDiffuse.rgb ) + flSpecMask * ( cSpec + cSpec2 ) + cRim + cEnvironment;
  411. #else
  412. // Outer layer blended over inner/background colour
  413. float3 cExterior = cBase.rgb * ( cAmbient.rgb + cDiffuse.rgb ) + flSpecMask * ( cSpec2 + cRim );
  414. float3 cInterior = IceInterior( vWorldNormal, vBumpedTSNormal, vEyeDir, flFresnel,
  415. vScreenPos, flPixelDepth, flCamDist, cAvgAmbient, cFlashlightColor );
  416. //float3 cInterior = SampleBackgroundBlurred( vBumpedTSNormal, vWorldNormal, vScreenPos, flCamDist );
  417. // Inner layer is meant to be smooth, glassy ice, so give it unmasked envmap and spec
  418. cInterior.rgb *= cBase.rgb;
  419. cInterior.rgb += cSpec + cEnvironment;
  420. cOut.rgb = lerp( cInterior.rgb, cExterior, flSkinOpacity );
  421. #endif
  422. #if CONTACT_SHADOW == 1
  423. cOut.rgb *= IceContactShadow( vWorldNormal, i.vClosestSurfaceDir );
  424. #endif
  425. // TODO: Support fog
  426. cOut.a = 1;
  427. return FinalOutput( cOut, 0, PIXEL_FOG_TYPE_NONE, TONEMAP_SCALE_LINEAR, g_fWriteDepthToAlpha, flPixelDepth );
  428. }