Weiches Shadowmapping mittels Tiefenkegel-Projektion
Verfasst: 25.01.2010, 22:35
Hallo Leute,
ich möchte euch hier eine Idee vorstellen, die ich seit geraumer Weile mit mir rumtrage. Es geht darum, die bekannte Technik Shadow Mapping so zu erweitern, dass mit wenig zusätzlicher Rechenzeit der optische Eindruck von mit zunehmendem Abstand aufweichenden Schattenkanten erreicht wird. Ich habe dazu eine Idee entwickelt und einen kleinen Prototypen gebaut, der die Stärken und Schwächen der Technik präsentiert. Dazu aber gleich mehr - wir arbeiten natürlich ordentlich wissenschaftlich strukturiert.
Das Problem
In den bekannten Techniken zur Schattenvisualisierung werden Lichtquellen üblicherweise als Punkt angenähert. Das sorgt abgesehen von den üblichen Aliasing-Problemen von z.B. Shadow Mapping für pixel-scharfe Schattenkanten. Physikalisch betrachtet ist das aber nicht haltbar - jede Lichtquelle hat eine gewisse Ausdehnung. Die Menge an Licht, die an einem bestimmten Punkt von einer Lichtquelle aus ankommt, ist also keine binäre Funktion. Und das menschliche Sehzentrum benutzt unbewusst die Licht-Schattenübergänge, um zusätzliche Tiefeninformationen zu der betrachteten Szene zu gewinnen. Es scheint also erstrebenswert, die durch die Lichtquellenausdehnung entstehenden weichen Licht-Schatten-Übergänge in der Computergrafik nachzubilden.
Für eine wirklich korrekte Bestimmung des an einem Punkt empfangenen Lichtes einer ausgedehnten Lichtquelle wäre ein Flächenintegral über den Teil des Halbkugel-Horizonts eines Punkts notwendig, den die Lichtquelle aus Sicht des Punktes einnimmt.
Bisherige Arbeiten
Es gibt bereits einige Methoden, Schattenkanten aufzuweichen. Für Shadow Mapping ist die einfachste Methode das Percentage Closer Filtering (PCF), das schlicht über die binären Ergebnisse mehrerer Shadow Mapping Samples mittelt. Eine erweiterte Version davon ist NVidias Percentage Closer Soft Shadows, welche in einer Standard-ShadowMap zuerst mit maximalem Weichzeichner-Radius eine Hinderniss-Suche ausführt, um anhand der gefundenen Hindernisse und ihres durchschnittlichen Abstands dann den Radius des eigentlichen PCF-Kernels zu bestimmen. Die Technik funktioniert auf einer Standard-ShadowMap ohne jede Vorverarbeitung, benötigt aber sehr viele Samples aus der SM pro Bildschirmpixel, was die Technik vor allem bei hohen Auflösungen und mehrfachem Overdraw sehr teuer macht.
Es gibt viele weitere Techniken, die ich aber mangels akkurater Kenntnisse gerade nicht aufzählen kann. Ein Thread auf GameDev.net hat mal demonstriert, wie man bei Variance Shadow Mapping aus den beiden Werten den Abstand des Blockers gewinnen kann und dann durch gezielte Auswahl der MipMap sehr weiche Schatten erreichen kann - allerdings mit den für VSM üblichen Fehlbeleuchtungen (Light Bleeding). Es gab auch Versuche, das Integral im Pixelshader live nachzubilden - ich habe auch eine Version davon da, die aber abartig teuer ist, und es gibt Beiträge von richtigen Wissenschaftlern, die sich aber gerade meiner Googlung entziehen.
Ansatz
Ich möchte hier eine Methode vorstellen, die auf einer symptomatischen Herangehensweise basiert. Die Lichtquelle wird dazu als kreisförmig mit räumlich begrenztem Umfang angenommen. Bei dieser Annahme projiziert ein einzelner Punkt also eine Art Schattenkegel hinter sich in die Szene, welcher mit zunehmender Tiefe linear im Radius wächst und dabei indirekt proportional an "Dunkelheit" verliert. Für eine beliebige Stelle im Raum ist der Gesamtwert der Helligkeit von der Lichtquelle also das Integral über die kegelförmigen "Impulsantworten" (ich nenn die jetzt mal so) aller schattenwerfenden Elemente davor. Ein Querschnitt durch die Szene sähe dann so aus:
Die Idee ist jetzt, eine Funktion zu finden, die mit möglichst wenig Parametern den typischen Helligkeitsverlauf mit der Tiefe an Schattenkanten beschreibt. Mit einer solchen Funktion kann man beim Zeichnen der Szene dann die Lichthelligkeit einfach rekonstruieren und das eingangs genannte Flächenintegral könnte man also in den Shadow Map Space verschieben. Als Funktion wurde hier gewählt:
Helligkeit = Seitlicher Abstand von Hinderniskante / Tiefenabstand zum Hindernis
Es reichen zur Beschreibung also zwei Parameter: der seitliche Abstand zum nächsten schattenwerfenden Hindernis und der Tiefenabstand zur Tiefe dieses Hindernisses. Diese Parameter haben außerdem den Vorteil, dass sie sehr leicht aus einer ShadowMap zu bestimmen sind und dass die Bestimmung wie z.b. ein Gausscher Weichzeichner achsen-separierbar ist. Besonders letztere Eigenschaft ermöglicht es, die algorithmische Komplexität der Nachbearbeitung von O(n^2) auf O(n) zu senken. Die beiden Parameter sind außerdem freundlich interpolierbar, was die Ausnutzung der gängigen Textur-Interpolationsmöglichkeiten moderner GPUs ermöglicht.
--- Hier klafft übrigens noch eine Lücke in meiner Idee: zum Einen hat die Bestimmung der Kegel-Parameter als reine Suche nach dem nahesten Hindernis den unschönen Nebeneffekt, dass Schattenkegel von vorderen Hindernissen die schmaleren und damit dunkleren Kegel hinterer Hindernisse zu überdecken. Eine bessere Kombinationsfunktion als "wähle das naheste Hinderniss" wäre hilfreich, aber ich habe bisher keine passende gefunden. Und zum Anderen erzeugt eben diese Interpolation der Parameter einen unschönen Rand am äußersten Ende der Schattenkegel, den ich noch beseitigen muss.
Implementation
Zuerst wird eine normale Shadow Map mit den Tiefenwerten der Szene aus Sicht der Lichtquelle gefüllt. Dann wird in einem Nachbearbeitungsschritt aus diesen Tiefenwerten die Kegelparameter pro ShadowMap-Texel bestimmt, indem für jeden Texel der Abstand zum nächsten Hinderniss bestimmt wird, das auf den Texel einen Schattenkegel projiziert. Das kann als einzelner Pass implementiert werden, aber wirkliche Vorteile bringt die Technik erst, wenn man die Bestimmung der Kegelparameter als zwei separate Durchgänge für horizontale und vertikale Suche implementiert. Danach zeichnet man die Szene normal wie bei Verwendung einer normalen Shadow Map, nur dass man für den "Punkt ist im Licht"-Fall zusätzlich die Kegelfunktion auswertet.
Die Implementation beschränkt sich hier auf die "äußeren" Seiten der Schattenkegel. Theoretisch würde sich ein Kegel auf beiden Seiten des Hindernisses erstrecken, die Hälfte des Kegels wäre also innerhalb des Schattens. Allerdings benötigt man dann noch einen bzw. zwei weitere Parameter, um auch diesen Verlauf zu beschreiben. Und zum Anderen bekommt man damit die prinzipielle Probleme des ShadowMappings (keine Informationen über weitere Hindernisse jenseits des vordersten, schattenwerfenden Hindernisses) in die Szene. Das äußert sich dann durch Fehlbeleuchtung an Tiefenwert-Kanten, sehr ähnlich dem Light Bleeding von Variance Shadow Mapping. Ich werde daher auf die beidseitige Kegelprojektion nicht weiter eingehen. In der Praxis halte ich die Verwendung nur der äußeren Kegelseiten für hinreichend, um den gewünschten optischen Eindruck zu erreichen.
Ergebnisse
Die vorgeschlagene Kegelprojektionstechnik wurde in einem Prototypen implementiert. Dieser Prototyp implementiert einfaches Shadow Mapping, NVidias Percentage Closer Soft Shadow Mapping und die beschriebene Technik Depth Cone Shadow Mapping. Auch die beidseitige Variante ist implementiert, aber nicht gepflegt - die Ergebnisse davon sind definitiv unbrauchbar. Ich werde diese Variante evtl. noch bis zur Benutzbarkeit bearbeiten, aber das ist unsicher - aufgrund der prinzipiellen Probleme von Shadow Mapping wird die beidseitige Variante definitiv immer Light Bleeding-Artefakte enthalten, weswegen ich sie für den praktischen Gebrauch für untauglich halte.
Die vorgeschlagene Methode erreicht in der Praxis meist eine deutlich bessere Performance als NVidias PCSS. Sie hat besonders dann einen Vorteil, wenn die Schattenauflösung geringer als die Bildschirmauflösung ist. Im Prototypen ist das ab mittleren Fenstergrößen der Fall. Bei uns auf den Splitterwelten mit dem Bodenbewuchs, der vielfachen Overdraw verursacht, dürfte die Technik ihre Stärken deutlicher zeigen. Die Kegelparameter sind zudem sauber interpolierbar, der Helligkeitsverlauf an den Schattenkanten ist also frei von Banding-Artefakten.
Nachteile sind vor allem der begrenzte Radius der Schattenübergänge, der vor allem durch die Breite der Filter bei der ShadowMap-Nachbearbeitung bestimmt wird. Zudem kann die Technik mehr Rechenzeit als rein sample-basierende Methoden kosten, falls die ShadowMap gleiche oder höhere Auflösung als die Bildschirmauflösung hat. Zudem ist die Kombination der Filter-Ergebisse über eine einfache Auswahl des nahesten Hindernisses hinaus bislang noch ein ungelöstes Problem.
Diskussion
Mich würde jetzt interessieren, was ihr davon haltet. Die Methode hat noch ihre Macken, aber ich denke, dass der Ansatz vielversprechend ist. Besonders die Verlagerung der Rechenzeit hin zur Shadow Map klingt für mich sehr reizvoll, da wir wie gesagt durch unseren dichten Bodenbewuchs mit vielfachem Overdraw zu kämpfen haben. Depth Cone Shadow Mapping leistet in diesem Szenario gute Dienste, da es beim Rendern der Szene gegenüber normalem Shadow Mapping kaum zusätzliche Rechenzeit frisst - ein zusätzlicher Texturzugriff und etwa 5 Mathe-Ops. Zudem halte ich die absolute Freiheit von Fehlbelichtungs-Artefakten für sehr wichtig - aus diesem Grund habe ich z.B. nie Variance Shadow Mapping benutzt.
Bye, Thomas
ich möchte euch hier eine Idee vorstellen, die ich seit geraumer Weile mit mir rumtrage. Es geht darum, die bekannte Technik Shadow Mapping so zu erweitern, dass mit wenig zusätzlicher Rechenzeit der optische Eindruck von mit zunehmendem Abstand aufweichenden Schattenkanten erreicht wird. Ich habe dazu eine Idee entwickelt und einen kleinen Prototypen gebaut, der die Stärken und Schwächen der Technik präsentiert. Dazu aber gleich mehr - wir arbeiten natürlich ordentlich wissenschaftlich strukturiert.
Das Problem
In den bekannten Techniken zur Schattenvisualisierung werden Lichtquellen üblicherweise als Punkt angenähert. Das sorgt abgesehen von den üblichen Aliasing-Problemen von z.B. Shadow Mapping für pixel-scharfe Schattenkanten. Physikalisch betrachtet ist das aber nicht haltbar - jede Lichtquelle hat eine gewisse Ausdehnung. Die Menge an Licht, die an einem bestimmten Punkt von einer Lichtquelle aus ankommt, ist also keine binäre Funktion. Und das menschliche Sehzentrum benutzt unbewusst die Licht-Schattenübergänge, um zusätzliche Tiefeninformationen zu der betrachteten Szene zu gewinnen. Es scheint also erstrebenswert, die durch die Lichtquellenausdehnung entstehenden weichen Licht-Schatten-Übergänge in der Computergrafik nachzubilden.
Für eine wirklich korrekte Bestimmung des an einem Punkt empfangenen Lichtes einer ausgedehnten Lichtquelle wäre ein Flächenintegral über den Teil des Halbkugel-Horizonts eines Punkts notwendig, den die Lichtquelle aus Sicht des Punktes einnimmt.
Bisherige Arbeiten
Es gibt bereits einige Methoden, Schattenkanten aufzuweichen. Für Shadow Mapping ist die einfachste Methode das Percentage Closer Filtering (PCF), das schlicht über die binären Ergebnisse mehrerer Shadow Mapping Samples mittelt. Eine erweiterte Version davon ist NVidias Percentage Closer Soft Shadows, welche in einer Standard-ShadowMap zuerst mit maximalem Weichzeichner-Radius eine Hinderniss-Suche ausführt, um anhand der gefundenen Hindernisse und ihres durchschnittlichen Abstands dann den Radius des eigentlichen PCF-Kernels zu bestimmen. Die Technik funktioniert auf einer Standard-ShadowMap ohne jede Vorverarbeitung, benötigt aber sehr viele Samples aus der SM pro Bildschirmpixel, was die Technik vor allem bei hohen Auflösungen und mehrfachem Overdraw sehr teuer macht.
Es gibt viele weitere Techniken, die ich aber mangels akkurater Kenntnisse gerade nicht aufzählen kann. Ein Thread auf GameDev.net hat mal demonstriert, wie man bei Variance Shadow Mapping aus den beiden Werten den Abstand des Blockers gewinnen kann und dann durch gezielte Auswahl der MipMap sehr weiche Schatten erreichen kann - allerdings mit den für VSM üblichen Fehlbeleuchtungen (Light Bleeding). Es gab auch Versuche, das Integral im Pixelshader live nachzubilden - ich habe auch eine Version davon da, die aber abartig teuer ist, und es gibt Beiträge von richtigen Wissenschaftlern, die sich aber gerade meiner Googlung entziehen.
Ansatz
Ich möchte hier eine Methode vorstellen, die auf einer symptomatischen Herangehensweise basiert. Die Lichtquelle wird dazu als kreisförmig mit räumlich begrenztem Umfang angenommen. Bei dieser Annahme projiziert ein einzelner Punkt also eine Art Schattenkegel hinter sich in die Szene, welcher mit zunehmender Tiefe linear im Radius wächst und dabei indirekt proportional an "Dunkelheit" verliert. Für eine beliebige Stelle im Raum ist der Gesamtwert der Helligkeit von der Lichtquelle also das Integral über die kegelförmigen "Impulsantworten" (ich nenn die jetzt mal so) aller schattenwerfenden Elemente davor. Ein Querschnitt durch die Szene sähe dann so aus:
Die Idee ist jetzt, eine Funktion zu finden, die mit möglichst wenig Parametern den typischen Helligkeitsverlauf mit der Tiefe an Schattenkanten beschreibt. Mit einer solchen Funktion kann man beim Zeichnen der Szene dann die Lichthelligkeit einfach rekonstruieren und das eingangs genannte Flächenintegral könnte man also in den Shadow Map Space verschieben. Als Funktion wurde hier gewählt:
Helligkeit = Seitlicher Abstand von Hinderniskante / Tiefenabstand zum Hindernis
Es reichen zur Beschreibung also zwei Parameter: der seitliche Abstand zum nächsten schattenwerfenden Hindernis und der Tiefenabstand zur Tiefe dieses Hindernisses. Diese Parameter haben außerdem den Vorteil, dass sie sehr leicht aus einer ShadowMap zu bestimmen sind und dass die Bestimmung wie z.b. ein Gausscher Weichzeichner achsen-separierbar ist. Besonders letztere Eigenschaft ermöglicht es, die algorithmische Komplexität der Nachbearbeitung von O(n^2) auf O(n) zu senken. Die beiden Parameter sind außerdem freundlich interpolierbar, was die Ausnutzung der gängigen Textur-Interpolationsmöglichkeiten moderner GPUs ermöglicht.
--- Hier klafft übrigens noch eine Lücke in meiner Idee: zum Einen hat die Bestimmung der Kegel-Parameter als reine Suche nach dem nahesten Hindernis den unschönen Nebeneffekt, dass Schattenkegel von vorderen Hindernissen die schmaleren und damit dunkleren Kegel hinterer Hindernisse zu überdecken. Eine bessere Kombinationsfunktion als "wähle das naheste Hinderniss" wäre hilfreich, aber ich habe bisher keine passende gefunden. Und zum Anderen erzeugt eben diese Interpolation der Parameter einen unschönen Rand am äußersten Ende der Schattenkegel, den ich noch beseitigen muss.
Implementation
Zuerst wird eine normale Shadow Map mit den Tiefenwerten der Szene aus Sicht der Lichtquelle gefüllt. Dann wird in einem Nachbearbeitungsschritt aus diesen Tiefenwerten die Kegelparameter pro ShadowMap-Texel bestimmt, indem für jeden Texel der Abstand zum nächsten Hinderniss bestimmt wird, das auf den Texel einen Schattenkegel projiziert. Das kann als einzelner Pass implementiert werden, aber wirkliche Vorteile bringt die Technik erst, wenn man die Bestimmung der Kegelparameter als zwei separate Durchgänge für horizontale und vertikale Suche implementiert. Danach zeichnet man die Szene normal wie bei Verwendung einer normalen Shadow Map, nur dass man für den "Punkt ist im Licht"-Fall zusätzlich die Kegelfunktion auswertet.
Die Implementation beschränkt sich hier auf die "äußeren" Seiten der Schattenkegel. Theoretisch würde sich ein Kegel auf beiden Seiten des Hindernisses erstrecken, die Hälfte des Kegels wäre also innerhalb des Schattens. Allerdings benötigt man dann noch einen bzw. zwei weitere Parameter, um auch diesen Verlauf zu beschreiben. Und zum Anderen bekommt man damit die prinzipielle Probleme des ShadowMappings (keine Informationen über weitere Hindernisse jenseits des vordersten, schattenwerfenden Hindernisses) in die Szene. Das äußert sich dann durch Fehlbeleuchtung an Tiefenwert-Kanten, sehr ähnlich dem Light Bleeding von Variance Shadow Mapping. Ich werde daher auf die beidseitige Kegelprojektion nicht weiter eingehen. In der Praxis halte ich die Verwendung nur der äußeren Kegelseiten für hinreichend, um den gewünschten optischen Eindruck zu erreichen.
Ergebnisse
Die vorgeschlagene Kegelprojektionstechnik wurde in einem Prototypen implementiert. Dieser Prototyp implementiert einfaches Shadow Mapping, NVidias Percentage Closer Soft Shadow Mapping und die beschriebene Technik Depth Cone Shadow Mapping. Auch die beidseitige Variante ist implementiert, aber nicht gepflegt - die Ergebnisse davon sind definitiv unbrauchbar. Ich werde diese Variante evtl. noch bis zur Benutzbarkeit bearbeiten, aber das ist unsicher - aufgrund der prinzipiellen Probleme von Shadow Mapping wird die beidseitige Variante definitiv immer Light Bleeding-Artefakte enthalten, weswegen ich sie für den praktischen Gebrauch für untauglich halte.
Die vorgeschlagene Methode erreicht in der Praxis meist eine deutlich bessere Performance als NVidias PCSS. Sie hat besonders dann einen Vorteil, wenn die Schattenauflösung geringer als die Bildschirmauflösung ist. Im Prototypen ist das ab mittleren Fenstergrößen der Fall. Bei uns auf den Splitterwelten mit dem Bodenbewuchs, der vielfachen Overdraw verursacht, dürfte die Technik ihre Stärken deutlicher zeigen. Die Kegelparameter sind zudem sauber interpolierbar, der Helligkeitsverlauf an den Schattenkanten ist also frei von Banding-Artefakten.
Nachteile sind vor allem der begrenzte Radius der Schattenübergänge, der vor allem durch die Breite der Filter bei der ShadowMap-Nachbearbeitung bestimmt wird. Zudem kann die Technik mehr Rechenzeit als rein sample-basierende Methoden kosten, falls die ShadowMap gleiche oder höhere Auflösung als die Bildschirmauflösung hat. Zudem ist die Kombination der Filter-Ergebisse über eine einfache Auswahl des nahesten Hindernisses hinaus bislang noch ein ungelöstes Problem.
Diskussion
Mich würde jetzt interessieren, was ihr davon haltet. Die Methode hat noch ihre Macken, aber ich denke, dass der Ansatz vielversprechend ist. Besonders die Verlagerung der Rechenzeit hin zur Shadow Map klingt für mich sehr reizvoll, da wir wie gesagt durch unseren dichten Bodenbewuchs mit vielfachem Overdraw zu kämpfen haben. Depth Cone Shadow Mapping leistet in diesem Szenario gute Dienste, da es beim Rendern der Szene gegenüber normalem Shadow Mapping kaum zusätzliche Rechenzeit frisst - ein zusätzlicher Texturzugriff und etwa 5 Mathe-Ops. Zudem halte ich die absolute Freiheit von Fehlbelichtungs-Artefakten für sehr wichtig - aus diesem Grund habe ich z.B. nie Variance Shadow Mapping benutzt.
Bye, Thomas