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.
423 lines
16 KiB
423 lines
16 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
#ifndef COMMON_VERTEXLITGENERIC_DX9_H_ |
|
#define COMMON_VERTEXLITGENERIC_DX9_H_ |
|
|
|
#include "common_ps_fxc.h" |
|
|
|
// We store four light colors and positions in an |
|
// array of three of these structures like so: |
|
// |
|
// x y z w |
|
// +------+------+------+------+ |
|
// | L0.rgb | | |
|
// +------+------+------+ | |
|
// | L0.pos | L3 | |
|
// +------+------+------+ rgb | |
|
// | L1.rgb | | |
|
// +------+------+------+------+ |
|
// | L1.pos | | |
|
// +------+------+------+ | |
|
// | L2.rgb | L3 | |
|
// +------+------+------+ pos | |
|
// | L2.pos | | |
|
// +------+------+------+------+ |
|
// |
|
struct PixelShaderLightInfo |
|
{ |
|
float4 color; |
|
float4 pos; |
|
}; |
|
|
|
#define cOverbright 2.0f |
|
#define cOOOverbright 0.5f |
|
|
|
#define LIGHTTYPE_NONE 0 |
|
#define LIGHTTYPE_SPOT 1 |
|
#define LIGHTTYPE_POINT 2 |
|
#define LIGHTTYPE_DIRECTIONAL 3 |
|
|
|
// Better suited to Pixel shader models, 11 instructions in pixel shader |
|
// ... actually, now only 9: mul, cmp, cmp, mul, mad, mad, mad, mad, mad |
|
float3 PixelShaderAmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] ) |
|
{ |
|
float3 linearColor, nSquared = worldNormal * worldNormal; |
|
float3 isNegative = ( worldNormal >= 0.0 ) ? 0 : nSquared; |
|
float3 isPositive = ( worldNormal >= 0.0 ) ? nSquared : 0; |
|
linearColor = isPositive.x * cAmbientCube[0] + isNegative.x * cAmbientCube[1] + |
|
isPositive.y * cAmbientCube[2] + isNegative.y * cAmbientCube[3] + |
|
isPositive.z * cAmbientCube[4] + isNegative.z * cAmbientCube[5]; |
|
return linearColor; |
|
} |
|
|
|
// Better suited to Vertex shader models |
|
// Six VS instructions due to use of constant indexing (slt, mova, mul, mul, mad, mad) |
|
float3 VertexShaderAmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] ) |
|
{ |
|
float3 nSquared = worldNormal * worldNormal; |
|
int3 isNegative = ( worldNormal < 0.0 ); |
|
float3 linearColor; |
|
linearColor = nSquared.x * cAmbientCube[isNegative.x] + |
|
nSquared.y * cAmbientCube[isNegative.y+2] + |
|
nSquared.z * cAmbientCube[isNegative.z+4]; |
|
return linearColor; |
|
} |
|
|
|
float3 AmbientLight( const float3 worldNormal, const float3 cAmbientCube[6] ) |
|
{ |
|
// Vertex shader cases |
|
#ifdef SHADER_MODEL_VS_1_0 |
|
return VertexShaderAmbientLight( worldNormal, cAmbientCube ); |
|
#elif SHADER_MODEL_VS_1_1 |
|
return VertexShaderAmbientLight( worldNormal, cAmbientCube ); |
|
#elif SHADER_MODEL_VS_2_0 |
|
return VertexShaderAmbientLight( worldNormal, cAmbientCube ); |
|
#elif SHADER_MODEL_VS_3_0 |
|
return VertexShaderAmbientLight( worldNormal, cAmbientCube ); |
|
#else |
|
// Pixel shader case |
|
return PixelShaderAmbientLight( worldNormal, cAmbientCube ); |
|
#endif |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Compute scalar diffuse term with various optional tweaks such as |
|
// Half Lambert and ambient occlusion |
|
//----------------------------------------------------------------------------- |
|
float3 DiffuseTerm(const bool bHalfLambert, const float3 worldNormal, const float3 lightDir, |
|
const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoLightingWarp, in sampler lightWarpSampler ) |
|
{ |
|
float fResult; |
|
|
|
float NDotL = dot( worldNormal, lightDir ); // Unsaturated dot (-1 to 1 range) |
|
|
|
if ( bHalfLambert ) |
|
{ |
|
fResult = saturate(NDotL * 0.5 + 0.5); // Scale and bias to 0 to 1 range |
|
|
|
if ( !bDoLightingWarp ) |
|
{ |
|
fResult *= fResult; // Square |
|
} |
|
} |
|
else |
|
{ |
|
fResult = saturate( NDotL ); // Saturate pure Lambertian term |
|
} |
|
|
|
if ( bDoAmbientOcclusion ) |
|
{ |
|
// Raise to higher powers for darker AO values |
|
// float fAOPower = lerp( 4.0f, 1.0f, fAmbientOcclusion ); |
|
// result *= pow( NDotL * 0.5 + 0.5, fAOPower ); |
|
fResult *= fAmbientOcclusion; |
|
} |
|
|
|
float3 fOut = float3( fResult, fResult, fResult ); |
|
if ( bDoLightingWarp ) |
|
{ |
|
fOut = 2.0f * tex1D( lightWarpSampler, fResult ); |
|
} |
|
|
|
return fOut; |
|
} |
|
|
|
float3 PixelShaderDoGeneralDiffuseLight( const float fAtten, const float3 worldPos, const float3 worldNormal, |
|
in sampler NormalizeSampler, |
|
const float3 vPosition, const float3 vColor, const bool bHalfLambert, |
|
const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoLightingWarp, in sampler lightWarpSampler ) |
|
{ |
|
#if (defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)) |
|
float3 lightDir = normalize( vPosition - worldPos ); |
|
#else |
|
float3 lightDir = NormalizeWithCubemap( NormalizeSampler, vPosition - worldPos ); |
|
#endif |
|
return vColor * fAtten * DiffuseTerm( bHalfLambert, worldNormal, lightDir, bDoAmbientOcclusion, fAmbientOcclusion, bDoLightingWarp, lightWarpSampler ); |
|
} |
|
|
|
float3 PixelShaderGetLightVector( const float3 worldPos, PixelShaderLightInfo cLightInfo[3], int nLightIndex ) |
|
{ |
|
if ( nLightIndex == 3 ) |
|
{ |
|
// Unpack light 3 from w components... |
|
float3 vLight3Pos = float3( cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w ); |
|
return normalize( vLight3Pos - worldPos ); |
|
} |
|
else |
|
{ |
|
return normalize( cLightInfo[nLightIndex].pos - worldPos ); |
|
} |
|
} |
|
|
|
float3 PixelShaderGetLightColor( PixelShaderLightInfo cLightInfo[3], int nLightIndex ) |
|
{ |
|
if ( nLightIndex == 3 ) |
|
{ |
|
// Unpack light 3 from w components... |
|
return float3( cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w ); |
|
} |
|
else |
|
{ |
|
return cLightInfo[nLightIndex].color.rgb; |
|
} |
|
} |
|
|
|
|
|
void SpecularAndRimTerms( const float3 vWorldNormal, const float3 vLightDir, const float fSpecularExponent, |
|
const float3 vEyeDir, const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoSpecularWarp, in sampler specularWarpSampler, const float fFresnel, |
|
const float3 color, const bool bDoRimLighting, const float fRimExponent, |
|
|
|
// Outputs |
|
out float3 specularLighting, out float3 rimLighting ) |
|
{ |
|
rimLighting = float3(0.0f, 0.0f, 0.0f); |
|
|
|
//float3 vReflect = reflect( -vEyeDir, vWorldNormal ); // Reflect view through normal |
|
float3 vReflect = 2 * vWorldNormal * dot( vWorldNormal , vEyeDir ) - vEyeDir; // Reflect view through normal |
|
float LdotR = saturate(dot( vReflect, vLightDir )); // L.R (use half-angle instead?) |
|
specularLighting = pow( LdotR, fSpecularExponent ); // Raise to specular exponent |
|
|
|
// Optionally warp as function of scalar specular and fresnel |
|
if ( bDoSpecularWarp ) |
|
specularLighting *= tex2D( specularWarpSampler, float2(specularLighting.x, fFresnel) ); // Sample at { (L.R)^k, fresnel } |
|
|
|
specularLighting *= saturate(dot( vWorldNormal, vLightDir )); // Mask with N.L |
|
specularLighting *= color; // Modulate with light color |
|
|
|
if ( bDoAmbientOcclusion ) // Optionally modulate with ambient occlusion |
|
specularLighting *= fAmbientOcclusion; |
|
|
|
if ( bDoRimLighting ) // Optionally do rim lighting |
|
{ |
|
rimLighting = pow( LdotR, fRimExponent ); // Raise to rim exponent |
|
rimLighting *= saturate(dot( vWorldNormal, vLightDir )); // Mask with N.L |
|
rimLighting *= color; // Modulate with light color |
|
} |
|
} |
|
|
|
// Traditional fresnel term approximation |
|
float Fresnel( const float3 vNormal, const float3 vEyeDir ) |
|
{ |
|
float fresnel = saturate( 1 - dot( vNormal, vEyeDir ) ); // 1-(N.V) for Fresnel term |
|
return fresnel * fresnel; // Square for a more subtle look |
|
} |
|
|
|
// Traditional fresnel term approximation which uses 4th power (square twice) |
|
float Fresnel4( const float3 vNormal, const float3 vEyeDir ) |
|
{ |
|
float fresnel = saturate( 1 - dot( vNormal, vEyeDir ) ); // 1-(N.V) for Fresnel term |
|
fresnel = fresnel * fresnel; // Square |
|
return fresnel * fresnel; // Square again for a more subtle look |
|
} |
|
|
|
|
|
// |
|
// Custom Fresnel with low, mid and high parameters defining a piecewise continuous function |
|
// with traditional fresnel (0 to 1 range) as input. The 0 to 0.5 range blends between |
|
// low and mid while the 0.5 to 1 range blends between mid and high |
|
// |
|
// | |
|
// | . M . . . H |
|
// | . |
|
// L |
|
// | |
|
// +---------------- |
|
// 0 1 |
|
// |
|
float Fresnel( const float3 vNormal, const float3 vEyeDir, float3 vRanges ) |
|
{ |
|
//float result, f = Fresnel( vNormal, vEyeDir ); // Traditional Fresnel |
|
//if ( f > 0.5f ) |
|
// result = lerp( vRanges.y, vRanges.z, (2*f)-1 ); // Blend between mid and high values |
|
//else |
|
// result = lerp( vRanges.x, vRanges.y, 2*f ); // Blend between low and mid values |
|
|
|
// note: vRanges is now encoded as ((mid-min)*2, mid, (max-mid)*2) to optimize math |
|
float f = saturate( 1 - dot( vNormal, vEyeDir ) ); |
|
f = f*f - 0.5; |
|
return vRanges.y + (f >= 0.0 ? vRanges.z : vRanges.x) * f; |
|
} |
|
|
|
void PixelShaderDoSpecularLight( const float3 vWorldPos, const float3 vWorldNormal, const float fSpecularExponent, const float3 vEyeDir, |
|
const float fAtten, const float3 vLightColor, const float3 vLightDir, |
|
const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoSpecularWarp, in sampler specularWarpSampler, float fFresnel, |
|
const bool bDoRimLighting, const float fRimExponent, |
|
|
|
// Outputs |
|
out float3 specularLighting, out float3 rimLighting ) |
|
{ |
|
// Compute Specular and rim terms |
|
SpecularAndRimTerms( vWorldNormal, vLightDir, fSpecularExponent, |
|
vEyeDir, bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoSpecularWarp, specularWarpSampler, fFresnel, vLightColor * fAtten, |
|
bDoRimLighting, fRimExponent, specularLighting, rimLighting ); |
|
} |
|
|
|
float3 PixelShaderDoLightingLinear( const float3 worldPos, const float3 worldNormal, |
|
const float3 staticLightingColor, const bool bStaticLight, |
|
const bool bAmbientLight, const float4 lightAtten, const float3 cAmbientCube[6], |
|
in sampler NormalizeSampler, const int nNumLights, PixelShaderLightInfo cLightInfo[3], |
|
const bool bHalfLambert, const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoLightingWarp, in sampler lightWarpSampler ) |
|
{ |
|
float3 linearColor = 0.0f; |
|
|
|
if ( bStaticLight ) |
|
{ |
|
// The static lighting comes in in gamma space and has also been premultiplied by $cOOOverbright |
|
// need to get it into |
|
// linear space so that we can do adds. |
|
linearColor += GammaToLinear( staticLightingColor * cOverbright ); |
|
} |
|
|
|
if ( bAmbientLight ) |
|
{ |
|
float3 ambient = AmbientLight( worldNormal, cAmbientCube ); |
|
|
|
if ( bDoAmbientOcclusion ) |
|
ambient *= fAmbientOcclusion * fAmbientOcclusion; // Note squaring... |
|
|
|
linearColor += ambient; |
|
} |
|
|
|
if ( nNumLights > 0 ) |
|
{ |
|
linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.x, worldPos, worldNormal, NormalizeSampler, |
|
cLightInfo[0].pos, cLightInfo[0].color, bHalfLambert, |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoLightingWarp, lightWarpSampler ); |
|
if ( nNumLights > 1 ) |
|
{ |
|
linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.y, worldPos, worldNormal, NormalizeSampler, |
|
cLightInfo[1].pos, cLightInfo[1].color, bHalfLambert, |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoLightingWarp, lightWarpSampler ); |
|
if ( nNumLights > 2 ) |
|
{ |
|
linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.z, worldPos, worldNormal, NormalizeSampler, |
|
cLightInfo[2].pos, cLightInfo[2].color, bHalfLambert, |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoLightingWarp, lightWarpSampler ); |
|
if ( nNumLights > 3 ) |
|
{ |
|
// Unpack the 4th light's data from tight constant packing |
|
float3 vLight3Color = float3( cLightInfo[0].color.w, cLightInfo[0].pos.w, cLightInfo[1].color.w ); |
|
float3 vLight3Pos = float3( cLightInfo[1].pos.w, cLightInfo[2].color.w, cLightInfo[2].pos.w ); |
|
linearColor += PixelShaderDoGeneralDiffuseLight( lightAtten.w, worldPos, worldNormal, NormalizeSampler, |
|
vLight3Pos, vLight3Color, bHalfLambert, |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoLightingWarp, lightWarpSampler ); |
|
} |
|
} |
|
} |
|
} |
|
|
|
return linearColor; |
|
} |
|
|
|
void PixelShaderDoSpecularLighting( const float3 worldPos, const float3 worldNormal, const float fSpecularExponent, const float3 vEyeDir, |
|
const float4 lightAtten, const int nNumLights, PixelShaderLightInfo cLightInfo[3], |
|
const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoSpecularWarp, in sampler specularWarpSampler, float fFresnel, |
|
const bool bDoRimLighting, const float fRimExponent, |
|
|
|
// Outputs |
|
out float3 specularLighting, out float3 rimLighting ) |
|
{ |
|
specularLighting = rimLighting = float3( 0.0f, 0.0f, 0.0f ); |
|
float3 localSpecularTerm, localRimTerm; |
|
|
|
if( nNumLights > 0 ) |
|
{ |
|
PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir, |
|
lightAtten.x, PixelShaderGetLightColor( cLightInfo, 0 ), |
|
PixelShaderGetLightVector( worldPos, cLightInfo, 0 ), |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoSpecularWarp, specularWarpSampler, fFresnel, |
|
bDoRimLighting, fRimExponent, |
|
localSpecularTerm, localRimTerm ); |
|
|
|
specularLighting += localSpecularTerm; // Accumulate specular and rim terms |
|
rimLighting += localRimTerm; |
|
} |
|
|
|
if( nNumLights > 1 ) |
|
{ |
|
PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir, |
|
lightAtten.y, PixelShaderGetLightColor( cLightInfo, 1 ), |
|
PixelShaderGetLightVector( worldPos, cLightInfo, 1 ), |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoSpecularWarp, specularWarpSampler, fFresnel, |
|
bDoRimLighting, fRimExponent, |
|
localSpecularTerm, localRimTerm ); |
|
|
|
specularLighting += localSpecularTerm; // Accumulate specular and rim terms |
|
rimLighting += localRimTerm; |
|
} |
|
|
|
|
|
if( nNumLights > 2 ) |
|
{ |
|
PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir, |
|
lightAtten.z, PixelShaderGetLightColor( cLightInfo, 2 ), |
|
PixelShaderGetLightVector( worldPos, cLightInfo, 2 ), |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoSpecularWarp, specularWarpSampler, fFresnel, |
|
bDoRimLighting, fRimExponent, |
|
localSpecularTerm, localRimTerm ); |
|
|
|
specularLighting += localSpecularTerm; // Accumulate specular and rim terms |
|
rimLighting += localRimTerm; |
|
} |
|
|
|
if( nNumLights > 3 ) |
|
{ |
|
PixelShaderDoSpecularLight( worldPos, worldNormal, fSpecularExponent, vEyeDir, |
|
lightAtten.w, PixelShaderGetLightColor( cLightInfo, 3 ), |
|
PixelShaderGetLightVector( worldPos, cLightInfo, 3 ), |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoSpecularWarp, specularWarpSampler, fFresnel, |
|
bDoRimLighting, fRimExponent, |
|
localSpecularTerm, localRimTerm ); |
|
|
|
specularLighting += localSpecularTerm; // Accumulate specular and rim terms |
|
rimLighting += localRimTerm; |
|
} |
|
|
|
} |
|
|
|
float3 PixelShaderDoRimLighting( const float3 worldNormal, const float3 vEyeDir, const float3 cAmbientCube[6], float fFresnel ) |
|
{ |
|
float3 vReflect = reflect( -vEyeDir, worldNormal ); // Reflect view through normal |
|
|
|
return fFresnel * PixelShaderAmbientLight( vEyeDir, cAmbientCube ); |
|
} |
|
|
|
// Called directly by newer shaders or through the following wrapper for older shaders |
|
float3 PixelShaderDoLighting( const float3 worldPos, const float3 worldNormal, |
|
const float3 staticLightingColor, const bool bStaticLight, |
|
const bool bAmbientLight, const float4 lightAtten, const float3 cAmbientCube[6], |
|
in sampler NormalizeSampler, const int nNumLights, PixelShaderLightInfo cLightInfo[3], |
|
const bool bHalfLambert, |
|
|
|
// New optional/experimental parameters |
|
const bool bDoAmbientOcclusion, const float fAmbientOcclusion, |
|
const bool bDoLightingWarp, in sampler lightWarpSampler ) |
|
{ |
|
float3 linearColor = PixelShaderDoLightingLinear( worldPos, worldNormal, staticLightingColor, |
|
bStaticLight, bAmbientLight, lightAtten, |
|
cAmbientCube, NormalizeSampler, nNumLights, cLightInfo, bHalfLambert, |
|
bDoAmbientOcclusion, fAmbientOcclusion, |
|
bDoLightingWarp, lightWarpSampler ); |
|
|
|
// go ahead and clamp to the linear space equivalent of overbright 2 so that we match |
|
// everything else. |
|
// linearColor = HuePreservingColorClamp( linearColor, pow( 2.0f, 2.2 ) ); |
|
|
|
return linearColor; |
|
} |
|
|
|
#endif //#ifndef COMMON_VERTEXLITGENERIC_DX9_H_
|
|
|