Ich verwende eine Varianzschatten-Zuordnung mit einem "Standard" -Lichtblutungs-Fix in meiner Grafik-Engine, der auf verzögertem Rendern basiert. Ich habe eine einzelne Schattenzuordnung für ein gerichtetes Licht, da eine Testszene relativ klein ist. Das Speichern von Tiefe sieht folgendermaßen aus:
float moment1 = gl_FragCoord.z;
float moment2 = moment1 * moment1;
outColor = vec2(moment1, moment2);
Dann führe ich eine trennbare Gaußsche Unschärfe an dieser Textur durch.
Schattentest:
float Chebyshev(vec2 moments, float mean, float minVariance)
{
float shadow = 1.0f;
if(mean <= moments.x)
{
shadow = 1.0f;
return shadow;
}
else
{
float variance = moments.y - (moments.x * moments.x);
variance = max(variance, minVariance);
float d = mean - moments.x;
shadow = variance / (variance + (d * d));
float amount = 0.3f;
shadow = clamp((shadow - amount) / (1.0f - amount), 0.0f, 1.0f); // standard light bleeding fix
return shadow;
}
}
vec4 shadowCoord = shadowMatrix * vec4(viewSpacePosition, 1.0f);
vec2 moments = texture(ShadowMap, shadowCoord.xy).xy;
float minVariance = 0.000001f;
float shadow = Chebyshev(moments, shadowCoord.z, minVariance);
Ich fand eine interessante Technik - Exponential Varianz Shadow Mapping, die eine bessere Lösung für leichte Blutungen bieten sollte. Hier sparen Sie Tiefe für diese Technik:
float positiveExponent = 40.0f;
float negativeExponent = 5.0f
float depth = gl_FragCoord.z;
vec2 exponents = vec2(positiveExponent, negativeExponent);
depth = 2.0f * depth - 1.0f;
float pos = exp(exponents.x * depth);
float neg = -exp(-exponents.y * depth);
vec2 warpDepth = (pos, neg);
outColor = vec4(warpDepth, warpDepth * warpDepth);
Wie implementiere ich einen Schattentest für diese Technik? Hier ist mein Versuch:
float positiveExponent = 40.0f;
float negativeExponent = 5.0f;
vec2 exponents = vec2(positiveExponent, negativeExponent);
vec2 warpDepth(float depth)
{
depth = 2.0f * depth - 1.0f;
float pos = exp(exponents.x * depth);
float neg = -exp(-exponents.y * depth);
vec2 wDepth = vec2(pos, neg);
return wDepth;
}
float Chebyshev(vec2 moments, float mean, float minVariance)
{
float shadow = 1.0f;
if(mean <= moments.x)
{
shadow = 1.0f;
return shadow;
}
else
{
float variance = moments.y - (moments.x * moments.x);
variance = max(variance, minVariance);
float d = mean - moments.x;
shadow = variance / (variance + (d * d));
return shadow;
}
}
vec4 shadowCoord = shadowMatrix * vec4(viewSpacePosition, 1.0f);
vec4 moments = texture(ShadowMap, shadowCoord.xy).xyzw;
vec2 posMoments = vec2(moments.x, moments.z);
vec2 negMoments = vec2(moments.y, moments.w);
vec2 wDepth = warpDepth(shadowCoord.z);
//float minVariance = 0.000001f;
//Edit
vec2 depthScale = 0.0001f * exponents * wDepth;
vec2 minVariance = depthScale * depthScale;
float posResult = Chebyshev(posMoments, wDepth.x, minVariance.x);
float negResult = Chebyshev(negMoments, wDepth.y, minVariance.y);
shadow = min(posResult, negResult);
Leider entfernt es keine leichten Blutungen. Habe ich etwas falsch gemacht ? Vielleicht sollte ich zwei verschiedene minVariance basierend auf den positiven und negativen Exponenten berechnen?
Edit1: MinVariance auf folgende Weise berechnen:
vec2 depthScale = 0.0001f * exponents * wDepth;
vec2 minVariance = depthScale * depthScale;
float posResult = Chebyshev(posMoments, wDepth.x, minVariance.x);
float negResult = Chebyshev(negMoments, wDepth.y, minVariance.y);
liefert bessere Ergebnisse, aber leichte Blutungen sind immer noch gut sichtbar.
Edit2: Leichte Blutung: EVSM mit minVariance, wie oben berechnet, liefert ein besseres Ergebnis als VSM ohne Standard-Fix für leichte Blutungen, aber um das beste Ergebnis zu erzielen, verwende ich auch Standard-Fix für leichte Blutungen mit EVSM. In der Chebyshev-Funktion wird eine leichte Blutungskorrektur durchgeführt, aber vielleicht sollte ich sie nach folgender Berechnung hinzufügen: shadow = min (posResult, negResult)?
EVSM verursacht kleine Artefakte auf der beleuchteten Seite der Objekte, daher füge ich beim Speichern der EVSM-Tiefe etwas glPolygonOffset hinzu.
vec2 warpDepth = (pos, neg)
- sollte seinvec2(pos, neg
)