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.
372 lines
12 KiB
372 lines
12 KiB
//===================== Copyright (c) Valve Corporation. All Rights Reserved. ======================
|
|
|
|
// STATIC: "MAGNIFY" "0..1"
|
|
// STATIC: "BLUR" "0..1"
|
|
// STATIC: "FADEOUTONSILHOUETTE" "0..1"
|
|
// STATIC: "CUBEMAP" "0..1"
|
|
// STATIC: "REFRACTTINTTEXTURE" "0..1"
|
|
// STATIC: "MASKED" "0..1"
|
|
// STATIC: "COLORMODULATE" "0..1"
|
|
// STATIC: "SECONDARY_NORMAL" "0..1"
|
|
// STATIC: "MIRRORABOUTVIEWPORTEDGES" "0..1" [CONSOLE]
|
|
// STATIC: "MIRRORABOUTVIEWPORTEDGES" "0..0" [PC]
|
|
// STATIC: "SHADER_SRGB_READ" "0..1" [ps20b]
|
|
// STATIC: "LOCALREFRACT" "0..1"
|
|
|
|
#include "common_fog_ps_fxc.h"
|
|
// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..1" [ps20b] [PC]
|
|
// DYNAMIC: "WRITE_DEPTH_TO_DESTALPHA" "0..0" [ps20b] [CONSOLE]
|
|
// DYNAMIC: "D_NVIDIA_STEREO" "0..1" [ps20b] [PC]
|
|
// DYNAMIC: "D_NVIDIA_STEREO" "0..0" [ps20b] [CONSOLE]
|
|
|
|
// SKIP: $MASKED && $BLUR
|
|
|
|
#if defined( SHADER_MODEL_PS_2_0 )
|
|
#define WRITE_DEPTH_TO_DESTALPHA 0
|
|
#endif
|
|
|
|
#ifdef _X360
|
|
#ifdef SHADER_SRGB_READ
|
|
#undef SHADER_SRGB_READ
|
|
#endif
|
|
#define SHADER_SRGB_READ 1
|
|
#endif
|
|
|
|
#include "common_ps_fxc.h"
|
|
#include "shader_constant_register_map.h"
|
|
|
|
sampler NormalSampler2 : register( s1 );
|
|
sampler RefractSampler : register( s2 );
|
|
sampler NormalSampler : register( s3 );
|
|
|
|
#if CUBEMAP
|
|
samplerCUBE EnvmapSampler : register( s4 );
|
|
#endif
|
|
|
|
#if REFRACTTINTTEXTURE
|
|
sampler RefractTintSampler : register( s5 );
|
|
#endif
|
|
|
|
#if D_NVIDIA_STEREO
|
|
sampler StereoParamSampler : register( s6 );
|
|
#endif
|
|
|
|
const float3 g_EnvmapTint : register( c0 );
|
|
const float3 g_RefractTint : register( c1 );
|
|
const float3 g_EnvmapContrast : register( c2 );
|
|
const float3 g_EnvmapSaturation : register( c3 );
|
|
const float4 g_NormalizedViewportMinXYMaxWZ : register( c4 );
|
|
const float4 g_c5 : register( c5 );
|
|
#define g_RefractScale g_c5.x
|
|
#define g_flTime g_c5.w
|
|
|
|
const float4 g_c6 : register( c6 );
|
|
#define g_vMagnifyCenter g_c6.xy
|
|
#define g_flInverseMagnifyScale g_c6.z
|
|
|
|
const float3 g_c7 : register( c7 );
|
|
#define g_vRefractTextureAspectFixup ( g_c7.xy )
|
|
#define g_flRefractDepth ( g_c7.z )
|
|
|
|
const float4 g_FogParams : register( PSREG_FOG_PARAMS );
|
|
const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT );
|
|
|
|
static const int g_BlurCount = BLUR;
|
|
static const float g_BlurFraction = 1.0f / 512.0f;
|
|
static const float g_HalfBlurFraction = 0.5f * g_BlurFraction;
|
|
|
|
struct PS_INPUT
|
|
{
|
|
float4 vBumpTexCoord : TEXCOORD0; // NormalMap1 in xy, NormalMap2 in wz
|
|
float3 vTangentVertToEyeVector : TEXCOORD1;
|
|
float3 vWorldNormal : TEXCOORD2;
|
|
float3 vWorldTangent : TEXCOORD3;
|
|
float3 vWorldBinormal : TEXCOORD4;
|
|
float3 vRefractXYW : TEXCOORD5;
|
|
float3 vWorldViewVector : TEXCOORD6;
|
|
#if COLORMODULATE
|
|
float4 ColorModulate : COLOR0;
|
|
#endif
|
|
|
|
float4 worldPos_projPosZ : TEXCOORD7; // Necessary for pixel fog
|
|
};
|
|
|
|
// NVIDIA's function to convert mono refract UV to the correct stereo UV for each eye
|
|
float2 MonoTostereoClipPosXY( float3 vMonoClipPos ) // .z is actually .w
|
|
{
|
|
#if ( !D_NVIDIA_STEREO )
|
|
{
|
|
return vMonoClipPos.xy;
|
|
}
|
|
#else
|
|
{
|
|
// 0th pixel = 1/16 == 1/16 + 1/8 * 0
|
|
float flEyeSep = tex2D( StereoParamSampler, float2( 0.0625f, 0 ) ).x; // 0.19 * 0.1316;
|
|
|
|
// 1st pixel = 3/16 == 1/16 + 1/8 * 1
|
|
float flConvergence = tex2D( StereoParamSampler, float2( 0.1875, 0 ) ).x; // 4;
|
|
float3 vStereoClipPos = vMonoClipPos.xyz;
|
|
|
|
// Undo the stereo transform
|
|
vStereoClipPos.x += flEyeSep * ( vMonoClipPos.z - flConvergence );
|
|
return vStereoClipPos.xy;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
float4_color_return_type main( PS_INPUT i ) : COLOR
|
|
{
|
|
// AlexV - Don't delete this line. I will remove it in a few days.
|
|
//i.vBumpTexCoord.x = ( i.vBumpTexCoord.x - 98.0/255.0 ) / ( 158.0/255.0-98.0/255.0 );
|
|
|
|
float3 vResult;
|
|
|
|
float flPixelFogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.xyz, i.worldPos_projPosZ.xyz, i.worldPos_projPosZ.w );
|
|
|
|
float flBlend = 1.0f;
|
|
#if ( FADEOUTONSILHOUETTE )
|
|
{
|
|
//flBlend = -i.projNormal.z;
|
|
flBlend = saturate( dot( -i.vWorldViewVector.xyz, i.vWorldNormal.xyz ) );
|
|
flBlend = flBlend * flBlend * flBlend;
|
|
}
|
|
#endif
|
|
|
|
// Sample normal
|
|
float4 vNormalTexel = tex2D( NormalSampler, i.vBumpTexCoord.xy );
|
|
float4 vNormalTs = float4( vNormalTexel.xyz * 2.0f - 1.0f, vNormalTexel.a );
|
|
|
|
#if ( SECONDARY_NORMAL )
|
|
{
|
|
float3 vNormal2Ts = tex2D( NormalSampler, i.vBumpTexCoord.wz ).xyz * 2.0f - 1.0f;
|
|
vNormalTs.xyz = normalize( vNormalTs.xyz + vNormal2Ts.xyz );
|
|
}
|
|
#endif
|
|
|
|
#if ( REFRACTTINTTEXTURE )
|
|
float3 vRefractTintColor = 2.0 * g_RefractTint * tex2D( RefractTintSampler, i.vBumpTexCoord.xy ).rgb;
|
|
#else
|
|
float3 vRefractTintColor = g_RefractTint;
|
|
#endif
|
|
|
|
#if ( COLORMODULATE )
|
|
{
|
|
vRefractTintColor.rgb *= i.ColorModulate.rgb;
|
|
}
|
|
#endif
|
|
|
|
// Compute coordinates for sampling refraction
|
|
float2 vRefractTexCoordNoWarp = MonoTostereoClipPosXY( i.vRefractXYW.xyz ) / i.vRefractXYW.z; // Divide by w
|
|
float2 vRefractTexCoord = vNormalTs.xy; // This normal should be in screen space!
|
|
float flScale = vNormalTs.a * g_RefractScale;
|
|
#if COLORMODULATE
|
|
{
|
|
flScale *= i.ColorModulate.a;
|
|
}
|
|
#endif
|
|
vRefractTexCoord.xy *= flScale;
|
|
|
|
#if ( MAGNIFY )
|
|
{
|
|
vRefractTexCoord.xy += float2( 0.5, 0.5 ) + float2( g_vMagnifyCenter.x, g_vMagnifyCenter.y );
|
|
vRefractTexCoord.xy += ( vRefractTexCoordNoWarp.xy - float2( 0.5, 0.5 ) - float2( g_vMagnifyCenter.x, g_vMagnifyCenter.y ) ) * g_flInverseMagnifyScale;
|
|
}
|
|
#else
|
|
{
|
|
vRefractTexCoord.xy += vRefractTexCoordNoWarp.xy;
|
|
}
|
|
#endif
|
|
|
|
#if ( MIRRORABOUTVIEWPORTEDGES )
|
|
{
|
|
//
|
|
// need to mirror the texcoords on every border so that one splitscreen viewport doesn't bleed into another one.
|
|
//
|
|
|
|
// mirror on the min viewport in both dimensions
|
|
vRefractTexCoord.xy -= g_NormalizedViewportMinXYMaxWZ.xy;
|
|
vRefractTexCoord.xy = abs( vRefractTexCoord.xy );
|
|
vRefractTexCoord.xy += g_NormalizedViewportMinXYMaxWZ.xy;
|
|
|
|
// mirror on the max viewport in both dimensions
|
|
vRefractTexCoord.xy = g_NormalizedViewportMinXYMaxWZ.wz - vRefractTexCoord.xy;
|
|
vRefractTexCoord.xy = abs( vRefractTexCoord.xy );
|
|
vRefractTexCoord.xy = g_NormalizedViewportMinXYMaxWZ.wz - vRefractTexCoord.xy;
|
|
}
|
|
#endif
|
|
|
|
#if ( BLUR == 0 )
|
|
{
|
|
#if ( MASKED )
|
|
{
|
|
float4 vMaskedResult = tex2Dsrgb( RefractSampler, vRefractTexCoord.xy );
|
|
return FinalOutput( vMaskedResult, flPixelFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
}
|
|
#else
|
|
{
|
|
float3 vColorWarp = tex2Dsrgb( RefractSampler, vRefractTexCoord.xy ).rgb;
|
|
float3 vColorNoWarp = tex2Dsrgb( RefractSampler, vRefractTexCoordNoWarp.xy ).rgb;
|
|
|
|
vColorWarp.rgb *= vRefractTintColor.rgb;
|
|
vResult.rgb = lerp( vColorNoWarp.rgb, vColorWarp.rgb, flBlend );
|
|
}
|
|
#endif
|
|
}
|
|
#elif ( BLUR == 1 ) // use polyphase magic to convert 9 lookups into 4
|
|
{
|
|
// basic principle behind this transformation:
|
|
// [ A B C ]
|
|
// [ D E F ]
|
|
// [ G H I ]
|
|
// use bilinear filtering hardware to weight upper 2x2 samples evenly (0.25* [A + B + D + E]).
|
|
// scale the upper 2x2 by 4/9 (total area of kernel occupied)
|
|
// use bilinear filtering hardware to weight right 1x2 samples evenly (0.5*[C + F])
|
|
// scale right 1x2 by 2/9
|
|
// use bilinear filtering hardware to weight lower 2x1 samples evenly (0.5*[G + H])
|
|
// scale bottom 2x1 by 2/9
|
|
// fetch last sample (I) and scale by 1/9.
|
|
|
|
float2 upper_2x2_loc = vRefractTexCoord.xy - float2( g_HalfBlurFraction, g_HalfBlurFraction );
|
|
float2 right_1x2_loc = vRefractTexCoord.xy + float2( g_BlurFraction, -g_HalfBlurFraction );
|
|
float2 lower_2x1_loc = vRefractTexCoord.xy + float2( -g_HalfBlurFraction, g_BlurFraction );
|
|
float2 singleton_loc = vRefractTexCoord.xy + float2( g_BlurFraction, g_BlurFraction );
|
|
vResult.rgb = tex2D( RefractSampler, upper_2x2_loc ).rgb * 0.4444444;
|
|
vResult.rgb += tex2D( RefractSampler, right_1x2_loc ).rgb * 0.2222222;
|
|
vResult.rgb += tex2D( RefractSampler, lower_2x1_loc ).rgb * 0.2222222;
|
|
vResult.rgb += tex2D( RefractSampler, singleton_loc ).rgb * 0.1111111;
|
|
|
|
#if ( SHADER_SRGB_READ )
|
|
{
|
|
// Just do this once rather than after every blur step, which is wrong, but much more efficient
|
|
if ( IsX360() )
|
|
{
|
|
#if defined( CSTRIKE15 )
|
|
// [mariod] - RT's are all 2.2 gamma in CSTRIKE15
|
|
vResult.rgb = GammaToLinear( vResult.rgb );
|
|
#else
|
|
vResult.rgb = X360GammaToLinear( vResult.rgb );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
vResult.rgb = SrgbGammaToLinear( vResult.rgb );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
float3 vUnblurredColor = tex2Dsrgb( RefractSampler, vRefractTexCoordNoWarp.xy ).rgb;
|
|
vResult.rgb = lerp( vUnblurredColor.rgb, vResult.rgb * vRefractTintColor.rgb, flBlend );
|
|
}
|
|
#elif ( BLUR > 1 ) // iteratively step through render target
|
|
{
|
|
int x, y;
|
|
|
|
vResult.rgb = float3( 0.0f, 0.0f, 0.0f );
|
|
for ( x = -g_BlurCount; x <= g_BlurCount; x++ )
|
|
{
|
|
for ( y = -g_BlurCount; y <= g_BlurCount; y++ )
|
|
{
|
|
vResult.rgb += tex2D( RefractSampler, vRefractTexCoord.xy + float2( g_BlurFraction * x, g_BlurFraction * y ) ).rgb;
|
|
}
|
|
}
|
|
|
|
#if ( SHADER_SRGB_READ )
|
|
{
|
|
// Just do this once rather than after every blur step, which is wrong, but much more efficient
|
|
if ( IsX360() )
|
|
{
|
|
#if defined( CSTRIKE15 )
|
|
// [mariod] - RT's are all 2.2 gamma in CSTRIKE15
|
|
vResult.rgb = GammaToLinear( vResult.rgb );
|
|
#else
|
|
vResult.rgb = X360GammaToLinear( vResult.rgb );
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
vResult.rgb = SrgbGammaToLinear( vResult.rgb );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
int nWidth = g_BlurCount * 2 + 1;
|
|
vResult.rgb *= 1.0f / ( nWidth * nWidth );
|
|
|
|
// vResult is the blurred one now. . .now lerp.
|
|
float3 vUnblurredColor = tex2Dsrgb( RefractSampler, vRefractTexCoordNoWarp.xy );
|
|
vResult.rgb = lerp( vUnblurredColor.rgb, vResult.rgb * vRefractTintColor.rgb, flBlend );
|
|
}
|
|
#endif
|
|
|
|
#if ( LOCALREFRACT )
|
|
{
|
|
float2 vTexCoord = i.vBumpTexCoord.xy;
|
|
|
|
// The interpolaged tangent space vert to eye vector isn't good enough, so compute a higher quality vector here
|
|
float3 vVertexToEyeDirWs = g_EyePos_SpecExponent.xyz - i.worldPos_projPosZ.xyz;
|
|
float3 vVertexToEyeDirTs = Vec3WorldToTangentNormalized( vVertexToEyeDirWs.xyz, i.vWorldNormal.xyz, i.vWorldTangent.xyz, i.vWorldBinormal.xyz );
|
|
|
|
//float3 vRefractTs = refract( -vVertexToEyeDirTs.xyz, vNormalTs.xyz, 0.66 );
|
|
float3 vRefractTs = vVertexToEyeDirTs.xyz; // Just use the vert to eye vector as the refract vector
|
|
|
|
float flRDotN = -vRefractTs.z; // This is R.GeometricNormal, so just use tangent z
|
|
|
|
float2 vRefractedUv = vRefractTs.xy / flRDotN;
|
|
vRefractedUv.xy += vNormalTs.xy;
|
|
vRefractedUv.xy += ( 1.0f - vNormalTs.z ) * vRefractTs.xy / flRDotN;
|
|
vRefractedUv.xy *= g_vRefractTextureAspectFixup.xy * g_flRefractDepth;
|
|
|
|
// Original uv's
|
|
vRefractedUv.xy += vTexCoord.xy;
|
|
|
|
float4 vRefract = tex2Dsrgb( RefractSampler, saturate( vRefractedUv.xy ) );
|
|
float4 vRefract2 = tex2Dsrgb( RefractSampler, saturate( vTexCoord.xy + vNormalTs.xy*0.1 ) );
|
|
vRefract.rgb = lerp( vRefract.rgb, vRefract2.aaa, 0.025 );
|
|
|
|
float flFresnel = pow( vNormalTs.z, 3.0f );
|
|
vResult.rgb = vRefract.rgb * flFresnel * vRefractTintColor.rgb;
|
|
|
|
float3 vUnblurredColor = tex2Dsrgb( RefractSampler, vTexCoord.xy ).rgb;
|
|
vResult.rgb = lerp( vUnblurredColor.rgb, vResult.rgb, flBlend );
|
|
}
|
|
#endif
|
|
|
|
#if ( CUBEMAP )
|
|
{
|
|
float3 vNormalWs = Vec3TangentToWorld( vNormalTs.xyz, i.vWorldNormal.xyz, i.vWorldTangent.xyz, i.vWorldBinormal.xyz );
|
|
|
|
float3 vReflectRayWs = CalcReflectionVectorUnnormalized( vNormalWs.xyz, i.vTangentVertToEyeVector.xyz );
|
|
float3 vSpecularLighting = ENV_MAP_SCALE * texCUBE( EnvmapSampler, vReflectRayWs.xyz ).rgb;
|
|
|
|
// Spec mask
|
|
float flSpecularMask = vNormalTs.a;
|
|
vSpecularLighting.rgb *= flSpecularMask;
|
|
|
|
// Tint
|
|
vSpecularLighting.rgb *= g_EnvmapTint.rgb;
|
|
|
|
// Contrast
|
|
float3 vSpecularLightingSquared = vSpecularLighting.rgb * vSpecularLighting.rgb;
|
|
vSpecularLighting.rgb = lerp( vSpecularLighting.rgb, vSpecularLightingSquared.rgb, g_EnvmapContrast );
|
|
|
|
// Saturation
|
|
float3 vSpecularLuminance = dot( vSpecularLighting.rgb, float3( 0.299f, 0.587f, 0.114f ) );
|
|
vSpecularLighting.rgb = lerp( vSpecularLuminance.rgb, vSpecularLighting.rgb, g_EnvmapSaturation );
|
|
|
|
// Fresnel
|
|
float flNdotV = saturate( dot( vNormalTs.xyz, i.vTangentVertToEyeVector.xyz ) );
|
|
float g_flReflectance = 0.6f;
|
|
float flFresnel = g_flReflectance + ( ( 1.0f - g_flReflectance ) * pow( 1.0f - flNdotV, 1.0f ) );
|
|
|
|
vResult.rgb += vSpecularLighting.rgb * flFresnel;
|
|
}
|
|
#endif
|
|
|
|
#if ( COLORMODULATE )
|
|
float flResultAlpha = i.ColorModulate.a * vNormalTs.a;
|
|
#else
|
|
float flResultAlpha = vNormalTs.a;
|
|
#endif
|
|
|
|
return FinalOutput( float4( vResult, flResultAlpha ), flPixelFogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE, ( WRITE_DEPTH_TO_DESTALPHA != 0 ), i.worldPos_projPosZ.w );
|
|
}
|