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.
1000 lines
32 KiB
1000 lines
32 KiB
//========== Copyright (c) Valve Corporation, All rights reserved. ==========//
|
|
//
|
|
// Purpose: Common pixel shader code
|
|
//
|
|
// $NoKeywords: $
|
|
//
|
|
//===========================================================================//
|
|
|
|
#ifndef COMMON_PS_FXC_H_
|
|
#define COMMON_PS_FXC_H_
|
|
|
|
#include "common_fxc.h"
|
|
|
|
// Put global skip commands here. . make sure and check that the appropriate vars are defined
|
|
// so these aren't used on the wrong shaders!
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// We don't ever write water fog to dest alpha if we aren't doing water fog.
|
|
// SKIP: defined $PIXELFOGTYPE && defined $WRITEWATERFOGTODESTALPHA && ( $PIXELFOGTYPE != 1 ) && $WRITEWATERFOGTODESTALPHA
|
|
// --------------------------------------------------------------------------------
|
|
// Ditch all fastpath attempts if we are doing LIGHTING_PREVIEW.
|
|
// SKIP: defined $LIGHTING_PREVIEW && defined $FASTPATHENVMAPTINT && $LIGHTING_PREVIEW && $FASTPATHENVMAPTINT
|
|
// SKIP: defined $LIGHTING_PREVIEW && defined $FASTPATHENVMAPCONTRAST && $LIGHTING_PREVIEW && $FASTPATHENVMAPCONTRAST
|
|
// SKIP: defined $LIGHTING_PREVIEW && defined $FASTPATH && $LIGHTING_PREVIEW && $FASTPATH
|
|
// --------------------------------------------------------------------------------
|
|
// Ditch flashlight depth when flashlight is disabled
|
|
// SKIP: ($FLASHLIGHT || $FLASHLIGHTSHADOWS) && $LIGHTING_PREVIEW
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// PS3 sRGB writes require a half4 return value
|
|
#if defined( _PS3 )
|
|
#define float4_color_return_type half4
|
|
#else // _PS3
|
|
#define float4_color_return_type float4
|
|
#endif // !_PS3
|
|
|
|
// System defined pixel shader constants
|
|
|
|
#if defined( _X360 )
|
|
const bool g_bHighQualityShadows : register( b0 );
|
|
#endif
|
|
|
|
// NOTE: w == 1.0f / (Dest alpha compressed depth range).
|
|
const float4 g_LinearFogColor : register( c29 );
|
|
#define OO_DESTALPHA_DEPTH_RANGE (g_LinearFogColor.w)
|
|
|
|
// Linear and gamma light scale values
|
|
const float4 cLightScale : register( c30 );
|
|
#define LINEAR_LIGHT_SCALE (cLightScale.x)
|
|
#define LIGHT_MAP_SCALE (cLightScale.y)
|
|
#define ENV_MAP_SCALE (cLightScale.z)
|
|
#define GAMMA_LIGHT_SCALE (cLightScale.w)
|
|
|
|
// Flashlight constants
|
|
#if defined(SHADER_MODEL_PS_2_0) || defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)
|
|
const float4 cFlashlightColor : register( c28 );
|
|
#define flFlashlightNoLambertValue cFlashlightColor.w // This is either 0.0 or 2.0
|
|
|
|
const float4 cFlashlightScreenScale : register( c31 );
|
|
#define flFlashlightShadowBufferOneOverWidth cFlashlightScreenScale.z // 1.0f / ShadowBufferWidth
|
|
#define flFlashlightShadowBufferTwoOverWidth cFlashlightScreenScale.w // 2.0f / ShadowBufferWidth
|
|
#endif
|
|
|
|
// 3.0 standard constants
|
|
#if defined( SHADER_MODEL_PS_3_0 )
|
|
const float4 cScreenSize : register( c32 ); // Used for converting VPOS to useful 2D coordinates
|
|
#endif
|
|
|
|
const float4 g_vDepthFeatherProjToViewZW[2] : register( c13 );
|
|
const float4 g_vDepthFeatherViewportMad : register( c15 );
|
|
|
|
#define HDR_INPUT_MAP_SCALE 16.0f
|
|
|
|
#define TONEMAP_SCALE_NONE 0
|
|
#define TONEMAP_SCALE_LINEAR 1
|
|
#define TONEMAP_SCALE_GAMMA 2
|
|
|
|
#define PIXEL_FOG_TYPE_NONE -1 //MATERIAL_FOG_NONE is handled by PIXEL_FOG_TYPE_RANGE, this is for explicitly disabling fog in the shader
|
|
#define PIXEL_FOG_TYPE_RANGE 0 //range+none packed together in ps2b. Simply none in ps20 (instruction limits)
|
|
#define PIXEL_FOG_TYPE_HEIGHT 1
|
|
|
|
// If you change these, make the corresponding change in hardwareconfig.cpp
|
|
#define NVIDIA_PCF 0
|
|
#define ATI_NO_PCF_FETCH4 1
|
|
#define NVIDIA_PCF_CHEAP 2
|
|
#define ATI_NOPCF 3
|
|
|
|
#define GAMECONSOLE_NINE_TAP_PCF 0
|
|
#define GAMECONSOLE_SINGLE_TAP_PCF 1
|
|
|
|
struct LPREVIEW_PS_OUT
|
|
{
|
|
float4 color : COLOR0;
|
|
float4 normal : COLOR1;
|
|
float4 position : COLOR2;
|
|
float4 flags : COLOR3;
|
|
};
|
|
|
|
/*
|
|
// unused
|
|
float Luminance( float3 color )
|
|
{
|
|
return dot( color, float3( 0.30f, 0.59f, 0.11f ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
float LuminanceScaled( float3 color )
|
|
{
|
|
return dot( color, float3( 0.30f / MAX_HDR_OVERBRIGHT, 0.59f / MAX_HDR_OVERBRIGHT, 0.11f / MAX_HDR_OVERBRIGHT ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
float AvgColor( float3 color )
|
|
{
|
|
return dot( color, float3( 0.33333f, 0.33333f, 0.33333f ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
half4 DiffuseBump( sampler lightmapSampler,
|
|
float2 lightmapTexCoord1,
|
|
float2 lightmapTexCoord2,
|
|
float2 lightmapTexCoord3,
|
|
float3 normal )
|
|
{
|
|
float3 lightmapColor1 = tex2D( lightmapSampler, lightmapTexCoord1 );
|
|
float3 lightmapColor2 = tex2D( lightmapSampler, lightmapTexCoord2 );
|
|
float3 lightmapColor3 = tex2D( lightmapSampler, lightmapTexCoord3 );
|
|
|
|
float3 diffuseLighting;
|
|
diffuseLighting = saturate( dot( normal, bumpBasis[0] ) ) * lightmapColor1 +
|
|
saturate( dot( normal, bumpBasis[1] ) ) * lightmapColor2 +
|
|
saturate( dot( normal, bumpBasis[2] ) ) * lightmapColor3;
|
|
|
|
return float4( diffuseLighting, LuminanceScaled( diffuseLighting ) );
|
|
}
|
|
*/
|
|
|
|
|
|
/*
|
|
// unused
|
|
float Fresnel( float3 normal,
|
|
float3 eye,
|
|
float2 scaleBias )
|
|
{
|
|
float fresnel = 1.0f - dot( normal, eye );
|
|
fresnel = pow( fresnel, 5.0f );
|
|
|
|
return fresnel * scaleBias.x + scaleBias.y;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
half4 GetNormal( sampler normalSampler,
|
|
float2 normalTexCoord )
|
|
{
|
|
float4 normal = tex2D( normalSampler, normalTexCoord );
|
|
normal.rgb = 2.0f * normal.rgb - 1.0f;
|
|
|
|
return normal;
|
|
}
|
|
*/
|
|
|
|
// Needs to match NormalDecodeMode_t enum in imaterialsystem.h
|
|
#define NORM_DECODE_NONE 0
|
|
#define NORM_DECODE_ATI2N 1
|
|
#define NORM_DECODE_ATI2N_ALPHA 2
|
|
|
|
float4 DecompressNormal( sampler NormalSampler, float2 tc, int nDecompressionMode, sampler AlphaSampler )
|
|
{
|
|
float4 normalTexel = tex2D( NormalSampler, tc );
|
|
float4 result;
|
|
|
|
if ( nDecompressionMode == NORM_DECODE_NONE )
|
|
{
|
|
result = float4(normalTexel.xyz * 2.0f - 1.0f, normalTexel.a );
|
|
}
|
|
else if ( nDecompressionMode == NORM_DECODE_ATI2N )
|
|
{
|
|
result.xy = normalTexel.xy * 2.0f - 1.0f;
|
|
result.z = sqrt( 1.0f - dot(result.xy, result.xy) );
|
|
result.a = 1.0f;
|
|
}
|
|
else // ATI2N plus ATI1N for alpha
|
|
{
|
|
result.xy = normalTexel.xy * 2.0f - 1.0f;
|
|
result.z = sqrt( 1.0f - dot(result.xy, result.xy) );
|
|
result.a = tex2D( AlphaSampler, tc ).x; // Note that this comes in on the X channel
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
float4 DecompressNormal( sampler NormalSampler, float2 tc, int nDecompressionMode )
|
|
{
|
|
return DecompressNormal( NormalSampler, tc, nDecompressionMode, NormalSampler );
|
|
}
|
|
|
|
|
|
float3 NormalizeWithCubemap( samplerCUBE normalizeSampler, float3 input )
|
|
{
|
|
// return texCUBE( normalizeSampler, input ) * 2.0f - 1.0f;
|
|
return texCUBE( normalizeSampler, input ).xyz ;
|
|
}
|
|
|
|
/*
|
|
half4 EnvReflect( sampler envmapSampler,
|
|
sampler normalizeSampler,
|
|
float3 normal,
|
|
float3 eye,
|
|
float2 fresnelScaleBias )
|
|
{
|
|
float3 normEye = NormalizeWithCubemap( normalizeSampler, eye );
|
|
float fresnel = Fresnel( normal, normEye, fresnelScaleBias );
|
|
float3 reflect = CalcReflectionVectorUnnormalized( normal, eye );
|
|
return texCUBE( envmapSampler, reflect );
|
|
}
|
|
*/
|
|
|
|
#if defined( SHADER_MODEL_PS_3_0 )
|
|
// cScreenSize.xy contains { 1.0/w, 1.0/h }
|
|
// cScreenSize.zw contains { 0.5/w, 0.5/h }
|
|
float2 ComputeScreenPos( float2 vPos )
|
|
{
|
|
return vPos * cScreenSize.xy + cScreenSize.zw;
|
|
}
|
|
#endif
|
|
|
|
|
|
// Vectorized smoothstep for doing three smoothsteps at once. Used by uberlight
|
|
float3 smoothstep3( float3 edge0, float3 edge1, float3 OneOverWidth, float3 x )
|
|
{
|
|
x = saturate((x - edge0) * OneOverWidth); // Scale, bias and saturate x to the range of zero to one
|
|
return x*x*(3-2*x); // Evaluate polynomial
|
|
}
|
|
|
|
|
|
float CalcWaterFogAlpha( const float flWaterZ, const float flEyePosZ, const float flWorldPosZ, const float flProjPosZ, const float flFogOORange )
|
|
{
|
|
#if 0
|
|
// This version is what you use if you want a line-integral throught he water for water fog.
|
|
// float flDepthFromWater = flWaterZ - flWorldPosZ + 2.0f; // hackity hack . .this is for the DF_FUDGE_UP in view_scene.cpp
|
|
float flDepthFromWater = flWaterZ - flWorldPosZ;
|
|
|
|
// if flDepthFromWater < 0, then set it to 0
|
|
// This is the equivalent of moving the vert to the water surface if it's above the water surface
|
|
// We'll do this with the saturate at the end instead.
|
|
// flDepthFromWater = max( 0.0f, flDepthFromWater );
|
|
|
|
// Calculate the ratio of water fog to regular fog (ie. how much of the distance from the viewer
|
|
// to the vert is actually underwater.
|
|
float flDepthFromEye = flEyePosZ - flWorldPosZ;
|
|
float f = (flDepthFromWater / flDepthFromEye) * flProjPosZ;
|
|
|
|
// $tmp.w is now the distance that we see through water.
|
|
return saturate( f * flFogOORange );
|
|
#else
|
|
// This version is simply using the depth of the water to determine fog factor,
|
|
// which is cheaper than doing the line integral and also fixes some problems with having
|
|
// a hard line on the shore when the water surface is viewed tangentially.
|
|
// hackity hack . .the 2.0 is for the DF_FUDGE_UP in view_scene.cpp
|
|
return saturate( ( flWaterZ - flWorldPosZ - 2.0f ) * flFogOORange );
|
|
#endif
|
|
}
|
|
|
|
HALF CalcPixelFogFactor( int iPIXELFOGTYPE, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ )
|
|
{
|
|
float retVal;
|
|
if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE )
|
|
{
|
|
retVal = 0.0f;
|
|
}
|
|
if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) //range fog, or no fog depending on fog parameters
|
|
{
|
|
// This is one only path that we go down for L4D.
|
|
float flFogMaxDensity = fogParams.z;
|
|
float flFogEndOverRange = fogParams.x;
|
|
float flFogOORange = fogParams.w;
|
|
retVal = CalcRangeFogFactorNonFixedFunction( vWorldPos, vEyePos, flFogMaxDensity, flFogEndOverRange, flFogOORange );
|
|
}
|
|
else if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT ) //height fog
|
|
{
|
|
retVal = CalcWaterFogAlpha( fogParams.y, vEyePos.z, vWorldPos.z, flProjPosZ, fogParams.w );
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
HALF CalcPixelFogFactorSupportsVertexFog( int iPIXELFOGTYPE, const float4 fogParams, const float3 vEyePos, const float3 vWorldPos, const float flProjPosZ, const HALF flVertexFogFactor )
|
|
{
|
|
#if ( DOPIXELFOG )
|
|
{
|
|
return CalcPixelFogFactor( iPIXELFOGTYPE, fogParams, vEyePos, vWorldPos, flProjPosZ );
|
|
}
|
|
#else
|
|
{
|
|
return flVertexFogFactor;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//g_FogParams not defined by default, but this is the same layout for every shader that does define it
|
|
#define g_FogEndOverRange g_FogParams.x
|
|
#define g_WaterZ g_FogParams.y
|
|
#define g_FogMaxDensity g_FogParams.z
|
|
#define g_FogOORange g_FogParams.w
|
|
float3 BlendPixelFog( const float3 vShaderColor, float pixelFogFactor, const float3 vFogColor, const int iPIXELFOGTYPE )
|
|
{
|
|
if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) //either range fog or no fog depending on fog parameters and whether this is ps20 or ps2b
|
|
{
|
|
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) //Minimum requirement of ps2b
|
|
return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog
|
|
#else
|
|
return vShaderColor;
|
|
#endif
|
|
}
|
|
else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT )
|
|
{
|
|
return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor );
|
|
}
|
|
else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE )
|
|
{
|
|
return vShaderColor;
|
|
}
|
|
}
|
|
|
|
HALF3 BlendPixelFogHalf( const HALF3 vShaderColor, HALF pixelFogFactor, const HALF3 vFogColor, const int iPIXELFOGTYPE )
|
|
{
|
|
if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE ) //either range fog or no fog depending on fog parameters and whether this is ps20 or ps2b
|
|
{
|
|
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) //Minimum requirement of ps2b
|
|
return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor * pixelFogFactor ); //squaring the factor will get the middle range mixing closer to hardware fog
|
|
#else
|
|
return vShaderColor;
|
|
#endif
|
|
}
|
|
else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT )
|
|
{
|
|
return lerp( vShaderColor.rgb, vFogColor.rgb, pixelFogFactor );
|
|
}
|
|
else if( iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE )
|
|
{
|
|
return vShaderColor;
|
|
}
|
|
}
|
|
|
|
|
|
float SoftParticleDepth( float flDepth )
|
|
{
|
|
return flDepth * OO_DESTALPHA_DEPTH_RANGE;
|
|
}
|
|
|
|
|
|
float DepthToDestAlpha( const float flProjZ )
|
|
{
|
|
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) //Minimum requirement of ps2b
|
|
return SoftParticleDepth( flProjZ );
|
|
#else
|
|
return 1.0f;
|
|
#endif
|
|
}
|
|
|
|
float4_color_return_type FinalOutput( const float4 vShaderColor, float pixelFogFactor, const int iPIXELFOGTYPE, const int iTONEMAP_SCALE_TYPE, const bool bWriteDepthToDestAlpha = false, const float flProjZ = 1.0f )
|
|
{
|
|
float4 result;
|
|
if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR )
|
|
{
|
|
result.rgb = vShaderColor.rgb * LINEAR_LIGHT_SCALE;
|
|
}
|
|
else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA )
|
|
{
|
|
result.rgb = vShaderColor.rgb * GAMMA_LIGHT_SCALE;
|
|
}
|
|
else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_NONE )
|
|
{
|
|
result.rgb = vShaderColor.rgb;
|
|
}
|
|
|
|
if( bWriteDepthToDestAlpha )
|
|
result.a = DepthToDestAlpha( flProjZ );
|
|
else
|
|
result.a = vShaderColor.a;
|
|
|
|
if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE )
|
|
{
|
|
result.rgb = BlendPixelFog( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, iPIXELFOGTYPE );
|
|
}
|
|
|
|
#if defined(_X360) && defined( CSTRIKE15 )
|
|
// [mariod] - is this the only path? (defintely not TONEMAP_SCALE_GAMMA)...ensure aligned with what the shaders are doing, add combo (mimic srgb write render state) if necessary and mem exists...
|
|
if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR )
|
|
{
|
|
result.rgb = LinearToGamma( result.rgb );
|
|
//result.rgb = SrgbLinearToGamma( result.rgb );
|
|
}
|
|
#endif
|
|
|
|
|
|
return ( float4_color_return_type )result;
|
|
}
|
|
|
|
|
|
float4_color_return_type FinalOutputHalf( const HALF4 vShaderColor, float pixelFogFactor, const int iPIXELFOGTYPE, const int iTONEMAP_SCALE_TYPE, const bool bWriteDepthToDestAlpha = false, const float flProjZ = 1.0f )
|
|
{
|
|
HALF4 result;
|
|
if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR )
|
|
{
|
|
result.rgb = vShaderColor.rgb * (HALF)LINEAR_LIGHT_SCALE;
|
|
}
|
|
else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_GAMMA )
|
|
{
|
|
result.rgb = vShaderColor.rgb * (HALF)GAMMA_LIGHT_SCALE;
|
|
}
|
|
else if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_NONE )
|
|
{
|
|
result.rgb = vShaderColor.rgb;
|
|
}
|
|
|
|
if( bWriteDepthToDestAlpha )
|
|
result.a = DepthToDestAlpha( flProjZ );
|
|
else
|
|
result.a = vShaderColor.a;
|
|
|
|
if ( iPIXELFOGTYPE == PIXEL_FOG_TYPE_RANGE )
|
|
{
|
|
result.rgb = BlendPixelFogHalf( result.rgb, pixelFogFactor, g_LinearFogColor.rgb, iPIXELFOGTYPE );
|
|
}
|
|
|
|
#if defined(_X360) && defined( CSTRIKE15 )
|
|
// [mariod] - should we send TONEMAP_SCALE_NONE down this path too?
|
|
if( iTONEMAP_SCALE_TYPE == TONEMAP_SCALE_LINEAR )
|
|
{
|
|
result.rgb = LinearToGamma( result.rgb );
|
|
//result.rgb = SrgbLinearToGamma( result.rgb );
|
|
}
|
|
#endif
|
|
|
|
return ( float4_color_return_type )result;
|
|
}
|
|
|
|
LPREVIEW_PS_OUT FinalOutput( const LPREVIEW_PS_OUT vShaderColor, float pixelFogFactor, const int iPIXELFOGTYPE, const int iTONEMAP_SCALE_TYPE )
|
|
{
|
|
LPREVIEW_PS_OUT result;
|
|
result.color = FinalOutput( vShaderColor.color, pixelFogFactor, iPIXELFOGTYPE, iTONEMAP_SCALE_TYPE );
|
|
result.normal.rgb = vShaderColor.normal.rgb;
|
|
result.normal.a = vShaderColor.normal.a;
|
|
|
|
result.position.rgb = vShaderColor.position.rgb;
|
|
result.position.a = vShaderColor.position.a;
|
|
|
|
result.flags.rgb = vShaderColor.flags.rgb;
|
|
result.flags.a = vShaderColor.flags.a;
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
float RemapValClamped( float val, float A, float B, float C, float D)
|
|
{
|
|
float cVal = (val - A) / (B - A);
|
|
cVal = saturate( cVal );
|
|
|
|
return C + (D - C) * cVal;
|
|
}
|
|
|
|
|
|
//===================================================================================//
|
|
// This is based on Natasha Tatarchuk's Parallax Occlusion Mapping (ATI)
|
|
//===================================================================================//
|
|
// INPUT:
|
|
// inTexCoord:
|
|
// the texcoord for the height/displacement map before parallaxing
|
|
//
|
|
// vParallax:
|
|
// Compute initial parallax displacement direction:
|
|
// float2 vParallaxDirection = normalize( vViewTS.xy );
|
|
// float fLength = length( vViewTS );
|
|
// float fParallaxLength = sqrt( fLength * fLength - vViewTS.z * vViewTS.z ) / vViewTS.z;
|
|
// Out.vParallax = vParallaxDirection * fParallaxLength * fProjectedBumpHeight;
|
|
//
|
|
// vNormal:
|
|
// tangent space normal
|
|
//
|
|
// vViewW:
|
|
// float3 vViewW = /*normalize*/(mul( matViewInverse, float4( 0, 0, 0, 1)) - inPosition );
|
|
//
|
|
// OUTPUT:
|
|
// the new texcoord after parallaxing
|
|
float2 CalcParallaxedTexCoord( float2 inTexCoord, float2 vParallax, float3 vNormal,
|
|
float3 vViewW, sampler HeightMapSampler )
|
|
{
|
|
const int nMinSamples = 8;
|
|
const int nMaxSamples = 50;
|
|
|
|
// Normalize the incoming view vector to avoid artifacts:
|
|
// vView = normalize( vView );
|
|
vViewW = normalize( vViewW );
|
|
// vLight = normalize( vLight );
|
|
|
|
// Change the number of samples per ray depending on the viewing angle
|
|
// for the surface. Oblique angles require smaller step sizes to achieve
|
|
// more accurate precision
|
|
int nNumSteps = (int) lerp( nMaxSamples, nMinSamples, dot( vViewW, vNormal ) );
|
|
|
|
float4 cResultColor = float4( 0, 0, 0, 1 );
|
|
|
|
//===============================================//
|
|
// Parallax occlusion mapping offset computation //
|
|
//===============================================//
|
|
float fCurrHeight = 0.0;
|
|
float fStepSize = 1.0 / (float) nNumSteps;
|
|
float fPrevHeight = 1.0;
|
|
float fNextHeight = 0.0;
|
|
|
|
int nStepIndex = 0;
|
|
// bool bCondition = true;
|
|
|
|
float2 dx = ddx( inTexCoord );
|
|
float2 dy = ddy( inTexCoord );
|
|
|
|
float2 vTexOffsetPerStep = fStepSize * vParallax;
|
|
|
|
float2 vTexCurrentOffset = inTexCoord;
|
|
float fCurrentBound = 1.0;
|
|
|
|
float x = 0;
|
|
float y = 0;
|
|
float xh = 0;
|
|
float yh = 0;
|
|
|
|
float2 texOffset2 = 0;
|
|
|
|
bool bCondition = true;
|
|
while ( bCondition == true && nStepIndex < nNumSteps )
|
|
{
|
|
vTexCurrentOffset -= vTexOffsetPerStep;
|
|
|
|
fCurrHeight = tex2Dgrad( HeightMapSampler, vTexCurrentOffset, dx, dy ).r;
|
|
|
|
fCurrentBound -= fStepSize;
|
|
|
|
if ( fCurrHeight > fCurrentBound )
|
|
{
|
|
x = fCurrentBound;
|
|
y = fCurrentBound + fStepSize;
|
|
xh = fCurrHeight;
|
|
yh = fPrevHeight;
|
|
|
|
texOffset2 = vTexCurrentOffset - vTexOffsetPerStep;
|
|
|
|
bCondition = false;
|
|
}
|
|
else
|
|
{
|
|
nStepIndex++;
|
|
fPrevHeight = fCurrHeight;
|
|
}
|
|
|
|
} // End of while ( bCondition == true && nStepIndex > -1 )#else
|
|
|
|
fCurrentBound -= fStepSize;
|
|
|
|
float fParallaxAmount;
|
|
float numerator = (x * (y - yh) - y * (x - xh));
|
|
float denomenator = ((y - yh) - (x - xh));
|
|
// avoid NaN generation
|
|
if( ( numerator == 0.0f ) && ( denomenator == 0.0f ) )
|
|
{
|
|
fParallaxAmount = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
fParallaxAmount = numerator / denomenator;
|
|
}
|
|
|
|
float2 vParallaxOffset = vParallax * (1 - fParallaxAmount );
|
|
|
|
// Sample the height at the next possible step:
|
|
fNextHeight = tex2Dgrad( HeightMapSampler, texOffset2, dx, dy ).r;
|
|
|
|
// Original offset:
|
|
float2 texSampleBase = inTexCoord - vParallaxOffset;
|
|
|
|
return texSampleBase;
|
|
|
|
#if 0
|
|
cResultColor.rgb = ComputeDiffuseColor( texSampleBase, vLight );
|
|
|
|
float fBound = 1.0 - fStepSize * nStepIndex;
|
|
if ( fNextHeight < fCurrentBound )
|
|
// if( 0 )
|
|
{
|
|
//void DoIteration( in float2 vParallaxJittered, in float3 vLight, inout float4 cResultColor )
|
|
//cResultColor.rgb = float3(1,0,0);
|
|
DoIteration( vParallax + vPixelSize, vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
|
|
DoIteration( vParallax - vPixelSize, vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
|
|
DoIteration( vParallax + float2( -vPixelSize.x, vPixelSize.y ), vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
|
|
DoIteration( vParallax + float2( vPixelSize.x, -vPixelSize.y ), vLight, fStepSize, inTexCoord, nStepIndex, dx, dy, fBound, cResultColor );
|
|
|
|
cResultColor.rgb /= 5;
|
|
// cResultColor.rgb = float3( 1.0f, 0.0f, 0.0f );
|
|
} // End of if ( fNextHeight < fCurrentBound )
|
|
|
|
#if DOSHADOWS
|
|
{
|
|
//============================================//
|
|
// Soft shadow and self-occlusion computation //
|
|
//============================================//
|
|
// Compute the blurry shadows (note that this computation takes into
|
|
// account self-occlusion for shadow computation):
|
|
float sh0 = tex2D( sNormalMap, texSampleBase).w;
|
|
float shA = (tex2D( sNormalMap, texSampleBase + inXY * 0.88 ).w - sh0 - 0.88 ) * 1 * fShadowSoftening;
|
|
float sh9 = (tex2D( sNormalMap, texSampleBase + inXY * 0.77 ).w - sh0 - 0.77 ) * 2 * fShadowSoftening;
|
|
float sh8 = (tex2D( sNormalMap, texSampleBase + inXY * 0.66 ).w - sh0 - 0.66 ) * 4 * fShadowSoftening;
|
|
float sh7 = (tex2D( sNormalMap, texSampleBase + inXY * 0.55 ).w - sh0 - 0.55 ) * 6 * fShadowSoftening;
|
|
float sh6 = (tex2D( sNormalMap, texSampleBase + inXY * 0.44 ).w - sh0 - 0.44 ) * 8 * fShadowSoftening;
|
|
float sh5 = (tex2D( sNormalMap, texSampleBase + inXY * 0.33 ).w - sh0 - 0.33 ) * 10 * fShadowSoftening;
|
|
float sh4 = (tex2D( sNormalMap, texSampleBase + inXY * 0.22 ).w - sh0 - 0.22 ) * 12 * fShadowSoftening;
|
|
|
|
// Compute the actual shadow strength:
|
|
float fShadow = 1 - max( max( max( max( max( max( shA, sh9 ), sh8 ), sh7 ), sh6 ), sh5 ), sh4 );
|
|
|
|
cResultColor.rgb *= fShadow * 0.6 + 0.4;
|
|
}
|
|
#endif
|
|
|
|
return cResultColor;
|
|
#endif
|
|
}
|
|
|
|
|
|
//======================================//
|
|
// HSL Color space conversion routines //
|
|
//======================================//
|
|
|
|
#define HUE 0
|
|
#define SATURATION 1
|
|
#define LIGHTNESS 2
|
|
|
|
// Convert from RGB to HSL color space
|
|
float4 RGBtoHSL( float4 inColor )
|
|
{
|
|
float h, s;
|
|
float flMax = max( inColor.r, max( inColor.g, inColor.b ) );
|
|
float flMin = min( inColor.r, min( inColor.g, inColor.b ) );
|
|
|
|
float l = (flMax + flMin) / 2.0f;
|
|
|
|
if (flMax == flMin) // achromatic case
|
|
{
|
|
s = h = 0;
|
|
}
|
|
else // chromatic case
|
|
{
|
|
// Next, calculate the hue
|
|
float delta = flMax - flMin;
|
|
|
|
// First, calculate the saturation
|
|
if (l < 0.5f) // If we're in the lower hexcone
|
|
{
|
|
s = delta/(flMax + flMin);
|
|
}
|
|
else
|
|
{
|
|
s = delta/(2 - flMax - flMin);
|
|
}
|
|
|
|
if ( inColor.r == flMax )
|
|
{
|
|
h = (inColor.g - inColor.b)/delta; // color between yellow and magenta
|
|
}
|
|
else if ( inColor.g == flMax )
|
|
{
|
|
h = 2 + (inColor.b - inColor.r)/delta; // color between cyan and yellow
|
|
}
|
|
else // blue must be max
|
|
{
|
|
h = 4 + (inColor.r - inColor.g)/delta; // color between magenta and cyan
|
|
}
|
|
|
|
h *= 60.0f;
|
|
|
|
if (h < 0.0f)
|
|
{
|
|
h += 360.0f;
|
|
}
|
|
|
|
h /= 360.0f;
|
|
}
|
|
|
|
return float4 (h, s, l, 1.0f);
|
|
}
|
|
|
|
float HueToRGB( float v1, float v2, float vH )
|
|
{
|
|
float fResult = v1;
|
|
|
|
vH = fmod (vH + 1.0f, 1.0f);
|
|
|
|
if ( ( 6.0f * vH ) < 1.0f )
|
|
{
|
|
fResult = ( v1 + ( v2 - v1 ) * 6.0f * vH );
|
|
}
|
|
else if ( ( 2.0f * vH ) < 1.0f )
|
|
{
|
|
fResult = ( v2 );
|
|
}
|
|
else if ( ( 3.0f * vH ) < 2.0f )
|
|
{
|
|
fResult = ( v1 + ( v2 - v1 ) * ( ( 2.0f / 3.0f ) - vH ) * 6.0f );
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
// Convert from HSL to RGB color space
|
|
float4 HSLtoRGB( float4 hsl )
|
|
{
|
|
float r, g, b;
|
|
float h = hsl[HUE];
|
|
float s = hsl[SATURATION];
|
|
float l = hsl[LIGHTNESS];
|
|
|
|
if ( s == 0 )
|
|
{
|
|
r = g = b = l;
|
|
}
|
|
else
|
|
{
|
|
float v1, v2;
|
|
|
|
if ( l < 0.5f )
|
|
v2 = l * ( 1.0f + s );
|
|
else
|
|
v2 = ( l + s ) - ( s * l );
|
|
|
|
v1 = 2 * l - v2;
|
|
|
|
r = HueToRGB( v1, v2, h + ( 1.0f / 3.0f ) );
|
|
g = HueToRGB( v1, v2, h );
|
|
b = HueToRGB( v1, v2, h - ( 1.0f / 3.0f ) );
|
|
}
|
|
|
|
return float4( r, g, b, 1.0f );
|
|
}
|
|
|
|
|
|
// texture combining modes for combining base and detail/basetexture2
|
|
#define TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2 0 // original mode
|
|
#define TCOMBINE_RGB_ADDITIVE 1 // base.rgb+detail.rgb*fblend
|
|
#define TCOMBINE_DETAIL_OVER_BASE 2
|
|
#define TCOMBINE_FADE 3 // straight fade between base and detail.
|
|
#define TCOMBINE_BASE_OVER_DETAIL 4 // use base alpha for blend over detail
|
|
#define TCOMBINE_RGB_ADDITIVE_SELFILLUM 5 // add detail color post lighting
|
|
#define TCOMBINE_RGB_ADDITIVE_SELFILLUM_THRESHOLD_FADE 6
|
|
#define TCOMBINE_MOD2X_SELECT_TWO_PATTERNS 7 // use alpha channel of base to select between mod2x channels in r+a of detail
|
|
#define TCOMBINE_MULTIPLY 8
|
|
#define TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA 9 // use alpha channel of detail to mask base
|
|
#define TCOMBINE_SSBUMP_BUMP 10 // use detail to modulate lighting as an ssbump
|
|
#define TCOMBINE_SSBUMP_NOBUMP 11 // detail is an ssbump but use it as an albedo. shader does the magic here - no user needs to specify mode 11
|
|
#define TCOMBINE_NONE 12 // there is no detail texture
|
|
|
|
HALF4 TextureCombine( HALF4 baseColor, HALF4 detailColor, int combine_mode,
|
|
HALF fBlendFactor )
|
|
{
|
|
if ( combine_mode == TCOMBINE_MOD2X_SELECT_TWO_PATTERNS)
|
|
{
|
|
HALF3 dc=lerp(detailColor.r,detailColor.a, baseColor.a);
|
|
baseColor.rgb*=lerp(HALF3(1,1,1),2.0h*dc,fBlendFactor);
|
|
}
|
|
if ( combine_mode == TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2)
|
|
baseColor.rgb*=lerp(HALF3(1,1,1),2.0h*detailColor.rgb,fBlendFactor);
|
|
if ( combine_mode == TCOMBINE_RGB_ADDITIVE )
|
|
baseColor.rgb += fBlendFactor * detailColor.rgb;
|
|
if ( combine_mode == TCOMBINE_DETAIL_OVER_BASE )
|
|
{
|
|
HALF fblend=fBlendFactor * detailColor.a;
|
|
baseColor.rgb = lerp( baseColor.rgb, detailColor.rgb, fblend);
|
|
}
|
|
if ( combine_mode == TCOMBINE_FADE )
|
|
{
|
|
baseColor = lerp( baseColor, detailColor, fBlendFactor);
|
|
}
|
|
if ( combine_mode == TCOMBINE_BASE_OVER_DETAIL )
|
|
{
|
|
HALF fblend=fBlendFactor * (1-baseColor.a);
|
|
baseColor.rgb = lerp( baseColor.rgb, detailColor.rgb, fblend );
|
|
baseColor.a = detailColor.a;
|
|
}
|
|
if ( combine_mode == TCOMBINE_MULTIPLY )
|
|
{
|
|
baseColor = lerp( baseColor, baseColor*detailColor, fBlendFactor);
|
|
}
|
|
|
|
if (combine_mode == TCOMBINE_MASK_BASE_BY_DETAIL_ALPHA )
|
|
{
|
|
baseColor.a = lerp( baseColor.a, baseColor.a*detailColor.a, fBlendFactor );
|
|
}
|
|
if ( combine_mode == TCOMBINE_SSBUMP_NOBUMP )
|
|
{
|
|
baseColor.rgb = baseColor.rgb * dot( detailColor.rgb, 2.0h/3.0h );
|
|
}
|
|
return baseColor;
|
|
}
|
|
|
|
float3 lerp5(float3 f1, float3 f2, float i1, float i2, float x)
|
|
{
|
|
return f1+(f2-f1)*(x-i1)/(i2-i1);
|
|
}
|
|
|
|
HALF3 TextureCombinePostLighting( HALF3 lit_baseColor, HALF4 detailColor, int combine_mode,
|
|
HALF fBlendFactor )
|
|
{
|
|
if ( combine_mode == TCOMBINE_RGB_ADDITIVE_SELFILLUM )
|
|
lit_baseColor += fBlendFactor * detailColor.rgb;
|
|
if ( combine_mode == TCOMBINE_RGB_ADDITIVE_SELFILLUM_THRESHOLD_FADE )
|
|
{
|
|
// fade in an unusual way - instead of fading out color, remap an increasing band of it from
|
|
// 0..1
|
|
if ( fBlendFactor > 0.5h)
|
|
lit_baseColor += min(1, (1.0h/fBlendFactor)*max(0, detailColor.rgb-(1-fBlendFactor) ) );
|
|
else
|
|
lit_baseColor += 2*fBlendFactor*2*max(0, detailColor.rgb-.5h);
|
|
}
|
|
return lit_baseColor;
|
|
}
|
|
|
|
#if ( defined( _X360 ) || defined ( _PS3 ) )
|
|
float SampleHardwareDepth( sampler DepthSampler, float2 vDepthSampleCoords )
|
|
{
|
|
float flSceneProjZ = 0.0f;
|
|
|
|
#if ( defined( _PS3 ) )
|
|
{
|
|
float3 vSceneDepth = tex2D( DepthSampler, vDepthSampleCoords ).xyz;
|
|
// There's a slightly faster, but less precise way to recover Z here if we need it - see the Cgc docs.
|
|
vSceneDepth = round( vSceneDepth.xyz * 255.0f );
|
|
float3 vDepthFactorPrecise = float3( 65536.0/16777215.0, 256.0/16777215.0, 1.0/16777215.0 );
|
|
flSceneProjZ = dot( vSceneDepth.xyz, vDepthFactorPrecise );
|
|
}
|
|
#elif ( defined( _X360 ) )
|
|
{
|
|
float4 vSampledDepths;
|
|
asm
|
|
{
|
|
tfetch2D vSampledDepths.x___, vDepthSampleCoords, DepthSampler, OffsetX=0.5, OffsetY=0.5, MinFilter=point, MagFilter=point, MipFilter=point
|
|
};
|
|
flSceneProjZ = vSampledDepths.x;
|
|
#if ( defined( REVERSE_DEPTH_ON_X360 ) )
|
|
{
|
|
flSceneProjZ = 1.0f - flSceneProjZ;
|
|
}
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
return flSceneProjZ;
|
|
}
|
|
#endif
|
|
|
|
HALF DepthFeathering( sampler DepthSampler, const float4 vProjPos, float4 vDepthBlendConstants )
|
|
{
|
|
# if ( !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || defined(SHADER_MODEL_PS_2_0)) ) //minimum requirement of ps2b
|
|
{
|
|
float2 vProjPosDivW = vProjPos.xy / vProjPos.w;
|
|
float2 vScreenPos = vProjPosDivW * g_vDepthFeatherViewportMad.xy + g_vDepthFeatherViewportMad.zw;
|
|
|
|
float flFeatheredAlpha;
|
|
|
|
# if ( defined( _X360 ) || defined ( _PS3 ) )
|
|
{
|
|
// This code can handle oblique projection matrices used on the PS3. The depth feathering factor is a function computed in viewspace Z.
|
|
// Sample the scene's depth at the current fragment.
|
|
float flSceneProjZ = SampleHardwareDepth( DepthSampler, vScreenPos );
|
|
|
|
float4 vSceneProjPos = float4( vProjPosDivW.x, vProjPosDivW.y, flSceneProjZ, 1.0f );
|
|
|
|
float flSceneViewZ = dot( vSceneProjPos, g_vDepthFeatherProjToViewZW[0] );
|
|
float flSceneViewW = dot( vSceneProjPos, g_vDepthFeatherProjToViewZW[1] );
|
|
flSceneViewZ /= flSceneViewW;
|
|
|
|
// Computes the fragment's viewspace Z from its projection space coord.
|
|
// We could iterate the fragment's viewspace Z to save these 2 dots and a rcp, but this would require an extra iterator
|
|
// and modifications to all the vertex shaders to compute viewspace coords (which was tricky enough that this approach seems
|
|
// like the best compromise of devtime+testing vs. perf).
|
|
float flSurfViewZ = dot( vProjPos, g_vDepthFeatherProjToViewZW[0] );
|
|
float flSurfViewW = dot( vProjPos, g_vDepthFeatherProjToViewZW[1] );
|
|
flSurfViewZ /= flSurfViewW;
|
|
|
|
flFeatheredAlpha = flSurfViewZ - flSceneViewZ;
|
|
flFeatheredAlpha = saturate( saturate( vDepthBlendConstants.z * flFeatheredAlpha ) + vDepthBlendConstants.w );
|
|
}
|
|
# else
|
|
{
|
|
float flSceneDepth = tex2D( DepthSampler, vScreenPos ).a; // PC uses dest alpha of the frame buffer
|
|
float flSpriteDepth = SoftParticleDepth( vProjPos.z );
|
|
|
|
flFeatheredAlpha = abs(flSceneDepth - flSpriteDepth) * vDepthBlendConstants.x;
|
|
flFeatheredAlpha = max( smoothstep( 0.75f, 1.0f, flSceneDepth ), flFeatheredAlpha ); //as the sprite approaches the edge of our compressed depth space, the math stops working. So as the sprite approaches the far depth, smoothly remove feathering.
|
|
flFeatheredAlpha = saturate( flFeatheredAlpha );
|
|
}
|
|
# endif
|
|
|
|
return flFeatheredAlpha;
|
|
}
|
|
# else
|
|
{
|
|
return 1.0h;
|
|
}
|
|
# endif
|
|
}
|
|
|
|
HALF ComputeCameraFade( float4 vProjPos, float flNearPlane = 7.0f )
|
|
{
|
|
#if ( defined( _X360 ) || defined ( _PS3 ) )
|
|
// Compute viewspace Z and W, just like depth feathering (which is currently only supported on the consoles,
|
|
// which is why I'm only computing the factor in viewspace on the consoles as well).
|
|
float flSurfViewZ = dot( vProjPos, g_vDepthFeatherProjToViewZW[0] );
|
|
float flSurfViewW = dot( vProjPos, g_vDepthFeatherProjToViewZW[1] );
|
|
// Project to W=1.
|
|
flSurfViewZ /= flSurfViewW;
|
|
// Compute fade factor from viewspace Z.
|
|
float flFadeFactorScale = .06f;
|
|
flSurfViewZ = saturate( ( -flSurfViewZ - flNearPlane ) * flFadeFactorScale );
|
|
return flSurfViewZ * flSurfViewZ;
|
|
#else
|
|
return smoothstep( 0.0f, 1.0f, saturate( vProjPos.z * 0.025f ) );
|
|
#endif
|
|
}
|
|
|
|
#define ORDERED_DITHER_MAGNITUDE .008f
|
|
|
|
float3 ScreenSpaceOrderedDither( float2 vScreenPos )
|
|
{
|
|
if (0)
|
|
{
|
|
// No dithering.
|
|
return float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
else if (1)
|
|
{
|
|
// Iestyn's RGB dither (3 extra instructions).
|
|
float3 vDither = dot( float2(171, 231), vScreenPos );
|
|
vDither = frac( vDither / float3( 103, 71, 97 ) ) - 0.5f;
|
|
return ( vDither / 255 ) * .175f;
|
|
}
|
|
else if (0)
|
|
{
|
|
// 2x2 ordered dither.
|
|
vScreenPos = frac( vScreenPos * .5f ) * 2.0f;
|
|
return ( ( vScreenPos.y * 3.0f + ( vScreenPos.y * 2.0f - 1.0f ) * vScreenPos.x * -2.0f ) - 2.0f ) * ( ORDERED_DITHER_MAGNITUDE / 4.0f );
|
|
}
|
|
else
|
|
{
|
|
// 2x2 ordered dither (unoptimized - ~5 extra instructions).
|
|
float3 vOutput = float3(0, 0, 0);
|
|
|
|
int2 vDitherPhase = frac(vScreenPos * .5f) * 2.0f;
|
|
float flMagnitude = ORDERED_DITHER_MAGNITUDE * (1.0f/4.0f);
|
|
if (vDitherPhase.x)
|
|
{
|
|
if (vDitherPhase.y)
|
|
{
|
|
// 11: 1
|
|
vOutput.xyz += float3(1.0f, 1.0f, 1.0f) * flMagnitude;
|
|
}
|
|
else
|
|
{
|
|
// 10: 2
|
|
vOutput.xyz += float3(2.0f, 2.0f, 2.0f) * flMagnitude;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (vDitherPhase.y)
|
|
{
|
|
// 01: 3
|
|
vOutput.xyz += float3(3.0f, 3.0f, 3.0f) * flMagnitude;
|
|
}
|
|
else
|
|
{
|
|
// 00: 0
|
|
//vOutput = float3(0.0f, 0.0f, 0.0f);
|
|
}
|
|
}
|
|
// Output biasing to match Iestyn's dither.
|
|
vOutput -= float3(2.0f, 2.0f, 2.0f) * flMagnitude;
|
|
return vOutput;
|
|
}
|
|
}
|
|
|
|
|
|
#endif //#ifndef COMMON_PS_FXC_H_
|