806 lines
28 KiB
C
806 lines
28 KiB
C
//========= Copyright 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!
|
|
|
|
// --------------------------------------------------------------------------------
|
|
// HDR should never be enabled if we don't aren't running in float or integer
|
|
// HDR mode. SKIP: defined $HDRTYPE && defined $HDRENABLED && !$HDRTYPE &&
|
|
// $HDRENABLED
|
|
// --------------------------------------------------------------------------------
|
|
// 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
|
|
// --------------------------------------------------------------------------------
|
|
// We don't need fog in the pixel shader if we aren't in float fog mode2
|
|
// NOSKIP: defined $HDRTYPE && defined $HDRENABLED && defined $PIXELFOGTYPE &&
|
|
// $HDRTYPE != HDR_TYPE_FLOAT && $FOGTYPE != 0
|
|
// --------------------------------------------------------------------------------
|
|
// We don't do HDR and LIGHTING_PREVIEW at the same time since it's running LDR
|
|
// in hammer.
|
|
// SKIP: defined $LIGHTING_PREVIEW && defined $HDRTYPE && $LIGHTING_PREVIEW &&
|
|
// $HDRTYPE != 0
|
|
// --------------------------------------------------------------------------------
|
|
// 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
|
|
// --------------------------------------------------------------------------------
|
|
|
|
// 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);
|
|
const float4 cFlashlightScreenScale
|
|
: register(c31); // .zw are currently unused
|
|
#define flFlashlightNoLambertValue \
|
|
cFlashlightColor.w // This is either 0.0 or 2.0
|
|
#endif
|
|
|
|
#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_POISSON 0
|
|
#define ATI_NOPCF 1
|
|
#define ATI_NO_PCF_FETCH4 2
|
|
|
|
struct LPREVIEW_PS_OUT {
|
|
float4 color : COLOR0;
|
|
float4 normal : COLOR1;
|
|
float4 position : COLOR2;
|
|
float4 flags : COLOR3;
|
|
};
|
|
|
|
/*
|
|
// unused
|
|
HALF Luminance( HALF3 color )
|
|
{
|
|
return dot( color, HALF3( HALF_CONSTANT(0.30f), HALF_CONSTANT(0.59f),
|
|
HALF_CONSTANT(0.11f) ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
HALF LuminanceScaled( HALF3 color )
|
|
{
|
|
return dot( color, HALF3( HALF_CONSTANT(0.30f) / MAX_HDR_OVERBRIGHT,
|
|
HALF_CONSTANT(0.59f) / MAX_HDR_OVERBRIGHT, HALF_CONSTANT(0.11f) /
|
|
MAX_HDR_OVERBRIGHT ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
HALF AvgColor( HALF3 color )
|
|
{
|
|
return dot( color, HALF3( HALF_CONSTANT(0.33333f),
|
|
HALF_CONSTANT(0.33333f), HALF_CONSTANT(0.33333f) ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
HALF4 DiffuseBump( sampler lightmapSampler,
|
|
float2 lightmapTexCoord1,
|
|
float2 lightmapTexCoord2,
|
|
float2 lightmapTexCoord3,
|
|
HALF3 normal )
|
|
{
|
|
HALF3 lightmapColor1 = tex2D( lightmapSampler, lightmapTexCoord1 );
|
|
HALF3 lightmapColor2 = tex2D( lightmapSampler, lightmapTexCoord2 );
|
|
HALF3 lightmapColor3 = tex2D( lightmapSampler, lightmapTexCoord3 );
|
|
|
|
HALF3 diffuseLighting;
|
|
diffuseLighting = saturate( dot( normal, bumpBasis[0] ) ) *
|
|
lightmapColor1 + saturate( dot( normal, bumpBasis[1] ) ) * lightmapColor2 +
|
|
saturate( dot( normal, bumpBasis[2] )
|
|
) * lightmapColor3;
|
|
|
|
return HALF4( diffuseLighting, LuminanceScaled( diffuseLighting ) );
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
HALF Fresnel( HALF3 normal,
|
|
HALF3 eye,
|
|
HALF2 scaleBias )
|
|
{
|
|
HALF fresnel = HALF_CONSTANT(1.0f) - dot( normal, eye );
|
|
fresnel = pow( fresnel, HALF_CONSTANT(5.0f) );
|
|
|
|
return fresnel * scaleBias.x + scaleBias.y;
|
|
}
|
|
*/
|
|
|
|
/*
|
|
// unused
|
|
HALF4 GetNormal( sampler normalSampler,
|
|
float2 normalTexCoord )
|
|
{
|
|
HALF4 normal = tex2D( normalSampler, normalTexCoord );
|
|
normal.rgb = HALF_CONSTANT(2.0f) * normal.rgb - HALF_CONSTANT(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);
|
|
}
|
|
|
|
HALF3 NormalizeWithCubemap(sampler normalizeSampler, HALF3 input) {
|
|
// return texCUBE( normalizeSampler, input ) * 2.0f - 1.0f;
|
|
return texCUBE(normalizeSampler, input);
|
|
}
|
|
|
|
/*
|
|
HALF4 EnvReflect( sampler envmapSampler,
|
|
sampler normalizeSampler,
|
|
HALF3 normal,
|
|
float3 eye,
|
|
HALF2 fresnelScaleBias )
|
|
{
|
|
HALF3 normEye = NormalizeWithCubemap( normalizeSampler, eye );
|
|
HALF fresnel = Fresnel( normal, normEye, fresnelScaleBias );
|
|
HALF3 reflect = CalcReflectionVectorUnnormalized( normal, eye );
|
|
return texCUBE( envmapSampler, reflect );
|
|
}
|
|
*/
|
|
|
|
float CalcWaterFogAlpha(const float flWaterZ, const float flEyePosZ,
|
|
const float flWorldPosZ, const float flProjPosZ,
|
|
const float flFogOORange) {
|
|
// 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 = saturate(flDepthFromWater * (1.0 / flDepthFromEye));
|
|
|
|
// $tmp.w is now the distance that we see through water.
|
|
return saturate(f * flProjPosZ * flFogOORange);
|
|
}
|
|
|
|
float CalcRangeFog(const float flProjPosZ, const float flFogStartOverRange,
|
|
const float flFogMaxDensity, const float flFogOORange) {
|
|
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
|
|
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
|
|
return saturate(min(flFogMaxDensity,
|
|
(flProjPosZ * flFogOORange) - flFogStartOverRange));
|
|
#else
|
|
return 0.0f; // ps20 shaders will never have range fog enabled because too
|
|
// many ran out of slots.
|
|
#endif
|
|
}
|
|
|
|
float CalcPixelFogFactor(int iPIXELFOGTYPE, const float4 fogParams,
|
|
const float flEyePosZ, const float flWorldPosZ,
|
|
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
|
|
{
|
|
retVal =
|
|
CalcRangeFog(flProjPosZ, fogParams.x, fogParams.z, fogParams.w);
|
|
} else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_HEIGHT) // height fog
|
|
{
|
|
retVal = CalcWaterFogAlpha(fogParams.y, flEyePosZ, flWorldPosZ,
|
|
flProjPosZ, fogParams.w);
|
|
}
|
|
|
|
return retVal;
|
|
}
|
|
|
|
// 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
|
|
pixelFogFactor = saturate(pixelFogFactor);
|
|
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, saturate(pixelFogFactor));
|
|
} else if (iPIXELFOGTYPE == PIXEL_FOG_TYPE_NONE) {
|
|
return vShaderColor;
|
|
}
|
|
}
|
|
|
|
#if ((defined(SHADER_MODEL_PS_2_B) || defined(SHADER_MODEL_PS_3_0)) && \
|
|
(CONVERT_TO_SRGB != 0))
|
|
sampler1D GammaTableSampler : register(s15);
|
|
|
|
float3 SRGBOutput(const float3 vShaderColor) {
|
|
// On ps2b capable hardware we always have the linear->gamma conversion
|
|
// table texture in sampler s15.
|
|
float3 result;
|
|
result.r = tex1D(GammaTableSampler, vShaderColor.r).r;
|
|
result.g = tex1D(GammaTableSampler, vShaderColor.g).r;
|
|
result.b = tex1D(GammaTableSampler, vShaderColor.b).r;
|
|
return result;
|
|
}
|
|
|
|
#else
|
|
|
|
float3 SRGBOutput(const float3 vShaderColor) {
|
|
return vShaderColor; // ps 1.1, 1.4, and 2.0 never do srgb conversion in
|
|
// the pixel shader
|
|
}
|
|
|
|
#endif
|
|
|
|
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 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;
|
|
|
|
result.rgb = BlendPixelFog(result.rgb, pixelFogFactor, g_LinearFogColor.rgb,
|
|
iPIXELFOGTYPE);
|
|
|
|
#if !(defined(SHADER_MODEL_PS_1_1) || defined(SHADER_MODEL_PS_1_4) || \
|
|
defined(SHADER_MODEL_PS_2_0)) // Minimum requirement of ps2b
|
|
result.rgb = SRGBOutput(result.rgb); // SRGB in pixel shader conversion
|
|
#endif
|
|
|
|
return 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 = SRGBOutput(vShaderColor.normal.rgb);
|
|
result.normal.a = vShaderColor.normal.a;
|
|
|
|
result.position.rgb = SRGBOutput(vShaderColor.position.rgb);
|
|
result.position.a = vShaderColor.position.a;
|
|
|
|
result.flags.rgb = SRGBOutput(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
|
|
|
|
float4 TextureCombine(float4 baseColor, float4 detailColor, int combine_mode,
|
|
float fBlendFactor) {
|
|
if (combine_mode == TCOMBINE_MOD2X_SELECT_TWO_PATTERNS) {
|
|
float3 dc = lerp(detailColor.r, detailColor.a, baseColor.a);
|
|
baseColor.rgb *= lerp(float3(1, 1, 1), 2.0 * dc, fBlendFactor);
|
|
}
|
|
if (combine_mode == TCOMBINE_RGB_EQUALS_BASE_x_DETAILx2)
|
|
baseColor.rgb *=
|
|
lerp(float3(1, 1, 1), 2.0 * detailColor.rgb, fBlendFactor);
|
|
if (combine_mode == TCOMBINE_RGB_ADDITIVE)
|
|
baseColor.rgb += fBlendFactor * detailColor.rgb;
|
|
if (combine_mode == TCOMBINE_DETAIL_OVER_BASE) {
|
|
float 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) {
|
|
float 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.0 / 3.0);
|
|
}
|
|
return baseColor;
|
|
}
|
|
|
|
float3 lerp5(float3 f1, float3 f2, float i1, float i2, float x) {
|
|
return f1 + (f2 - f1) * (x - i1) / (i2 - i1);
|
|
}
|
|
|
|
float3 TextureCombinePostLighting(float3 lit_baseColor, float4 detailColor,
|
|
int combine_mode, float 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.5)
|
|
// lit_baseColor += min(1, (1.0/fBlendFactor)*max(0,
|
|
//detailColor.rgb-(1-fBlendFactor) ) ); else lit_baseColor +=
|
|
//2*fBlendFactor*2*max(0, detailColor.rgb-.5);
|
|
|
|
float f = fBlendFactor - 0.5;
|
|
float fMult = (f >= 0) ? 1.0 / fBlendFactor : 4 * fBlendFactor;
|
|
float fAdd = (f >= 0) ? 1.0 - fMult : -0.5 * fMult;
|
|
lit_baseColor += saturate(fMult * detailColor.rgb + fAdd);
|
|
}
|
|
return lit_baseColor;
|
|
}
|
|
|
|
// NOTE: On X360. fProjZ is expected to be pre-reversed for cheaper math here in
|
|
// the pixel shader
|
|
float DepthFeathering(sampler DepthSampler, const float2 vScreenPos,
|
|
float fProjZ, float fProjW, 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
|
|
{
|
|
float flFeatheredAlpha;
|
|
float2 flDepths;
|
|
#define flSceneDepth flDepths.x
|
|
#define flSpriteDepth flDepths.y
|
|
|
|
#if (defined(_X360))
|
|
{
|
|
// Get depth from the depth texture. Need to sample with the offset
|
|
// of (0.5, 0.5) to fix rounding errors
|
|
asm {
|
|
tfetch2D flDepths.x___, vScreenPos, DepthSampler, OffsetX=0.5, OffsetY=0.5, MinFilter=point, MagFilter=point, MipFilter=point
|
|
}
|
|
;
|
|
|
|
#if (!defined(REVERSE_DEPTH_ON_X360))
|
|
flSceneDepth = 1.0f - flSceneDepth;
|
|
#endif
|
|
|
|
// get the sprite depth into the same range as the texture depth
|
|
flSpriteDepth = fProjZ / fProjW;
|
|
|
|
// unproject to get at the pre-projection z. This value is much more
|
|
// linear than depth
|
|
flDepths = vDepthBlendConstants.z / flDepths;
|
|
flDepths = vDepthBlendConstants.y - flDepths;
|
|
|
|
flFeatheredAlpha = flSceneDepth - flSpriteDepth;
|
|
flFeatheredAlpha *= vDepthBlendConstants.x;
|
|
flFeatheredAlpha = saturate(flFeatheredAlpha);
|
|
}
|
|
#else
|
|
{
|
|
flSceneDepth = tex2D(DepthSampler, vScreenPos)
|
|
.a; // PC uses dest alpha of the frame buffer
|
|
flSpriteDepth = SoftParticleDepth(fProjZ);
|
|
|
|
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
|
|
|
|
#undef flSceneDepth
|
|
#undef flSpriteDepth
|
|
|
|
return flFeatheredAlpha;
|
|
}
|
|
#else
|
|
{ return 1.0f; }
|
|
#endif
|
|
}
|
|
|
|
#endif //#ifndef COMMON_PS_FXC_H_
|