Exponential Shadow Maps sind nicht weich zu kriegen
Verfasst: 26.09.2012, 12:39
Guten Tag, verehrtes Fachpublikum.
Ich bin aktuell mal wieder dabei, die Splitterwelten zu optimieren. Gegen die CPU-Last wird demnächst hoffentlich ein Multicore-Renderer kommen, aber vorerst will ich noch die Schatten für gelegentlich auftretenden Stellen mit massivem Overdraw optimieren. Meine Wahl viel dabei auf Exponential Shadow Maps, da die einzige mir bekannte praktikable Alternative Variance Shadow Mapping bei den üblichen Tiefenkomplexitäten einer Splitterwelt mehr Light leaked als ein chaotisch-guter Paladin.
Also Exponential Shadow Mapping. Sieht auf den Bildern, die man im Netz dazu findet, wirklich gut aus. Aber nicht bei mir. Ich habe als Vergleich immernoch meine 32xPCF-Implementierung hier, die auch sauber gegen Selbstschattierung geschützt wurde. Aber ESM bleibt bei mir knochenhart, selbst mit 11x11 Box- oder Gauss-Weichzeichner auf der Shadow Map, logarithmisch oder linear. Es ist alles wurscht, ich kriege Penumbra Regions von bestenfalls einem Texel Breite. Folgende Bilder für maximale Aufmerksamkeitserhaschung:
ESM, ohne Filtering. Sieht größtenteils wie normales Shadow Mapping aus, aber man erkennt die Wirkung bei Kontaktschatten, z.B. beim Charakter. ESM, mit 11x11 Gauss-Filter. Schon besser, aber immernoch knochenhart. Erneuter Hinweis: exponentieller Filter sieht praktisch genauso aus, aber dazu später mehr. Referenzbild: 32 PCF-Samples emulieren einen ungefähr 5x5-Boxfilter. Man bemerke die trotzdem weiter reichenden Penumbra Regions. Jetzt meine Frage an die Experten: hat hier jemand schonmal Erfahrungen mit Exponential Shadow Mapping gesammelt? Was kann ich hier tun, um die Weite der Schatten-Licht-Übergänge so breit zu bekommen, wie die Filtergröße es verspricht? Zur Vollständigkeit noch der Schatten-Code:
und beispielhaft der Quer-Filter, die lineare Variante. Die logarithmische Variante sieht mehr oder minder exakt gleich aus. Außer ich baue einen Multiplikator ein wie in den Beispielen im Netz, dann habe ich gar keinen Schatten mehr.
Danke im Voraus für eure Zeit!
Ich bin aktuell mal wieder dabei, die Splitterwelten zu optimieren. Gegen die CPU-Last wird demnächst hoffentlich ein Multicore-Renderer kommen, aber vorerst will ich noch die Schatten für gelegentlich auftretenden Stellen mit massivem Overdraw optimieren. Meine Wahl viel dabei auf Exponential Shadow Maps, da die einzige mir bekannte praktikable Alternative Variance Shadow Mapping bei den üblichen Tiefenkomplexitäten einer Splitterwelt mehr Light leaked als ein chaotisch-guter Paladin.
Also Exponential Shadow Mapping. Sieht auf den Bildern, die man im Netz dazu findet, wirklich gut aus. Aber nicht bei mir. Ich habe als Vergleich immernoch meine 32xPCF-Implementierung hier, die auch sauber gegen Selbstschattierung geschützt wurde. Aber ESM bleibt bei mir knochenhart, selbst mit 11x11 Box- oder Gauss-Weichzeichner auf der Shadow Map, logarithmisch oder linear. Es ist alles wurscht, ich kriege Penumbra Regions von bestenfalls einem Texel Breite. Folgende Bilder für maximale Aufmerksamkeitserhaschung:
ESM, ohne Filtering. Sieht größtenteils wie normales Shadow Mapping aus, aber man erkennt die Wirkung bei Kontaktschatten, z.B. beim Charakter. ESM, mit 11x11 Gauss-Filter. Schon besser, aber immernoch knochenhart. Erneuter Hinweis: exponentieller Filter sieht praktisch genauso aus, aber dazu später mehr. Referenzbild: 32 PCF-Samples emulieren einen ungefähr 5x5-Boxfilter. Man bemerke die trotzdem weiter reichenden Penumbra Regions. Jetzt meine Frage an die Experten: hat hier jemand schonmal Erfahrungen mit Exponential Shadow Mapping gesammelt? Was kann ich hier tun, um die Weite der Schatten-Licht-Übergänge so breit zu bekommen, wie die Filtergröße es verspricht? Zur Vollständigkeit noch der Schatten-Code:
Code: Alles auswählen
lichtStaerke *= saturate( exp( 10000.0f * (tex2D( TexSchatten, rein.mSchattenTexKoords.xy).x - rein.mLichtEntfernung)));
Code: Alles auswählen
float4 main( const PixelEingabe rein) : COLOR0
{
float tiefe;
// Mitte samplen als Startwert
tiefe = tex2D( TexBild, rein.mTexKoords).x * 0.24609375f;
// Texturkoords für die linken 5 Samples berechnen.
float2 t1 = saturate( rein.mTexKoords + float2( -5.0f, 0.0f) * gBild.zw);
float2 t2 = saturate( rein.mTexKoords + float2( -4.0f, 0.0f) * gBild.zw);
float2 t3 = saturate( rein.mTexKoords + float2( -3.0f, 0.0f) * gBild.zw);
float2 t4 = saturate( rein.mTexKoords + float2( -2.0f, 0.0f) * gBild.zw);
float2 t5 = saturate( rein.mTexKoords + float2( -1.0f, 0.0f) * gBild.zw);
// linke Seite in den Durchschnitt einfließen lassen
tiefe += tex2D( TexBild, t1).x * 0.0009765625f;
tiefe += tex2D( TexBild, t2).x * 0.009765625f;
tiefe += tex2D( TexBild, t3).x * 0.0439453125f;
tiefe += tex2D( TexBild, t4).x * 0.1171875f;
tiefe += tex2D( TexBild, t5).x * 0.205078125f;
// Texturkoords der rechten Seite
t1 = min( rein.mTexKoords + float2( 1.0f, 0.0f) * gBild.zw, gBild.xy);
t2 = min( rein.mTexKoords + float2( 2.0f, 0.0f) * gBild.zw, gBild.xy);
t3 = min( rein.mTexKoords + float2( 3.0f, 0.0f) * gBild.zw, gBild.xy);
t4 = min( rein.mTexKoords + float2( 4.0f, 0.0f) * gBild.zw, gBild.xy);
t5 = min( rein.mTexKoords + float2( 5.0f, 0.0f) * gBild.zw, gBild.xy);
// rechte Seite samplen
tiefe += tex2D( TexBild, t1).x * 0.205078125f;
tiefe += tex2D( TexBild, t2).x * 0.1171875f;
tiefe += tex2D( TexBild, t3).x * 0.0439453125f;
tiefe += tex2D( TexBild, t4).x * 0.009765625f;
tiefe += tex2D( TexBild, t5).x * 0.0009765625f;
return tiefe.xxxx;
}