mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-22 22:27:05 +00:00
202 lines
9.1 KiB
Plaintext
202 lines
9.1 KiB
Plaintext
//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
|
|
//
|
|
// Purpose:
|
|
//
|
|
//=============================================================================
|
|
|
|
// STATIC: "CONVERT_TO_SRGB" "0..1" [ps20b][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC]
|
|
// STATIC: "CONVERT_TO_SRGB" "0..1" [ps30][= g_pHardwareConfig->NeedsShaderSRGBConversion()] [PC]
|
|
// STATIC: "CONVERT_TO_SRGB" "0..0" [= 0] [XBOX]
|
|
// STATIC: "LIGHTWARPTEXTURE" "0..1"
|
|
// DYNAMIC: "PIXELFOGTYPE" "0..1"
|
|
// DYNAMIC: "WRITEWATERFOGTODESTALPHA" "0..1"
|
|
// DYNAMIC: "NUM_LIGHTS" "0..2" [ps20]
|
|
// DYNAMIC: "NUM_LIGHTS" "0..4" [ps20b]
|
|
// DYNAMIC: "NUM_LIGHTS" "0..4" [ps30]
|
|
|
|
|
|
#include "shader_constant_register_map.h"
|
|
|
|
sampler BaseSampler : register( s0 ); // Base map
|
|
sampler DiffuseWarpSampler : register( s1 ); // 1D texture for diffuse lighting modification
|
|
sampler RefractSampler : register( s2 ); // Refraction map copied from back buffer
|
|
sampler NormalSampler : register( s3 ); // Normal map
|
|
sampler SpecExponentSampler : register( s4 ); // Flashlight cookie
|
|
sampler NormalizeSampler : register( s5 ); // Normalization cube map
|
|
|
|
const float3x3 g_ViewProj : register( c0 ); // 1st row of Projection matrix
|
|
// c1 // 2nd row
|
|
// c2 // 4th row
|
|
const float2 g_CloakControl : register( c3 ); // { refract amount, cloak, ?, ? }
|
|
const float3 cAmbientCube[6] : register( PSREG_AMBIENT_CUBE );
|
|
const float4 g_EyePos_SpecExponent : register( PSREG_EYEPOS_SPEC_EXPONENT );
|
|
const float4 g_FogParams : register( PSREG_FOG_PARAMS );
|
|
const float4 g_FlashlightAttenuationFactors_RimMask : register( PSREG_FLASHLIGHT_ATTENUATION ); // On non-flashlight pass, x has rim mask control
|
|
const float4 g_RimBoost : register( PSREG_FLASHLIGHT_POSITION_RIM_BOOST );
|
|
const float4 g_FresnelSpecParams : register( PSREG_FRESNEL_SPEC_PARAMS ); // xyz are fresnel, w is specular boost
|
|
const float4 g_SpecularRimParams : register( PSREG_SPEC_RIM_PARAMS ); // xyz are specular tint color, w is rim power
|
|
PixelShaderLightInfo cLightInfo[3] : register( PSREG_LIGHT_INFO_ARRAY ); // 2 registers each - 6 registers total
|
|
|
|
#define g_fRimBoost g_RimBoost.w
|
|
#define g_FresnelRanges g_FresnelSpecParams.xyz
|
|
#define g_SpecularBoost g_FresnelSpecParams.w
|
|
#define g_SpecularTint g_SpecularRimParams.xyz
|
|
#define g_RimExponent g_SpecularRimParams.w
|
|
#define g_FlashlightAttenuationFactors g_FlashlightAttenuationFactors_RimMask
|
|
#define g_RimMaskControl g_FlashlightAttenuationFactors_RimMask.x
|
|
|
|
// 8 2D Poisson offsets (designed to use .xy and .wz swizzles (not .zw)
|
|
static const float4 gPoissonOffset[4] = { float4 (-0.0876f, 0.9703f, 0.5651f, 0.4802f ),
|
|
float4 ( 0.1851f, 0.1580f, -0.0617f, -0.2616f ),
|
|
float4 (-0.5477f, -0.6603f, 0.0711f, -0.5325f ),
|
|
float4 (-0.0751f, -0.8954f, 0.4054f, 0.6384f ) };
|
|
|
|
struct PS_INPUT
|
|
{
|
|
float2 vBaseTexCoord : TEXCOORD0;
|
|
float3x3 tangentSpaceTranspose : TEXCOORD1;
|
|
// second row : TEXCOORD2;
|
|
// third row : TEXCOORD3;
|
|
float3 worldPos : TEXCOORD4;
|
|
float3 projPos : TEXCOORD5;
|
|
float4 lightAtten : TEXCOORD6;
|
|
};
|
|
|
|
float4 main( PS_INPUT i ) : COLOR
|
|
{
|
|
float3 vSpecular = float3( 0.0f, 0.0f, 0.0f );
|
|
bool bDoDiffuseWarp = LIGHTWARPTEXTURE ? true : false;
|
|
int nNumLights = NUM_LIGHTS;
|
|
|
|
// Base color
|
|
float4 albedo = tex2D( BaseSampler, i.vBaseTexCoord );
|
|
|
|
// Load normal and expand range
|
|
float4 vNormalSample = tex2D( NormalSampler, i.vBaseTexCoord );
|
|
float3 tangentSpaceNormal = 2.0f * vNormalSample.xyz - 1.0f;
|
|
|
|
// We need a world space normal if we're doing any lighting
|
|
float3 vWorldNormal = normalize( mul( i.tangentSpaceTranspose, tangentSpaceNormal ) );
|
|
float3 vWorldEyeDir = normalize( g_EyePos_SpecExponent.xyz - i.worldPos );
|
|
|
|
// Vanilla 1-(N.V) fresnel term used later in transition lerp
|
|
float fresnel = 1-saturate( dot( vWorldNormal, vWorldEyeDir ) );
|
|
|
|
// Summation of diffuse illumination from all local lights
|
|
float3 diffuseLighting = PixelShaderDoLighting( i.worldPos, vWorldNormal,
|
|
float3( 0.0f, 0.0f, 0.0f ), false,
|
|
true, i.lightAtten, cAmbientCube, NormalizeSampler,
|
|
nNumLights, cLightInfo, true, false, 1.0f,
|
|
bDoDiffuseWarp, DiffuseWarpSampler );
|
|
|
|
|
|
|
|
|
|
|
|
// Transform world space normal into clip space and project
|
|
float2 vProjNormal;
|
|
vProjNormal.x = dot( vWorldNormal, g_ViewProj[0] ); // 1st row
|
|
vProjNormal.y = dot( vWorldNormal, g_ViewProj[1] ); // 2nd row
|
|
|
|
// Compute coordinates for sampling refraction
|
|
float2 vRefractTexCoordNoWarp = i.projPos.xy / i.projPos.z;
|
|
float2 vRefractTexCoord = vProjNormal.xy;
|
|
float scale = lerp( g_CloakControl.x, 0.0f, g_CloakControl.y );
|
|
vRefractTexCoord *= scale;
|
|
vRefractTexCoord += vRefractTexCoordNoWarp;
|
|
|
|
#ifdef SHADER_MODEL_PS_2_0
|
|
float3 vRefract = tex2D( RefractSampler, vRefractTexCoordNoWarp );
|
|
#endif
|
|
|
|
// Extra refraction rays, specular, rim etc are only done on ps_2_b
|
|
#if defined( SHADER_MODEL_PS_2_B ) || defined( SHADER_MODEL_PS_3_0 )
|
|
// Blur by scalable Poisson filter
|
|
float fBlurAmount = lerp( 0.05f, 0.0f, g_CloakControl.y );
|
|
float3 vRefract = tex2D( RefractSampler, vRefractTexCoord );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[0].xy * fBlurAmount );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[0].wz * fBlurAmount );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[1].xy * fBlurAmount );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[1].wz * fBlurAmount );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[2].xy * fBlurAmount );
|
|
vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[2].wz * fBlurAmount );
|
|
|
|
// We're right at the hairy edge of constant register usage and hence have to drop these taps...
|
|
// vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[3].xy * fBlurAmount );
|
|
// vRefract += tex2D( RefractSampler, vRefractTexCoord + gPoissonOffset[3].wz * fBlurAmount );
|
|
vRefract /= 7.0f;
|
|
|
|
float3 rimLighting = float3( 0.0f, 0.0f, 0.0f );
|
|
float3 specularLighting = float3( 0.0f, 0.0f, 0.0f );
|
|
float fSpecExp = g_EyePos_SpecExponent.w;
|
|
float fSpecMask = vNormalSample.a;
|
|
|
|
float4 vSpecExpMap = tex2D( SpecExponentSampler, i.vBaseTexCoord );
|
|
float fSpecExpMap = vSpecExpMap.r;
|
|
float fRimMask = 1.0f;//lerp( 1.0f, vSpecExpMap.a, g_RimMaskControl ); // Select rim mask
|
|
float3 vSpecularTint;
|
|
|
|
// If the exponent passed in as a constant is zero, use the value from the map as the exponent
|
|
if ( fSpecExp == 0 )
|
|
fSpecExp = 1.0f - fSpecExpMap + 150.0f * fSpecExpMap;
|
|
|
|
// If constant tint is negative, tint with albedo, based upon scalar tint map
|
|
if ( g_SpecularTint.r == -1 )
|
|
vSpecularTint = lerp( float3(1.0f, 1.0f, 1.0f), albedo, vSpecExpMap.g );
|
|
else
|
|
vSpecularTint = g_SpecularTint.rgb;
|
|
|
|
// Fresnel to match regular specular lighting
|
|
float fFresnelRanges = Fresnel( vWorldNormal, vWorldEyeDir, g_FresnelRanges );
|
|
|
|
// Summation of specular from all local lights besides the flashlight
|
|
PixelShaderDoSpecularLighting( i.worldPos, vWorldNormal, fSpecExp, vWorldEyeDir,
|
|
i.lightAtten, nNumLights, cLightInfo, false, 1.0f, false,
|
|
NormalizeSampler, 1.0f, true, g_RimExponent,
|
|
|
|
// Outputs
|
|
specularLighting, rimLighting );
|
|
|
|
// Modulate with spec mask, boost, tint and fresnel ranges
|
|
specularLighting *= fSpecMask * g_SpecularBoost * fFresnelRanges * vSpecularTint;
|
|
|
|
float fRimFresnel = Fresnel4( vWorldNormal, vWorldEyeDir );
|
|
|
|
// Add in rim light modulated with tint, mask and traditional Fresnel (not using Fresnel ranges)
|
|
rimLighting *= vSpecularTint * fRimMask * fRimFresnel;
|
|
|
|
// Fold rim lighting into specular term by using the max so that we don't really add light twice...
|
|
specularLighting = max (specularLighting, rimLighting);
|
|
|
|
// Add in view-ray lookup from ambient cube
|
|
specularLighting += fRimFresnel * fRimMask * vSpecularTint /* g_fRimBoost */ * PixelShaderAmbientLight( vWorldEyeDir, cAmbientCube) * saturate(dot(vWorldNormal, float3(0, 0 , 1)) );
|
|
|
|
float tintLerpFactor = saturate(lerp( 1, fresnel-1.1, saturate(g_CloakControl.y)));
|
|
tintLerpFactor = smoothstep( 0.4f, 0.425f, tintLerpFactor );
|
|
float3 vTintedRefract = lerp( vRefract, albedo * vRefract, 0.7f );
|
|
vRefract = lerp( vRefract, vTintedRefract, tintLerpFactor );
|
|
|
|
vSpecular = specularLighting * smoothstep( 0.98, 0.8, saturate(g_CloakControl.y ));
|
|
#endif
|
|
|
|
// Blend refraction component with diffusely lit model
|
|
float diffuseLerpFactor = saturate(lerp( 1, fresnel - 1.35, saturate(g_CloakControl.y)));
|
|
diffuseLerpFactor = smoothstep( 0.4f, 0.425f, diffuseLerpFactor );
|
|
|
|
float3 fDiffuse = lerp( vRefract, albedo * diffuseLighting, diffuseLerpFactor );
|
|
float3 result = fDiffuse + vSpecular;
|
|
|
|
float alpha = 1.0f;
|
|
|
|
// Emulate LinearColorToHDROutput() when uncloaked
|
|
result = lerp( result.xyz * LINEAR_LIGHT_SCALE, result, saturate(g_CloakControl.y) );
|
|
|
|
float fogFactor = CalcPixelFogFactor( PIXELFOGTYPE, g_FogParams, g_EyePos_SpecExponent.z, i.worldPos.z, i.projPos.z );
|
|
|
|
#if WRITEWATERFOGTODESTALPHA && (PIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT)
|
|
alpha = fogFactor;
|
|
#endif
|
|
|
|
return FinalOutput( float4( result, alpha ), fogFactor, PIXELFOGTYPE, TONEMAP_SCALE_NONE );
|
|
}
|