310 lines
8.5 KiB
HLSL
310 lines
8.5 KiB
HLSL
/* This file implements standard programs for depth shadow mapping.
|
|
These particular ones are suitable for additive lighting models, and
|
|
include 3 techniques to reduce depth fighting on self-shadowed surfaces,
|
|
constant bias, gradient (slope-scale) bias, and a fuzzy shadow map comparison*/
|
|
|
|
|
|
// Shadow caster vertex program.
|
|
void casterVP(
|
|
float4 position : POSITION,
|
|
out float4 outPos : POSITION,
|
|
out float2 outDepth : TEXCOORD0,
|
|
|
|
uniform float4x4 worldViewProj,
|
|
uniform float4 texelOffsets,
|
|
uniform float4 depthRange
|
|
)
|
|
{
|
|
outPos = mul(worldViewProj, position);
|
|
|
|
// fix pixel / texel alignment
|
|
outPos.xy += texelOffsets.zw * outPos.w;
|
|
// linear depth storage
|
|
// offset / scale range output
|
|
#if LINEAR_RANGE
|
|
outDepth.x = (outPos.z - depthRange.x) * depthRange.w;
|
|
#else
|
|
outDepth.x = outPos.z;
|
|
#endif
|
|
outDepth.y = outPos.w;
|
|
}
|
|
|
|
|
|
// Shadow caster fragment program for high-precision single-channel textures
|
|
void casterFP(
|
|
float2 depth : TEXCOORD0,
|
|
out float4 result : COLOR)
|
|
|
|
{
|
|
#if LINEAR_RANGE
|
|
float finalDepth = depth.x;
|
|
#else
|
|
float finalDepth = depth.x / depth.y;
|
|
#endif
|
|
// just smear across all components
|
|
// therefore this one needs high individual channel precision
|
|
result = float4(finalDepth, finalDepth, finalDepth, 1);
|
|
}
|
|
|
|
|
|
|
|
void receiverVP(
|
|
float4 position : POSITION,
|
|
float4 normal : NORMAL,
|
|
|
|
out float4 outPos : POSITION,
|
|
out float4 outColour : COLOR,
|
|
out float4 outShadowUV : TEXCOORD0,
|
|
|
|
uniform float4x4 world,
|
|
uniform float4x4 worldIT,
|
|
uniform float4x4 worldViewProj,
|
|
uniform float4x4 texViewProj,
|
|
uniform float4 lightPosition,
|
|
uniform float4 lightColour,
|
|
uniform float4 shadowDepthRange
|
|
)
|
|
{
|
|
float4 worldPos = mul(world, position);
|
|
outPos = mul(worldViewProj, position);
|
|
|
|
float3 worldNorm = mul(worldIT, normal).xyz;
|
|
|
|
// calculate lighting (simple vertex lighting)
|
|
float3 lightDir = normalize(
|
|
lightPosition.xyz - (worldPos.xyz * lightPosition.w));
|
|
|
|
outColour = lightColour * max(dot(lightDir, worldNorm), 0.0);
|
|
|
|
// calculate shadow map coords
|
|
outShadowUV = mul(texViewProj, worldPos);
|
|
#if LINEAR_RANGE
|
|
// adjust by fixed depth bias, rescale into range
|
|
outShadowUV.z = (outShadowUV.z - shadowDepthRange.x) * shadowDepthRange.w;
|
|
#endif
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
void receiverFP(
|
|
float4 position : POSITION,
|
|
float4 shadowUV : TEXCOORD0,
|
|
float4 vertexColour : COLOR,
|
|
|
|
uniform sampler2D shadowMap : register(s0),
|
|
uniform float inverseShadowmapSize,
|
|
uniform float fixedDepthBias,
|
|
uniform float gradientClamp,
|
|
uniform float gradientScaleBias,
|
|
uniform float shadowFuzzyWidth,
|
|
|
|
out float4 result : COLOR)
|
|
{
|
|
// point on shadowmap
|
|
#if LINEAR_RANGE
|
|
shadowUV.xy = shadowUV.xy / shadowUV.w;
|
|
#else
|
|
shadowUV = shadowUV / shadowUV.w;
|
|
#endif
|
|
float centerdepth = tex2D(shadowMap, shadowUV.xy).x;
|
|
|
|
// gradient calculation
|
|
float pixeloffset = inverseShadowmapSize;
|
|
float4 depths = float4(
|
|
tex2D(shadowMap, shadowUV.xy + float2(-pixeloffset, 0)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(+pixeloffset, 0)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(0, -pixeloffset)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(0, +pixeloffset)).x);
|
|
|
|
float2 differences = abs( depths.yw - depths.xz );
|
|
float gradient = min(gradientClamp, max(differences.x, differences.y));
|
|
float gradientFactor = gradient * gradientScaleBias;
|
|
|
|
// visibility function
|
|
float depthAdjust = gradientFactor + (fixedDepthBias * centerdepth);
|
|
float finalCenterDepth = centerdepth + depthAdjust;
|
|
|
|
// shadowUV.z contains lightspace position of current object
|
|
|
|
#if FUZZY_TEST
|
|
// fuzzy test - introduces some ghosting in result and doesn't appear to be needed?
|
|
//float visibility = saturate(1 + delta_z / (gradient * shadowFuzzyWidth));
|
|
float visibility = saturate(1 + (finalCenterDepth - shadowUV.z) * shadowFuzzyWidth * shadowUV.w);
|
|
|
|
result = vertexColour * visibility;
|
|
#else
|
|
// hard test
|
|
#if PCF
|
|
// use depths from prev, calculate diff
|
|
depths += depthAdjust.xxxx;
|
|
float final = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.x > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.y > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.z > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.w > shadowUV.z) ? 1.0f : 0.0f;
|
|
|
|
final *= 0.2f;
|
|
|
|
result = float4(vertexColour.xyz * final, 1);
|
|
|
|
#else
|
|
result = (finalCenterDepth > shadowUV.z) ? vertexColour : float4(0,0,0,1);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Expand a range-compressed vector
|
|
float3 expand(float3 v)
|
|
{
|
|
return (v - 0.5) * 2;
|
|
}
|
|
|
|
|
|
/* Normal mapping plus depth shadowmapping receiver programs
|
|
*/
|
|
void normalMapShadowReceiverVp(float4 position : POSITION,
|
|
float3 normal : NORMAL,
|
|
float2 uv : TEXCOORD0,
|
|
float3 tangent : TANGENT0,
|
|
|
|
// outputs
|
|
out float4 outPos : POSITION,
|
|
out float4 outShadowUV : TEXCOORD0,
|
|
out float2 oUv : TEXCOORD1,
|
|
out float3 oTSLightDir : TEXCOORD2,
|
|
// parameters
|
|
uniform float4 lightPosition, // object space
|
|
uniform float4x4 world,
|
|
uniform float4x4 worldViewProj,
|
|
uniform float4x4 texViewProj)
|
|
{
|
|
float4 worldPos = mul(world, position);
|
|
outPos = mul(worldViewProj, position);
|
|
|
|
// calculate shadow map coords
|
|
outShadowUV = mul(texViewProj, worldPos);
|
|
#if LINEAR_RANGE
|
|
// adjust by fixed depth bias, rescale into range
|
|
outShadowUV.z = (outShadowUV.z - shadowDepthRange.x) * shadowDepthRange.w;
|
|
#endif
|
|
|
|
// pass the main uvs straight through unchanged
|
|
oUv = uv;
|
|
|
|
// calculate tangent space light vector
|
|
// Get object space light direction
|
|
// Non-normalised since we'll do that in the fragment program anyway
|
|
float3 lightDir = lightPosition.xyz - (position * lightPosition.w);
|
|
|
|
// Calculate the binormal (NB we assume both normal and tangent are
|
|
// already normalised)
|
|
// NB looks like nvidia cross params are BACKWARDS to what you'd expect
|
|
// this equates to NxT, not TxN
|
|
float3 binormal = cross(tangent, normal);
|
|
|
|
// Form a rotation matrix out of the vectors
|
|
float3x3 rotation = float3x3(tangent, binormal, normal);
|
|
|
|
// Transform the light vector according to this matrix
|
|
oTSLightDir = mul(rotation, lightDir);
|
|
|
|
|
|
}
|
|
|
|
|
|
void normalMapShadowReceiverFp(
|
|
float4 shadowUV : TEXCOORD0,
|
|
float2 uv : TEXCOORD1,
|
|
float3 TSlightDir : TEXCOORD2,
|
|
|
|
out float4 result : COLOR,
|
|
|
|
uniform float4 lightColour,
|
|
uniform float inverseShadowmapSize,
|
|
uniform float fixedDepthBias,
|
|
uniform float gradientClamp,
|
|
uniform float gradientScaleBias,
|
|
uniform float shadowFuzzyWidth,
|
|
|
|
uniform sampler2D shadowMap : register(s0),
|
|
uniform sampler2D normalMap : register(s1),
|
|
uniform samplerCUBE normalCubeMap : register(s2))
|
|
{
|
|
|
|
// retrieve normalised light vector, expand from range-compressed
|
|
float3 lightVec = expand(texCUBE(normalCubeMap, TSlightDir).xyz);
|
|
|
|
// get bump map vector, again expand from range-compressed
|
|
float3 bumpVec = expand(tex2D(normalMap, uv).xyz);
|
|
|
|
// Calculate dot product
|
|
float4 vertexColour = lightColour * dot(bumpVec, lightVec);
|
|
|
|
|
|
// point on shadowmap
|
|
#if LINEAR_RANGE
|
|
shadowUV.xy = shadowUV.xy / shadowUV.w;
|
|
#else
|
|
shadowUV = shadowUV / shadowUV.w;
|
|
#endif
|
|
float centerdepth = tex2D(shadowMap, shadowUV.xy).x;
|
|
|
|
// gradient calculation
|
|
float pixeloffset = inverseShadowmapSize;
|
|
float4 depths = float4(
|
|
tex2D(shadowMap, shadowUV.xy + float2(-pixeloffset, 0)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(+pixeloffset, 0)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(0, -pixeloffset)).x,
|
|
tex2D(shadowMap, shadowUV.xy + float2(0, +pixeloffset)).x);
|
|
|
|
float2 differences = abs( depths.yw - depths.xz );
|
|
float gradient = min(gradientClamp, max(differences.x, differences.y));
|
|
float gradientFactor = gradient * gradientScaleBias;
|
|
|
|
// visibility function
|
|
float depthAdjust = gradientFactor + (fixedDepthBias * centerdepth);
|
|
float finalCenterDepth = centerdepth + depthAdjust;
|
|
|
|
// shadowUV.z contains lightspace position of current object
|
|
|
|
#if FUZZY_TEST
|
|
// fuzzy test - introduces some ghosting in result and doesn't appear to be needed?
|
|
//float visibility = saturate(1 + delta_z / (gradient * shadowFuzzyWidth));
|
|
float visibility = saturate(1 + (finalCenterDepth - shadowUV.z) * shadowFuzzyWidth * shadowUV.w);
|
|
|
|
result = vertexColour * visibility;
|
|
#else
|
|
// hard test
|
|
#if PCF
|
|
// use depths from prev, calculate diff
|
|
depths += depthAdjust.xxxx;
|
|
float final = (finalCenterDepth > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.x > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.y > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.z > shadowUV.z) ? 1.0f : 0.0f;
|
|
final += (depths.w > shadowUV.z) ? 1.0f : 0.0f;
|
|
|
|
final *= 0.2f;
|
|
|
|
result = float4(vertexColour.xyz * final, 1);
|
|
|
|
#else
|
|
result = (finalCenterDepth > shadowUV.z) ? vertexColour : float4(0,0,0,1);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|