- remove shadow acne from PCF shadowmap filter and add three quality levels

This commit is contained in:
Magnus Norddahl 2018-10-05 04:36:11 +02:00
parent 09bc6614cf
commit 6135e867a9
4 changed files with 72 additions and 68 deletions

View file

@ -2442,7 +2442,9 @@ OPTVAL_FOO_DUMB = "foo_dumb";
OPTVAL_ALIASING = "Aliasing";
OPTVAL_LINEAR = "Linear";
OPTVAL_NEAREST = "Nearest";
OPTVAL_PCF = "PCF";
OPTVAL_PCF_LOW = "PCF (Low)";
OPTVAL_PCF_MEDIUM = "PCF (Medium)";
OPTVAL_PCF_HIGH = "PCF (High)";
OPTVAL_CUBIC = "Cubic";
OPTVAL_BLEP = "Band-limited step";
OPTVAL_LINEARSLOW = "Linear (Slower)";

View file

@ -2419,7 +2419,9 @@ OPTVAL_DEFAULT = "Défaut";
OPTVAL_SOUNDSYSTEM = "Système Sonore";
OPTVAL_LINEAR = "Linéaire";
OPTVAL_NEAREST = "Nearest";
OPTVAL_PCF = "PCF";
OPTVAL_PCF_LOW = "PCF (Low)";
OPTVAL_PCF_MEDIUM = "PCF (Medium)";
OPTVAL_PCF_HIGH = "PCF (High)";
OPTVAL_CUBIC = "Cubique";
OPTVAL_BLEP = "Step limité sur bande";
OPTVAL_LINEARSLOW = "Linéaire (Lent)";

View file

@ -2259,7 +2259,9 @@ OptionValue ShadowMapQuality
OptionValue ShadowMapFilter
{
0, "$OPTVAL_NEAREST"
1, "$OPTVAL_PCF"
1, "$OPTVAL_PCF_LOW"
2, "$OPTVAL_PCF_MEDIUM"
3, "$OPTVAL_PCF_HIGH"
}

View file

@ -178,47 +178,26 @@ float shadowDirToU(vec2 dir)
}
}
float sampleShadowmap(vec2 dir, float v)
vec2 shadowUToDir(float u)
{
float u = shadowDirToU(dir);
float dist2 = dot(dir, dir);
return step(dist2, texture(ShadowMap, vec2(u, v)).x);
u *= 4.0;
vec2 raydir;
switch (int(u))
{
case 0: raydir = vec2(u * 2.0 - 1.0, 1.0); break;
case 1: raydir = vec2(1.0, 1.0 - (u - 1.0) * 2.0); break;
case 2: raydir = vec2(1.0 - (u - 2.0) * 2.0, -1.0); break;
case 3: raydir = vec2(-1.0, (u - 3.0) * 2.0 - 1.0); break;
}
return raydir;
}
float sampleShadowmapLinear(vec2 dir, float v)
float sampleShadowmap(vec3 planePoint, float v)
{
float u = shadowDirToU(dir);
float dist2 = dot(dir, dir);
float bias = 1.0;
float negD = dot(vWorldNormal.xyz, planePoint);
vec2 isize = textureSize(ShadowMap, 0);
vec2 size = vec2(isize);
vec2 fetchPos = vec2(u, v) * size - vec2(0.5, 0.0);
if (fetchPos.x < 0.0)
fetchPos.x += size.x;
ivec2 ifetchPos = ivec2(fetchPos);
int y = ifetchPos.y;
float t = fract(fetchPos.x);
int x0 = ifetchPos.x;
int x1 = ifetchPos.x + 1;
if (x1 == isize.x)
x1 = 0;
float depth0 = texelFetch(ShadowMap, ivec2(x0, y), 0).x;
float depth1 = texelFetch(ShadowMap, ivec2(x1, y), 0).x;
return mix(step(dist2, depth0), step(dist2, depth1), t);
}
vec2 shadowmapAdjustedRay(vec4 lightpos)
{
vec3 planePoint = pixelpos.xyz - lightpos.xyz;
if (dot(planePoint.xz, planePoint.xz) < 1.0)
return planePoint.xz * 0.5;
vec3 ray = normalize(planePoint);
vec3 ray = planePoint;
vec2 isize = textureSize(ShadowMap, 0);
float scale = float(isize.x) * 0.25;
@ -239,51 +218,70 @@ vec2 shadowmapAdjustedRay(vec4 lightpos)
ray.x = sign(ray.x);
}
float bias = 1.0;
float negD = dot(vWorldNormal.xyz, planePoint);
float t = negD / dot(vWorldNormal.xyz, ray) - bias;
return ray.xz * t;
vec2 dir = ray.xz * t;
float u = shadowDirToU(dir);
float dist2 = dot(dir, dir);
return step(dist2, texture(ShadowMap, vec2(u, v)).x);
}
//===========================================================================
//
// Check if light is in shadow using Percentage Closer Filtering (PCF)
//
//===========================================================================
float sampleShadowmapPCF(vec3 planePoint, float v)
{
float bias = 1.0;
float negD = dot(vWorldNormal.xyz, planePoint);
vec3 ray = planePoint;
if (abs(ray.z) > abs(ray.x))
ray.y = ray.y / abs(ray.z);
else
ray.y = ray.y / abs(ray.x);
vec2 isize = textureSize(ShadowMap, 0);
float scale = float(isize.x);
float texelPos = floor(shadowDirToU(ray.xz) * scale);
float sum = 0.0;
float step_count = uShadowmapFilter;
texelPos -= step_count + 0.5;
for (float x = -step_count; x <= step_count; x++)
{
float u = texelPos / scale;
vec2 dir = shadowUToDir(u);
ray.x = dir.x;
ray.z = dir.y;
float t = negD / dot(vWorldNormal.xyz, ray) - bias;
dir = ray.xz * t;
float dist2 = dot(dir, dir);
sum += step(dist2, texture(ShadowMap, vec2(u, v)).x);
texelPos++;
}
return sum / (uShadowmapFilter * 2.0 + 1.0);
}
float shadowmapAttenuation(vec4 lightpos, float shadowIndex)
{
if (shadowIndex >= 1024.0)
return 1.0; // No shadowmap available for this light
float v = (shadowIndex + 0.5) / 1024.0;
vec3 planePoint = pixelpos.xyz - lightpos.xyz;
vec2 ray = shadowmapAdjustedRay(lightpos);
if (dot(planePoint.xz, planePoint.xz) < 1.0)
return 1.0; // Light is too close
float v = (shadowIndex + 0.5) / 1024.0;
if (uShadowmapFilter <= 0)
{
return sampleShadowmap(ray, v);
//return sampleShadowmapLinear(ray, v);
return sampleShadowmap(planePoint, v);
}
else
{
float length = length(ray);
if (length < 3.0)
return 1.0;
vec2 dir = ray / length * min(length / 50.0, 1.0); // avoid sampling behind light
vec2 normal = vec2(-dir.y, dir.x);
vec2 bias = dir * 10.0;
float sum = 0.0;
float step_count = ((uShadowmapFilter - 1) / 2.);
for (float x = -step_count; x <= step_count; x++)
{
sum += sampleShadowmap(ray + normal * x /*- bias * abs(x)*/, v);
}
return sum / uShadowmapFilter;
return sampleShadowmapPCF(planePoint, v);
}
}