|
|
|
|
|
|
// Chebychev's inequality (one-tailed version) |
|
|
|
// P( x >= t ) <= pmax(t) := sigma^2 / (sigma^2 + (t - u)^2) |
|
|
|
// for us t is depth, u is E(x) i.d. the blurred depth |
|
|
|
real ShadowMoments_ChebyshevsInequality( real2 moments, real depth, real minVariance, real lightLeakBias ) |
|
|
|
float ShadowMoments_ChebyshevsInequality( float2 moments, float depth, float minVariance, float lightLeakBias ) |
|
|
|
real variance = max( moments.y - (moments.x * moments.x), minVariance ); |
|
|
|
float variance = max( moments.y - (moments.x * moments.x), minVariance ); |
|
|
|
real mD = depth - moments.x; |
|
|
|
real p = variance / (variance + mD * mD); |
|
|
|
float mD = depth - moments.x; |
|
|
|
float p = variance / (variance + mD * mD); |
|
|
|
|
|
|
|
p = saturate( (p - lightLeakBias) / (1.0 - lightLeakBias) ); |
|
|
|
return max( p, depth <= moments.x ); |
|
|
|
|
|
|
real2 ShadowMoments_WarpDepth( real depth, real2 exponents ) |
|
|
|
float2 ShadowMoments_WarpDepth( float depth, float2 exponents ) |
|
|
|
real pos = exp( exponents.x * depth ); |
|
|
|
real neg = -exp(-exponents.y * depth ); |
|
|
|
return real2( pos, neg ); |
|
|
|
float pos = exp( exponents.x * depth ); |
|
|
|
float neg = -exp(-exponents.y * depth ); |
|
|
|
return float2( pos, neg ); |
|
|
|
// Prepare the moments so there's little quantization error when storing the moments at real |
|
|
|
// Prepare the moments so there's little quantization error when storing the moments at float |
|
|
|
real4 ShadowMoments_Encode16MSM( real depth ) |
|
|
|
float4 ShadowMoments_Encode16MSM( float depth ) |
|
|
|
real dsq = depth * depth; |
|
|
|
real4 moments = { depth, dsq, depth * dsq, dsq * dsq }; |
|
|
|
real4x4 mat = { - 2.07224649 , 13.7948857237, 0.105877704 , 9.7924062118, |
|
|
|
float dsq = depth * depth; |
|
|
|
float4 moments = { depth, dsq, depth * dsq, dsq * dsq }; |
|
|
|
float4x4 mat = { - 2.07224649 , 13.7948857237, 0.105877704 , 9.7924062118, |
|
|
|
real4 optimized = mul( moments, mat ); |
|
|
|
float4 optimized = mul( moments, mat ); |
|
|
|
real4 ShadowMoments_Decode16MSM( real4 moments ) |
|
|
|
float4 ShadowMoments_Decode16MSM( float4 moments ) |
|
|
|
real4x4 mat = { 0.2227744146, 0.1549679261, 0.1451988946, 0.163127443, |
|
|
|
float4x4 mat = { 0.2227744146, 0.1549679261, 0.1451988946, 0.163127443, |
|
|
|
0.0771972861, 0.1394629426, 0.2120202157, 0.2591432266, |
|
|
|
0.7926986636, 0.7963415838, 0.7258694464, 0.6539092497, |
|
|
|
0.0319417555, -0.1722823173, -0.2758014811, -0.3376131734 }; |
|
|
|
|
|
|
// Note: Don't call this with all moments being equal or 0.0, otherwise this code degenerates into lots of +/-inf calculations |
|
|
|
// which don't behave quite the same on all hardware. |
|
|
|
void ShadowMoments_SolveMSM( real4 moments, real depth, real momentBias, out real3 z, out real4 b ) |
|
|
|
void ShadowMoments_SolveMSM( float4 moments, float depth, float momentBias, out float3 z, out float4 b ) |
|
|
|
{ |
|
|
|
// Bias input data to avoid artifacts |
|
|
|
z[0] = depth; |
|
|
|
|
|
|
real L32D22 = mad( -b[0], b[1], b[2] ); |
|
|
|
real D22 = mad( -b[0], b[0], b[1] ); |
|
|
|
real sqDepthVar = mad( -b[1], b[1], b[3] ); |
|
|
|
real D33D22 = dot( real2( sqDepthVar, -L32D22 ), real2( D22, L32D22 ) ); |
|
|
|
real InvD22 = 1.0 / D22; |
|
|
|
real L32 = L32D22 * InvD22; |
|
|
|
float L32D22 = mad( -b[0], b[1], b[2] ); |
|
|
|
float D22 = mad( -b[0], b[0], b[1] ); |
|
|
|
float sqDepthVar = mad( -b[1], b[1], b[3] ); |
|
|
|
float D33D22 = dot( float2( sqDepthVar, -L32D22 ), float2( D22, L32D22 ) ); |
|
|
|
float InvD22 = 1.0 / D22; |
|
|
|
float L32 = L32D22 * InvD22; |
|
|
|
real3 c = real3( 1.0, z[0], z[0] * z[0] ); |
|
|
|
float3 c = float3( 1.0, z[0], z[0] * z[0] ); |
|
|
|
// Forward substitution to solve L * c1 = bz; |
|
|
|
c[1] -= b.x; |
|
|
|
c[2] -= b.y + L32 * c[1]; |
|
|
|
|
|
|
c[1] -= L32 * c[2]; |
|
|
|
c[0] -= dot( c.yz, b.xy ); |
|
|
|
// Solve the quadratic equation c[0] + c[1] * z + c[2] * z^2 to obtain solutions z[1] and z[2] |
|
|
|
real p = c[1] / c[2]; |
|
|
|
real q = c[0] / c[2]; |
|
|
|
real D = ((p*p) * 0.25) - q; |
|
|
|
real r = sqrt( D ); |
|
|
|
float p = c[1] / c[2]; |
|
|
|
float q = c[0] / c[2]; |
|
|
|
float D = ((p*p) * 0.25) - q; |
|
|
|
float r = sqrt( D ); |
|
|
|
real ShadowMoments_SolveDelta3MSM( real3 z, real2 b, real lightLeakBias ) |
|
|
|
float ShadowMoments_SolveDelta3MSM( float3 z, float2 b, float lightLeakBias ) |
|
|
|
real4 switchVal = (z[2] < z[0]) ? real4( z[1], z[0], 1.0, 1.0 ) |
|
|
|
: ((z[1] < z[0]) ? real4( z[0], z[1], 0.0, 1.0 ) : 0.0.xxxx); |
|
|
|
float4 switchVal = (z[2] < z[0]) ? float4( z[1], z[0], 1.0, 1.0 ) |
|
|
|
: ((z[1] < z[0]) ? float4( z[0], z[1], 0.0, 1.0 ) : 0.0.xxxx); |
|
|
|
real quotient = (switchVal[0] * z[2] - b[0] * (switchVal[0] + z[2]) + b[1]) / ((z[2] - switchVal[1]) * (z[0] - z[1])); |
|
|
|
real attenuation = saturate( switchVal[2] + switchVal[3] * quotient ); |
|
|
|
float quotient = (switchVal[0] * z[2] - b[0] * (switchVal[0] + z[2]) + b[1]) / ((z[2] - switchVal[1]) * (z[0] - z[1])); |
|
|
|
float attenuation = saturate( switchVal[2] + switchVal[3] * quotient ); |
|
|
|
real ShadowMoments_SolveDelta4MSM( real3 z, real4 b, real lightLeakBias) |
|
|
|
float ShadowMoments_SolveDelta4MSM( float3 z, float4 b, float lightLeakBias) |
|
|
|
real zFree = ((b[2] - b[1]) * z[0] + b[2] - b[3]) / ((b[1] - b[0]) * z[0] + b[1] - b[2]); |
|
|
|
real w1Factor = (z[0] > zFree) ? 1.0 : 0.0; |
|
|
|
real attenuation = saturate( (b[1] - b[0] + (b[2] - b[0] - (zFree + 1.0) * (b[1] - b[0])) * (zFree - w1Factor - z[0]) / (z[0] * (z[0] - zFree))) / (zFree - w1Factor) + 1.0 - b[0] ); |
|
|
|
float zFree = ((b[2] - b[1]) * z[0] + b[2] - b[3]) / ((b[1] - b[0]) * z[0] + b[1] - b[2]); |
|
|
|
float w1Factor = (z[0] > zFree) ? 1.0 : 0.0; |
|
|
|
float attenuation = saturate( (b[1] - b[0] + (b[2] - b[0] - (zFree + 1.0) * (b[1] - b[0])) * (zFree - w1Factor - z[0]) / (z[0] * (z[0] - zFree))) / (zFree - w1Factor) + 1.0 - b[0] ); |
|
|
|
|
|
|
|
return saturate( ((1.0 - attenuation) - lightLeakBias) / (1.0 - lightLeakBias) ); |
|
|
|
} |