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.
716 lines
25 KiB
716 lines
25 KiB
//========== Copyright (c) Valve Corporation, All rights reserved. ==========//
|
|
|
|
// STATIC: "BASETEXTURE" "0..1"
|
|
// STATIC: "MULTITEXTURE" "0..1"
|
|
// STATIC: "REFLECT" "0..1"
|
|
// STATIC: "REFRACT" "0..1"
|
|
// STATIC: "ABOVEWATER" "0..1"
|
|
// STATIC: "FLOWMAP" "0..1" [ps20b]
|
|
// STATIC: "FLOWMAP" "0..0" [ps20] [ = 0; ]
|
|
// STATIC: "FLOW_DEBUG" "0..2"
|
|
// STATIC: "FLASHLIGHT" "0..1" [ps20b]
|
|
// STATIC: "FLASHLIGHT" "0..0" [ps20] [ = 0; ]
|
|
// STATIC: "LIGHTMAPWATERFOG" "0..1" [ps20b]
|
|
// STATIC: "LIGHTMAPWATERFOG" "0..0" [ps20] [ = 0; ]
|
|
// STATIC: "FORCEFRESNEL" "0..1"
|
|
// STATIC: "SIMPLEOVERLAY" "0..1"
|
|
// DYNAMIC: "FLASHLIGHTSHADOWS" "0..1" [ps20b]
|
|
// DYNAMIC: "FLASHLIGHTSHADOWS" "0..0" [ps20] [ = 0; ]
|
|
// DYNAMIC: "BUILDWORLDIMPOSTER" "0..1" [ = r_buildingmapforworld.GetBool() ? 1 : 0 ]
|
|
|
|
// Multitexture and basetexture are mutually exclusive.
|
|
// SKIP: $MULTITEXTURE && $BASETEXTURE
|
|
|
|
// SKIP: $SIMPLEOVERLAY && $BASETEXTURE
|
|
// SKIP: $SIMPLEOVERLAY && !$FLOWMAP
|
|
|
|
// flowmap doesn't play with multitexture
|
|
// SKIP: $FLOWMAP && $MULTITEXTURE
|
|
|
|
// Have to have the flashlight on to get flashlightshadows.
|
|
// SKIP: ( $FLASHLIGHT == 0 ) && ( $FLASHLIGHTSHADOWS == 1 )
|
|
|
|
// basetexture doesn't work with flashlight or lightmapwaterfog. multitexture doesn't either. We don't use basetexture or multitexture in newer code and instead use flowmap and flashlight.
|
|
// SKIP: ( $FLASHLIGHT || $LIGHTMAPWATERFOG ) && ( ( $BASETEXTURE && !$FLOWMAP ) || $MULTITEXTURE )
|
|
|
|
#if defined( _PS3 ) || defined ( _X360 )
|
|
#define DEPTH_EDGE_FEATHERING 1
|
|
#endif
|
|
|
|
#ifdef _X360
|
|
#define SHADER_SRGB_READ 1
|
|
#endif
|
|
|
|
#include "common_ps_fxc.h"
|
|
#include "common_fog_ps_fxc.h"
|
|
|
|
#include "shader_constant_register_map.h"
|
|
#include "common_flashlight_fxc.h"
|
|
|
|
#if REFRACT
|
|
sampler RefractSampler : register( s0 );
|
|
#endif
|
|
|
|
#if REFLECT
|
|
sampler ReflectSampler : register( s1 );
|
|
#else
|
|
sampler EnvSampler : register( s1 );
|
|
#endif
|
|
|
|
sampler NormalSampler : register( s2 );
|
|
sampler LightmapSampler : register( s3 );
|
|
|
|
#if BASETEXTURE
|
|
sampler BaseTextureSampler : register( s10 );
|
|
#endif
|
|
|
|
#if SIMPLEOVERLAY
|
|
sampler SimpleOverlaySampler : register( s11 );
|
|
#endif
|
|
|
|
#if FLOWMAP
|
|
sampler FlowmapSampler : register( s4 );
|
|
sampler FlowNoiseSampler : register( s5 );
|
|
#endif
|
|
|
|
#if FLASHLIGHT
|
|
sampler FlashlightSampler : register( s6 );
|
|
sampler ShadowDepthSampler : register( s7 );
|
|
sampler RandRotSampler : register( s8 );
|
|
|
|
#if defined(_PS3)
|
|
// Needed for optimal shadow filter code generation on PS3.
|
|
#pragma texformat ShadowDepthSampler DEPTH_COMPONENT24
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if DEPTH_EDGE_FEATHERING
|
|
sampler DepthSampler : register( s9 );
|
|
#endif
|
|
|
|
const float4 g_vRefractTint : register( c1 );
|
|
const float4 g_vReflectTint : register( c4 );
|
|
#define g_flWaterBlendFactor (g_vReflectTint.a)
|
|
const float4 g_ReflectRefractScale : register( c5 ); // xy - reflect scale, zw - refract scale
|
|
const float4 g_WaterFogColor : register( c6 );
|
|
|
|
const float4 g_WaterFogParams : register( c7 );
|
|
#define g_WaterFogStart g_WaterFogParams.x
|
|
#define g_WaterFogEndMinusStart g_WaterFogParams.y
|
|
#define g_Reflect_OverBright g_WaterFogParams.z
|
|
|
|
const float g_flTime : register( c8 );
|
|
|
|
#if FLOWMAP
|
|
const float2 g_vFlowScrollRate : register( c9 );
|
|
#endif
|
|
|
|
// The flashlight on the water surface is basically the diffuse flashlight * waterfogcolor * g_flFlashlightTint.
|
|
// g_flFlashlightTint is tweakable in cases where the water fog color is really dark and the flashlight doesn't show up, etc.
|
|
const float3 g_flFlashlightTint : register( c10 );
|
|
|
|
const float4 g_PixelFogParams : register( PSREG_FOG_PARAMS ); // c12
|
|
const float3 g_EyePos : register( PSREG_EYEPOS_SPEC_EXPONENT ); // c11
|
|
|
|
const float4 g_vFlowParams1 : register( c13 );
|
|
#define g_flWorldUvScale ( g_vFlowParams1.x ) // 1.0f / 10.0f
|
|
#define g_flNormalUvScale ( g_vFlowParams1.y ) // 1.0f / 1.15f
|
|
#define g_flBumpStrength ( g_vFlowParams1.z ) // 3.0f
|
|
#define g_flDisplaceStrength ( g_vFlowParams1.w ) // 0.025f // Amount to displace the color fetch based on the normals
|
|
|
|
const float3 g_vFlowParams2 : register( c14 );
|
|
#define g_flFlowTimeIntervalInSeconds ( g_vFlowParams2.x ) // 0.4f // Number of seconds to lerp from texture 1 to texture 2
|
|
#define g_flFlowUvScrollDistance ( g_vFlowParams2.y ) // 0.25f // Distance in uv space to fetch
|
|
#define g_flNoiseScale ( g_vFlowParams2.z )
|
|
|
|
const float4 g_vColorFlowParams1 : register( c26 );
|
|
#define g_flColorFlowUvScale ( g_vColorFlowParams1.x ) // 1.0f / 1.15f
|
|
#define g_flColorFlowTimeIntervalInSeconds ( g_vColorFlowParams1.y ) // 0.4f // Number of seconds to lerp from texture 1 to texture 2
|
|
#define g_flColorFlowUvScrollDistance ( g_vColorFlowParams1.z ) // 0.25f // Distance in uv space to fetch
|
|
#define g_flColorFlowLerpExp ( g_vColorFlowParams1.w )
|
|
|
|
const float4 g_FlashlightAttenuationFactors : register( c15 );
|
|
const float3 g_FlashlightPos : register( c16 );
|
|
const float4x4 g_FlashlightWorldToTexture : register( c17 ); // through c20
|
|
const float4 g_vShadowTweaks : register( c21 );
|
|
|
|
// These constants are used to rotate the world space water normals around the up axis to align the
|
|
// normal with the camera and then give us a 2D offset vector to use for reflection and refraction uv's
|
|
const float3 g_vWorldToViewWater0 : register( c22 );
|
|
const float3 g_vWorldToViewWater1 : register( c23 );
|
|
|
|
float4 g_vInvViewportTransform : register( c24 );
|
|
const float g_flForcedFresnelValue : register( c25 );
|
|
|
|
#if DEPTH_EDGE_FEATHERING
|
|
const float4 g_ProjToWorldZW[2] : register( c33 );
|
|
const float4 g_DepthEdgeFeatheringParams : register( c35 );
|
|
#endif
|
|
|
|
struct PS_INPUT
|
|
{
|
|
float2 vBumpTexCoord : TEXCOORD0;
|
|
float3 vPositionToCameraRayWs : TEXCOORD1;
|
|
float4 vReflectXY_vRefractYX : TEXCOORD2;
|
|
float4 vProjPos : TEXCOORD3;
|
|
float3 worldPos : TEXCOORD4;
|
|
#if FLASHLIGHT
|
|
float4 flashlightSpacePos : TEXCOORD5;
|
|
#endif
|
|
#if MULTITEXTURE
|
|
float4 vExtraBumpTexCoord : TEXCOORD5;
|
|
#endif
|
|
|
|
#if ( BASETEXTURE && !FLOWMAP )
|
|
float4 lightmapTexCoord1And2 : TEXCOORD5_centroid;
|
|
float2 lightmapTexCoord3 : TEXCOORD6_centroid;
|
|
#endif
|
|
|
|
#if LIGHTMAPWATERFOG
|
|
float2 lightmapTexCoord : TEXCOORD7_centroid;
|
|
#endif
|
|
|
|
#if defined( _X360 )
|
|
float2 vScreenPos : VPOS;
|
|
#endif
|
|
};
|
|
|
|
float2 UnpackNormal2D( float2 vNormal )
|
|
{
|
|
return ( ( vNormal.xy * 2.0 ) - 1.0 );
|
|
}
|
|
|
|
float3 UnpackNormal3D( float3 vNormal )
|
|
{
|
|
return ( ( vNormal.xyz * 2.0 ) - 1.0 );
|
|
}
|
|
|
|
float3 ComputeNormalFromXY( float2 vXY )
|
|
{
|
|
float3 vNormalTs;
|
|
|
|
vNormalTs.xy = vXY.xy;
|
|
vNormalTs.z = sqrt( saturate( 1.0 - dot( vNormalTs.xy, vNormalTs.xy ) ) );
|
|
|
|
return vNormalTs.xyz;
|
|
}
|
|
|
|
float3 ComputeNormalFromRGTexture( float2 vRGPixel )
|
|
{
|
|
float3 vNormalTs;
|
|
|
|
vNormalTs.xy = UnpackNormal2D( vRGPixel.rg );
|
|
vNormalTs.z = sqrt( saturate( 1.0 - dot( vNormalTs.xy, vNormalTs.xy ) ) );
|
|
|
|
return vNormalTs.xyz;
|
|
}
|
|
|
|
// Turning off 32bit lightmaps on Portal 2 to save shader perf. --Thorsten
|
|
//#define USE_32BIT_LIGHTMAPS_ON_360 //uncomment to use 32bit lightmaps, be sure to keep this in sync with the same #define in materialsystem/cmatlightmaps.cpp
|
|
|
|
float3 LightMapSample( sampler LightmapSampler, float2 vTexCoord )
|
|
{
|
|
#if ( !defined( _X360 ) || !defined( USE_32BIT_LIGHTMAPS_ON_360 ) )
|
|
{
|
|
float3 sample = tex2D( LightmapSampler, vTexCoord ).rgb;
|
|
|
|
return sample;
|
|
}
|
|
#else
|
|
{
|
|
#if 0 //1 for cheap sampling, 0 for accurate scaling from the individual samples
|
|
{
|
|
float4 sample = tex2D( LightmapSampler, vTexCoord );
|
|
|
|
return sample.rgb * sample.a;
|
|
}
|
|
#else
|
|
{
|
|
float4 Weights;
|
|
float4 samples_0; //no arrays allowed in inline assembly
|
|
float4 samples_1;
|
|
float4 samples_2;
|
|
float4 samples_3;
|
|
|
|
asm {
|
|
tfetch2D samples_0, vTexCoord.xy, LightmapSampler, OffsetX = -0.5, OffsetY = -0.5, MinFilter=point, MagFilter=point, MipFilter=keep, UseComputedLOD=false
|
|
tfetch2D samples_1, vTexCoord.xy, LightmapSampler, OffsetX = 0.5, OffsetY = -0.5, MinFilter=point, MagFilter=point, MipFilter=keep, UseComputedLOD=false
|
|
tfetch2D samples_2, vTexCoord.xy, LightmapSampler, OffsetX = -0.5, OffsetY = 0.5, MinFilter=point, MagFilter=point, MipFilter=keep, UseComputedLOD=false
|
|
tfetch2D samples_3, vTexCoord.xy, LightmapSampler, OffsetX = 0.5, OffsetY = 0.5, MinFilter=point, MagFilter=point, MipFilter=keep, UseComputedLOD=false
|
|
|
|
getWeights2D Weights, vTexCoord.xy, LightmapSampler
|
|
};
|
|
|
|
Weights = float4( (1-Weights.x)*(1-Weights.y), Weights.x*(1-Weights.y), (1-Weights.x)*Weights.y, Weights.x*Weights.y );
|
|
|
|
float3 result;
|
|
result.rgb = samples_0.rgb * (samples_0.a * Weights.x);
|
|
result.rgb += samples_1.rgb * (samples_1.a * Weights.y);
|
|
result.rgb += samples_2.rgb * (samples_2.a * Weights.z);
|
|
result.rgb += samples_3.rgb * (samples_3.a * Weights.w);
|
|
|
|
return result;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
float3 PosToColor( float3 vScenePositionWs )
|
|
{
|
|
int ix = vScenePositionWs.x / 15.0f;
|
|
int iy = vScenePositionWs.y / 15.0f;
|
|
float3 cColor;
|
|
|
|
if ( frac( iy / 2.0f ) )
|
|
{
|
|
if ( frac( ix / 2.0f ) )
|
|
{
|
|
cColor = float3( 1.0f, 0.0f, 0.0f );
|
|
}
|
|
else
|
|
{
|
|
cColor = float3( 0.0f, 1.0f, 0.0f );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( frac( ix / 2.0f ) )
|
|
{
|
|
cColor = float3( 0.0f, 1.0f, 0.0f );
|
|
}
|
|
else
|
|
{
|
|
cColor = float3( 1.0f, 0.0f, 0.0f );
|
|
}
|
|
}
|
|
|
|
return cColor;
|
|
}
|
|
|
|
float4_color_return_type main( PS_INPUT i ) : COLOR
|
|
{
|
|
float4 vResult;
|
|
float flFogFactor;
|
|
|
|
/* For debugging - Flash all water surfaces blue
|
|
vResult.rgba = float4( 0.25, 0.25, 1.0, 0 ) * ( ( floor( g_flTime * 2.0f ) % 2 ) * 0.9f + 0.1f );
|
|
flFogFactor = 0;
|
|
return FinalOutput( float4( vResult.rgb, 1.0f ), flFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
//*/
|
|
|
|
// Compute coordinates for sampling Reflection
|
|
float3 vPositionToCameraDirWs = normalize( i.vPositionToCameraRayWs.xyz );
|
|
|
|
float4 vNormalWs;
|
|
float4 vFlowColor = float4( 0.0f, 0.0f, 0.0f, 0.0f );
|
|
#if ( FLOWMAP )
|
|
{
|
|
float flWorldUvScale = g_flWorldUvScale;
|
|
float flNormalUvScale = g_flNormalUvScale;
|
|
float flFlowTimeIntervalInSeconds = g_flFlowTimeIntervalInSeconds;
|
|
float flFlowUvScrollDistance = g_flFlowUvScrollDistance;
|
|
float flBumpStrength = g_flBumpStrength;
|
|
float flNoiseScale = g_flNoiseScale;
|
|
|
|
// Input uv
|
|
float2 vWorldUv = i.vBumpTexCoord.xy * flWorldUvScale;
|
|
float2 vUv1 = float2( i.worldPos.x, -i.worldPos.y ) * flNormalUvScale;
|
|
float2 vUv2 = vUv1.xy;
|
|
|
|
// Noise texture is used to offset the time interval different spatially so we don't see pulsing
|
|
float flNoise = tex2D( FlowNoiseSampler, float2( i.worldPos.x, -i.worldPos.y ) * flNoiseScale ).g;
|
|
|
|
// Flow texel has a 2D flow vector in the rg channels of the texture
|
|
float4 vFlowTexel = tex2D( FlowmapSampler, vWorldUv.xy );
|
|
#if ( FLOW_DEBUG == 1 ) // Flow vectors
|
|
{
|
|
vResult.rgba = float4( pow( vFlowTexel.rgb, 2.2f ), 0 );
|
|
flFogFactor = 0;
|
|
return FinalOutput( float4( vResult.rgb, 1.0f ), flFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
}
|
|
#elif ( FLOW_DEBUG == 2 ) // Noise
|
|
{
|
|
vResult.rgba = pow( flNoise, 2.2 );
|
|
flFogFactor = 0;
|
|
return FinalOutput( float4( vResult.rgb, 1.0f ), flFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
}
|
|
#endif
|
|
|
|
// Unpack world flow vector from texture
|
|
float2 vFlowVectorTs = ( vFlowTexel.rg * 2.0f ) - 1.0f;
|
|
|
|
float flTimeInIntervals = ( g_flTime / ( flFlowTimeIntervalInSeconds * 2.0f ) ) + flNoise;
|
|
float flScrollTime1 = frac( flTimeInIntervals );
|
|
float flScrollTime2 = frac( flTimeInIntervals + 0.5f ); // Half an interval off from texture 1
|
|
|
|
// Every interval has a unique offset so we don't see the same bump texels repeating continuously
|
|
float flOffset1 = floor( flTimeInIntervals ) * 0.311f;
|
|
float flOffset2 = floor( flTimeInIntervals + 0.5f ) * 0.311f + 0.5f; // The +0.5 is to match the phase offset
|
|
|
|
// Final flow uv is originalUv + interval offset + ( flowvector * scroll
|
|
float2 vFlowUv1 = vUv1.xy + flOffset1 + ( flScrollTime1 * ( flFlowUvScrollDistance * vFlowVectorTs.xy ) );
|
|
float2 vFlowUv2 = vUv2.xy + flOffset2 + ( flScrollTime2 * ( flFlowUvScrollDistance * vFlowVectorTs.xy ) );
|
|
|
|
// Lerp values to blend between the two layers of bump
|
|
float flWeight1 = abs( ( 2.0f * frac( flTimeInIntervals + 0.5f ) ) - 1.0f );
|
|
float flWeight2 = abs( ( 2.0f * frac( flTimeInIntervals ) ) - 1.0f );
|
|
|
|
float4 vNormalTexel1 = tex2D( NormalSampler, vFlowUv1.xy );
|
|
float4 vNormalTexel2 = tex2D( NormalSampler, vFlowUv2.xy );
|
|
|
|
float3 vNormal1 = ( vNormalTexel1.rgb );
|
|
float3 vNormal2 = ( vNormalTexel2.rgb );
|
|
|
|
// Combine both layers
|
|
vNormalWs.xy = UnpackNormal2D( lerp( vNormal1.xy, vNormal2.xy, flWeight2 ) );
|
|
|
|
// Change bump strength based on the length of the flow vector
|
|
//vNormalWs.xy *= ( length( vFlowVectorTs.xy ) + 0.05f ) * flBumpStrength;
|
|
vNormalWs.xy *= ( ( vFlowVectorTs.x * vFlowVectorTs.x + vFlowVectorTs.y * vFlowVectorTs.y ) + 0.1f ) * flBumpStrength;
|
|
|
|
// Generate normal from 2D scaled normal
|
|
vNormalWs.xyz = ComputeNormalFromXY( vNormalWs.xy );
|
|
//return pow( float4( vNormalWs.xy*0.5+0.5, 0, 0), 2.2);
|
|
|
|
//vResult.rgba = float4( SrgbGammaToLinear( vNormalWs.xyz * 0.5 + 0.5 ), 0 );
|
|
//flFogFactor = 0;
|
|
//return FinalOutput( float4( vResult.rgb, 1.0f ), flFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
|
|
vNormalWs.a = 1.0f;
|
|
|
|
//-------------------------------------------------------------//
|
|
// Specifying a base texture with flow gives us a sludge layer //
|
|
//-------------------------------------------------------------//
|
|
#if ( BASETEXTURE )
|
|
{
|
|
float flParallaxIntensity = lerp( vNormalTexel1.a, vNormalTexel2.a, flWeight2 );
|
|
flParallaxIntensity *= g_flDisplaceStrength;
|
|
float2 vParallaxDirWs = vPositionToCameraDirWs.xy - vNormalWs.xy;
|
|
float2 vColorUv = ( float2( i.worldPos.x, -i.worldPos.y ) )* g_flColorFlowUvScale + vParallaxDirWs * flParallaxIntensity;
|
|
|
|
float flTimeInIntervals = ( g_flTime / ( g_flColorFlowTimeIntervalInSeconds * 2.0f ) ) + flNoise;
|
|
float flScrollTime1 = frac( flTimeInIntervals ) - 0.5;
|
|
float flScrollTime2 = frac( flTimeInIntervals + 0.5f ) - 0.5; // Half an interval off from texture 1
|
|
|
|
float flOffset1 = floor( flTimeInIntervals ) * 0.311f;
|
|
float flOffset2 = floor( flTimeInIntervals + 0.5f ) * 0.311f + 0.5f; // The +0.5 is to match the phase offset
|
|
|
|
float2 vColorFlowUv1 = vColorUv.xy + flOffset1 + ( flScrollTime1 * ( g_flColorFlowUvScrollDistance * vFlowVectorTs.xy ) );
|
|
float2 vColorFlowUv2 = vColorUv.xy + flOffset2 + ( flScrollTime2 * ( g_flColorFlowUvScrollDistance * vFlowVectorTs.xy ) );
|
|
|
|
float flWeight1 = pow( abs( ( 2.0f * frac( flTimeInIntervals + 0.5f ) ) - 1.0f ), g_flColorFlowLerpExp );
|
|
float flWeight2 = pow( abs( ( 2.0f * frac( flTimeInIntervals ) ) - 1.0f ), g_flColorFlowLerpExp );
|
|
|
|
float4 vColorTexel1 = tex2Dsrgb( BaseTextureSampler, vColorFlowUv1.xy );
|
|
float4 vColorTexel2 = tex2Dsrgb( BaseTextureSampler, vColorFlowUv2.xy );
|
|
|
|
vFlowColor.rgba = vColorTexel1.rgba * flWeight1;
|
|
vFlowColor.rgba += vColorTexel2.rgba * flWeight2;
|
|
vFlowColor.rgba *= vFlowTexel.a; // Mask color flow by alpha of flowmap
|
|
}
|
|
#endif
|
|
}
|
|
#elif ( MULTITEXTURE )
|
|
{
|
|
vNormalWs.xyz = tex2D( NormalSampler, i.vBumpTexCoord ).xyz;
|
|
float3 vNormalWs1 = tex2D( NormalSampler, i.vExtraBumpTexCoord.xy ).xyz;
|
|
float3 vNormalWs2 = tex2D( NormalSampler, i.vExtraBumpTexCoord.zw ).xyz;
|
|
vNormalWs.xyz = 0.33 * ( vNormalWs.xyz + vNormalWs1.xyz + vNormalWs2.xyz );
|
|
|
|
vNormalWs.xyz = 2.0 * vNormalWs.xyz - 1.0;
|
|
|
|
vNormalWs.a = 1.0f;
|
|
}
|
|
#else
|
|
{
|
|
vNormalWs.xyzw = DecompressNormal( NormalSampler, i.vBumpTexCoord, NORM_DECODE_NONE );
|
|
}
|
|
#endif
|
|
|
|
// Perform division by W only once
|
|
float flOoW = 1.0f / i.vProjPos.w;
|
|
float2 unwarpedRefractTexCoord = i.vReflectXY_vRefractYX.wz * flOoW;
|
|
// Deal with the viewport transform. We don't do splitscreen on PC, so don't bother doing this with PS20.
|
|
unwarpedRefractTexCoord = g_vInvViewportTransform.zw + unwarpedRefractTexCoord * g_vInvViewportTransform.xy;
|
|
|
|
#if ( ABOVEWATER && REFRACT )
|
|
float4 unwarpedSample = tex2Dsrgb( RefractSampler, unwarpedRefractTexCoord );
|
|
float waterFogDepthValue = unwarpedSample.a;
|
|
#else
|
|
// We don't actually have valid depth values in alpha when we are underwater looking out, so
|
|
// just set to farthest value.
|
|
float waterFogDepthValue = 1.0f;
|
|
#endif
|
|
|
|
float4 vReflectRefractScale = g_ReflectRefractScale;
|
|
#if !BASETEXTURE
|
|
vReflectRefractScale *= waterFogDepthValue;
|
|
#endif
|
|
|
|
// vectorize the dependent UV calculations (reflect = .xy, refract = .wz)
|
|
float4 vN;
|
|
vN.x = dot( g_vWorldToViewWater0.xyz, vNormalWs.xyz );
|
|
vN.y = dot( g_vWorldToViewWater1.xyz, vNormalWs.xyz );
|
|
vN.wz = vN.xy;
|
|
|
|
float4 vDependentTexCoords = vN.xyzw * vNormalWs.a * vReflectRefractScale;
|
|
vDependentTexCoords += i.vReflectXY_vRefractYX * flOoW;
|
|
|
|
float2 vReflectTexCoord = vDependentTexCoords.xy;
|
|
float2 vRefractTexCoord = vDependentTexCoords.wz;
|
|
|
|
float4 vReflectColor;
|
|
#if ( REFLECT )
|
|
{
|
|
vReflectColor.rgba = tex2Dsrgb( ReflectSampler, vReflectTexCoord );
|
|
}
|
|
#else
|
|
{
|
|
float3 vReflectWs = CalcReflectionVectorUnnormalized( vNormalWs.xyz, vPositionToCameraDirWs.xyz );
|
|
vReflectColor.rgba = ENV_MAP_SCALE * texCUBE( EnvSampler, vReflectWs.xyz );
|
|
}
|
|
#endif
|
|
|
|
vReflectColor *= g_vReflectTint;
|
|
|
|
float4 vRefractColor;
|
|
#if REFRACT
|
|
#if defined( _X360 )
|
|
{
|
|
// deal with the viewport transform for splitscreen
|
|
vRefractColor = tex2Dsrgb( RefractSampler, g_vInvViewportTransform.zw + vRefractTexCoord * g_vInvViewportTransform.xy );
|
|
}
|
|
#else
|
|
{
|
|
vRefractColor = tex2Dsrgb( RefractSampler, vRefractTexCoord );
|
|
}
|
|
#endif
|
|
float warpedAlpha = vRefractColor.a;
|
|
// get the depth value from the refracted sample to be used for fog.
|
|
#if ABOVEWATER
|
|
// Don't mess with this in the underwater case since we don't really have
|
|
// depth values there.
|
|
waterFogDepthValue = vRefractColor.a;
|
|
#endif
|
|
#endif
|
|
|
|
// Fresnel term
|
|
float fFresnel;
|
|
#if FORCEFRESNEL
|
|
{
|
|
fFresnel = g_flForcedFresnelValue;
|
|
}
|
|
#else
|
|
{
|
|
float flNdotV = saturate( dot( vPositionToCameraDirWs.xyz, vNormalWs.xyz ) );
|
|
|
|
float g_flReflectance = 0.2f;
|
|
fFresnel = g_flReflectance + ( ( 1.0f - g_flReflectance ) * pow( 1.0f - flNdotV, 5.0f ) );
|
|
}
|
|
#endif
|
|
|
|
// Light maps
|
|
float3 vLightmapColor = float3( 1.0f, 1.0f, 1.0f );
|
|
float4 vLightmappedWaterFogColor = g_WaterFogColor;
|
|
#if LIGHTMAPWATERFOG
|
|
{
|
|
float3 lightmapSample = LightMapSample( LightmapSampler, i.lightmapTexCoord.xy );
|
|
vLightmapColor.rgb = lightmapSample * LIGHT_MAP_SCALE * LINEAR_LIGHT_SCALE;
|
|
vLightmappedWaterFogColor.xyz *= vLightmapColor.rgb;
|
|
}
|
|
#endif
|
|
|
|
// blend between refraction and fog color.
|
|
#if ABOVEWATER
|
|
#if REFRACT
|
|
// Avoid seeing things in front of the water warped in the water refraction by not warping when that case happens.
|
|
// Causes a bit of artifacting around foreground objects, but looks better than warping the foreground objects in the water surface.
|
|
if ( warpedAlpha < 0.05f )
|
|
{
|
|
vRefractColor.xyz = unwarpedSample.xyz;
|
|
waterFogDepthValue = unwarpedSample.w;
|
|
}
|
|
#endif
|
|
#if REFRACT
|
|
float edgeFadeFactor = saturate( 3.5 * waterFogDepthValue );
|
|
vRefractColor = lerp( vRefractColor, vRefractColor * g_vRefractTint, edgeFadeFactor );
|
|
#endif
|
|
|
|
#if ( !defined( SHADER_MODEL_PS_2_0 ) )
|
|
{
|
|
vReflectColor *= saturate( 2.0 * waterFogDepthValue );
|
|
}
|
|
#endif
|
|
|
|
#if REFRACT
|
|
vRefractColor = lerp( vRefractColor, vLightmappedWaterFogColor, waterFogDepthValue );
|
|
#endif
|
|
#endif
|
|
|
|
// Flashlight
|
|
float3 vDiffuseLight = vLightmapColor.rgb;
|
|
#if ( FLASHLIGHT )
|
|
{
|
|
float3 vFlashlightDiffuseLighting = DoFlashlight( g_FlashlightPos, i.worldPos.xyz, i.flashlightSpacePos.xyzw, vNormalWs.xyz,
|
|
g_FlashlightAttenuationFactors.xyz, g_FlashlightAttenuationFactors.w, FlashlightSampler, ShadowDepthSampler,
|
|
RandRotSampler, 0, FLASHLIGHTSHADOWS, i.vProjPos.xy / i.vProjPos.w, false, g_vShadowTweaks );
|
|
|
|
#if ( ABOVEWATER && REFRACT )
|
|
{
|
|
vFlashlightDiffuseLighting *= edgeFadeFactor;
|
|
}
|
|
#endif
|
|
|
|
vDiffuseLight.xyz += g_flFlashlightTint * vFlashlightDiffuseLighting.xyz * LINEAR_LIGHT_SCALE;
|
|
}
|
|
#endif
|
|
|
|
#if !BASETEXTURE
|
|
{
|
|
// fFresnel == 1.0f means full reflection
|
|
#if ( REFRACT )
|
|
{
|
|
fFresnel *= saturate( ( waterFogDepthValue - 0.05f ) * 20.0f );
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if ( BASETEXTURE && !FLOWMAP )
|
|
float4 baseSample = tex2D( BaseTextureSampler, i.vBumpTexCoord.xy );
|
|
|
|
float2 bumpCoord1;
|
|
float2 bumpCoord2;
|
|
float2 bumpCoord3;
|
|
ComputeBumpedLightmapCoordinates( i.lightmapTexCoord1And2, i.lightmapTexCoord3.xy,
|
|
bumpCoord1, bumpCoord2, bumpCoord3 );
|
|
|
|
float4 lightmapSample1 = tex2D( LightmapSampler, bumpCoord1 );
|
|
float3 lightmapColor1 = lightmapSample1.rgb;
|
|
float3 lightmapColor2 = tex2D( LightmapSampler, bumpCoord2 ).rgb;
|
|
float3 lightmapColor3 = tex2D( LightmapSampler, bumpCoord3 ).rgb;
|
|
|
|
float3 dp;
|
|
dp.x = saturate( dot( vNormalWs.xyz, bumpBasis[0] ) );
|
|
dp.y = saturate( dot( vNormalWs.xyz, bumpBasis[1] ) );
|
|
dp.z = saturate( dot( vNormalWs.xyz, bumpBasis[2] ) );
|
|
dp *= dp;
|
|
|
|
float3 diffuseLighting = dp.x * lightmapColor1 +
|
|
dp.y * lightmapColor2 +
|
|
dp.z * lightmapColor3;
|
|
float sum = dot( dp, float3( 1.0f, 1.0f, 1.0f ) );
|
|
diffuseLighting *= LIGHT_MAP_SCALE / sum;
|
|
float3 diffuseComponent = baseSample.rgb * diffuseLighting;
|
|
#endif
|
|
|
|
// The underwater color will be reused for the flashlight
|
|
float3 vUnderWater = g_WaterFogColor.rgb; // Default to fog color, but may be overridden below
|
|
|
|
#if ( REFLECT && REFRACT )
|
|
{
|
|
#if ( BASETEXTURE && FLOWMAP )
|
|
{
|
|
float3 vLight = vDiffuseLight.rgb;
|
|
|
|
// The alpha of flow color represents translucency from 0.0-0.5. The range 0.5-1.0 allows pixels to float above the water
|
|
float3 vSludge = vFlowColor.rgb * vLight.rgb;
|
|
vUnderWater.rgb = lerp( vRefractColor.rgb, vSludge.rgb, saturate( vFlowColor.a * 2.0f ) );
|
|
float flSludgeAboveWater = smoothstep( 0.5f, 0.7f, vFlowColor.a );
|
|
vResult.rgb = lerp( vUnderWater.rgb, vReflectColor.rgb, saturate( fFresnel * ( 1.0f - flSludgeAboveWater ) ) );
|
|
//vUnderWater.rgb *= 1.0f - fFresnel; // I don't think this is necessary since the flashlight applies a cosine term
|
|
}
|
|
#else
|
|
{
|
|
vResult = vRefractColor + ( fFresnel * vReflectColor );
|
|
}
|
|
#endif
|
|
}
|
|
#elif ( REFRACT )
|
|
{
|
|
vResult = vRefractColor + ( fFresnel * vReflectColor );
|
|
}
|
|
#else
|
|
{
|
|
#if ( BASETEXTURE && FLOWMAP )
|
|
{
|
|
float3 vLight = vDiffuseLight.rgb;
|
|
|
|
// The alpha of flow color represents translucency from 0.0-0.5. The range 0.5-1.0 allows pixels to float above the water
|
|
float3 vSludge = vFlowColor.rgb * vLight.rgb;
|
|
vUnderWater.rgb = lerp( vLightmappedWaterFogColor.rgb, vSludge.rgb, saturate( vFlowColor.a * 2.0f ) );
|
|
float flSludgeAboveWater = smoothstep( 0.5f, 0.7f, vFlowColor.a );
|
|
vResult.rgb = lerp( vUnderWater.rgb, vReflectColor.rgb, saturate( fFresnel * ( 1.0f - flSludgeAboveWater ) ) );
|
|
#if ( BUILDWORLDIMPOSTER )
|
|
{
|
|
vResult.rgb = vUnderWater.rgb;
|
|
}
|
|
#endif
|
|
//vUnderWater.rgb *= 1.0f - fFresnel; // I don't think this is necessary since the flashlight applies a cosine term
|
|
}
|
|
#elif ( BASETEXTURE )
|
|
{
|
|
vResult = float4( diffuseComponent, 1.0f ) + vReflectColor * fFresnel * baseSample.a;
|
|
}
|
|
#else
|
|
{
|
|
vResult = lerp( vLightmappedWaterFogColor, vReflectColor, fFresnel );
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
#if ( SIMPLEOVERLAY )
|
|
float4 simpleOverlayComponent = tex2D( SimpleOverlaySampler, i.vBumpTexCoord );
|
|
vResult.rgb = lerp( vResult.rgb, simpleOverlayComponent.rgb * vLightmapColor, simpleOverlayComponent.a * saturate( ( waterFogDepthValue - 0.01f ) * 5.0f ) );
|
|
#endif
|
|
|
|
#if ( PIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE )
|
|
{
|
|
flFogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_PixelFogParams, g_EyePos, i.worldPos, i.vProjPos.z );
|
|
}
|
|
#else
|
|
{
|
|
flFogFactor = 0;
|
|
}
|
|
#endif
|
|
|
|
float flWaterBlendAlpha = 1.0f;
|
|
|
|
#if ( DEPTH_EDGE_FEATHERING )
|
|
{
|
|
float2 vDepthSampleCoords = i.vReflectXY_vRefractYX.wz * flOoW;
|
|
float2 vViewportRelativeDepthSampleCoords = g_vInvViewportTransform.zw + vDepthSampleCoords * g_vInvViewportTransform.xy;
|
|
|
|
// Sample the scene's depth at the current fragment.
|
|
float flSceneProjZ = SampleHardwareDepth( DepthSampler, vViewportRelativeDepthSampleCoords );
|
|
|
|
// Compute current fragment's projection/clip space coords.
|
|
float2 vClipUv = ( ( 2.0f * vDepthSampleCoords.xy ) - float2( 1.0f, 1.0f ) ) * float2( 1.0f, -1.0f );
|
|
float4 vProjPos = float4( vClipUv.x, vClipUv.y, flSceneProjZ, 1.0f );
|
|
// Recover worldspace Z and W.
|
|
float vSceneWorldZ = dot( vProjPos, g_ProjToWorldZW[0] );
|
|
float vSceneWorldW = dot( vProjPos, g_ProjToWorldZW[1] );
|
|
// Project to W=1.
|
|
vSceneWorldZ /= vSceneWorldW;
|
|
|
|
// We now have the worldspace Z's of the current fragment and the surface below it.
|
|
// Subtract the current water surface's height from the computed worldspace Z (height) to compute edge feathing factor.
|
|
flWaterBlendAlpha = i.worldPos.z - vSceneWorldZ;
|
|
flWaterBlendAlpha = saturate( saturate( g_DepthEdgeFeatheringParams.x * flWaterBlendAlpha ) + g_DepthEdgeFeatheringParams.y );
|
|
}
|
|
#endif
|
|
|
|
float4_color_return_type vOutput = FinalOutput( float4( vResult.rgb, g_flWaterBlendFactor * flWaterBlendAlpha ), flFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
|
|
#if ( defined( _X360 ) )
|
|
{
|
|
vOutput.rgb += ScreenSpaceOrderedDither( i.vScreenPos );
|
|
vOutput.rgb = LinearToGamma( vOutput.rgb ); // Simulate the sRGB write here since FinalOutupt() above skips this call with TONEMAP_SCALE_NONE
|
|
}
|
|
#endif
|
|
|
|
return vOutput;
|
|
}
|