Tiefenunschärfe: Unterschied zwischen den Versionen
Custom (Diskussion | Beiträge) K (→Was ist Tiefenunschärfe?) |
Custom (Diskussion | Beiträge) (→Eine theoretische Praxisübung: Daumenakrobatik.) |
||
Zeile 7: | Zeile 7: | ||
Die sogenannte Tiefenunschärfe ist ein Effekt, den zumindest aus der Realität jeder kennt, (und ich meine damit ausnahmslos jeder, der nicht gerade blind ist), dem aber kaum jemand große Beachtung schenkt. Das liegt hauptsächlich daran, dass es sich um einen Effekt handelt, der für das menschliche Auge (bzw. für die beiden menschlichen Augen) vollkommen alltäglich ist. | Die sogenannte Tiefenunschärfe ist ein Effekt, den zumindest aus der Realität jeder kennt, (und ich meine damit ausnahmslos jeder, der nicht gerade blind ist), dem aber kaum jemand große Beachtung schenkt. Das liegt hauptsächlich daran, dass es sich um einen Effekt handelt, der für das menschliche Auge (bzw. für die beiden menschlichen Augen) vollkommen alltäglich ist. | ||
=== Eine theoretische Praxisübung: Daumenakrobatik. === | === Eine theoretische Praxisübung: Daumenakrobatik. === | ||
− | Zur Demonstration nehme man seinen rechten Daumen (oder irgendeinen anderen Finger) und halte ihn vor einen größeren, entfernten Gegenstand wie zum Beispiel eine Wand. Dann fokussiere man mal den Daumen im Blickfeld. Was uns interessiert ist jetzt nicht, wie schön unser Daumen denn aussieht, sondern viel mehr was mit dem Hintergrund passiert. Er verschwimmt und solange wir unseren Daumen klar erkennen können, können wir den Hintergrund nur unscharf sehen. Anders herum können wir unseren Daumen nicht klar erkennen, während wir den Hintergrund fokussieren. Dieser Effekt verstärkt sich übrigens, je größer der Abstand von Daumen und Hintergrund ist. Das funktioniert übrigens auch seitlich. Man halte seine zwei Daumen auf eine Entfernung von etwa fünf Zentimetern nebeneinander, und versuche mal | + | Zur Demonstration nehme man seinen rechten Daumen (oder irgendeinen anderen Finger) und halte ihn vor einen größeren, entfernten Gegenstand wie zum Beispiel eine Wand. Dann fokussiere man mal den Daumen im Blickfeld. Was uns interessiert ist jetzt nicht, wie schön unser Daumen denn aussieht, sondern viel mehr was mit dem Hintergrund passiert. Er verschwimmt und solange wir unseren Daumen klar erkennen können, können wir den Hintergrund nur unscharf sehen. Anders herum können wir unseren Daumen nicht klar erkennen, während wir den Hintergrund fokussieren. Dieser Effekt verstärkt sich übrigens, je größer der Abstand von Daumen und Hintergrund ist. Das funktioniert übrigens auch seitlich. Man halte seine zwei Daumen auf eine Entfernung von etwa fünf Zentimetern nebeneinander, und versuche mal beide gleichzeitig zu fokussieren. |
=== Erkenntnis === | === Erkenntnis === |
Version vom 25. Dezember 2009, 01:16 Uhr
Inhaltsverzeichnis
Realität und Virtualität
Bevor wir zum Programmieren übergehen erstmal ein paar Kleinigkeiten vorweg. Wen diese nicht interessieren, kann den gesamten Punkt 1 eigentlich überspringen und direkt bei 2.1 anfangen. Ansonsten geht es los mit folgender Frage:
Was ist Tiefenunschärfe?
Die sogenannte Tiefenunschärfe ist ein Effekt, den zumindest aus der Realität jeder kennt, (und ich meine damit ausnahmslos jeder, der nicht gerade blind ist), dem aber kaum jemand große Beachtung schenkt. Das liegt hauptsächlich daran, dass es sich um einen Effekt handelt, der für das menschliche Auge (bzw. für die beiden menschlichen Augen) vollkommen alltäglich ist.
Eine theoretische Praxisübung: Daumenakrobatik.
Zur Demonstration nehme man seinen rechten Daumen (oder irgendeinen anderen Finger) und halte ihn vor einen größeren, entfernten Gegenstand wie zum Beispiel eine Wand. Dann fokussiere man mal den Daumen im Blickfeld. Was uns interessiert ist jetzt nicht, wie schön unser Daumen denn aussieht, sondern viel mehr was mit dem Hintergrund passiert. Er verschwimmt und solange wir unseren Daumen klar erkennen können, können wir den Hintergrund nur unscharf sehen. Anders herum können wir unseren Daumen nicht klar erkennen, während wir den Hintergrund fokussieren. Dieser Effekt verstärkt sich übrigens, je größer der Abstand von Daumen und Hintergrund ist. Das funktioniert übrigens auch seitlich. Man halte seine zwei Daumen auf eine Entfernung von etwa fünf Zentimetern nebeneinander, und versuche mal beide gleichzeitig zu fokussieren.
Erkenntnis
Nach spätestens einer Minute anstrengender Verrenkungen sollte man gemerkt haben, was der hauptsächliche Aspekt ist, um den es hier geht; Der Mensch ist nicht in der Lage, mehrere Bildpunkte gleichzeitig scharf darzustellen. (Kameras haben übrigens das selbe Problem)
Virtuelle Tiefenunschärfe
Jetzt erst einmal weg vom "RL". Tiefenunschärfe ist wie wir nun herausgefunden haben sollten kein Effekt, der unglaublich viel Spannung erzeugt wie zum Beispiel eine Explosion. Trotzdem bin ich der Meinung, dass sie in vielen Spielen fehlt und optisch gesehen einiges hermachen kann. Die fehlende Tiefenunschärfe ist oftmals der Grund, warum Computerspiele oder -Animationen sehr künstlich wirken. Warum? Ganz einfach: Ein jeder Mensch mit gesundem Geist, hält das was er gewohnt ist für realistisch, beziehungsweise reell. Er ist darauf spezialisiert, Muster zu erkennen und wieder zu erkennen. Auf unser Beispiel bezogen heißt das: wir sind es gewohnt, immer nur einen bestimmten Punkt in unserem Blickfeld scharf erkennen zu können. Da in Computerspielen häufig einfach alles gleichermaßen scharf gestellt wird, bemerkt unser Unterbewusstsein den Unterschied und stempelt das gesehene als künstlich ab. In der folgenden Anleitung versuche ich eine Technik zu erläutern, wie man Tiefenunschärfe ganz simpel in OpenGL umsetzen kann.
Programmierung
Die Technik, die ich hier erläutern möchte, ist wirklich sehr simpel und erfordert nicht all zu viel an Vorkenntnissen, ist aber auch nicht unbedingt für Anfänger geeignet, da man doch das eine oder andere falsch machen und somit die Performance in den Keller reißen kann. Eine Voraussetzung wäre eventuell glAccum, es ist aber nicht all zu schlimm, wenn man damit noch nicht gearbeitet hat.
Praktische Theorie
So. Jetzt geht's an's Programmieren. Womit fangen wir an? Richtig.
Die Grundidee
Wir wollen also jetzt einen Blureffekt, der abhängig vom Abstand zu einem bestimmten Punkt verschieden stark ist. Je weiter entfernt, umso stärker. Mit anderen Worten: Blurstärke ~ Radius. Nun wäre es reichlich sinnlos, für jeden Bildpunkt, der gezeichnet wird einen Radius zu berechnen und jeden Punkt einzeln zu blurren. Das wäre programmiertechnisch viel zu umständlich und würde der Performance wohl so stark in die Suppe spucken, dass diese eine zweite Schüssel bräuchte. Wir lösen das Problem, indem wir umdenken:
Rotation!
Die Idee ist so einfach wie gut. Nehmen wir zum Beispiel zwei Punkte auf einer Geraden. Dann rotieren wir die Gerade um den Ersten Punkt (rot) und den zweiten Punkt (grün) natürlich mit. der Abstand zwischen dem alten und dem neuen zweiten Punkt ist abhängig von Rotation und Abstand zum Ersten Punkt. Das klingt komplizierter als es eigentlich ist.
Umsetzung
Nun stellt sich die Frage: was bringt uns dieses Prinzip und wie setzen wir es in unserer 3D-Szenerie um? Es ist wirklich nicht all zu kompliziert. Wir wissen, dass es genau einen Punkt gibt, den wir scharf stellen wollen, der Rest soll abhängig vom Abstand verwischt dargestellt werden. Wie macht mal Blur? Genau. Man mischt nah aneinander liegende Farbwerte miteinander. Da wir das aber wie gesagt nicht per Pixel machen wollen, kommt jetzt die Idee mit der Rotation und der Accum Buffer ins Spiel. Die Idee ist es, die Szenerie mehrfach in den Accum Buffer zu Rendern und dabei jedes mal leicht um den Fokuspunkt zu rotieren.
Der erste Schritt wäre, den Accum Buffer vorzubereiten:
glClear(GL_ACCUM_BUFFER_BIT); //Accum Buffer leeren
Dann wäre es sinnvoll eine Variable für den Fokuspunkt anzulegen:
TVector3f = record
x,y,z: single;
end;
focus: tvector3f; //fokuspunkt
Als nächstes legen wir eine for-Schleife an, in der die Szene gerendert werden wird, und setzen an den Anfang das gewohnte freiräumen des Farb- und Tiefenbuffers.
for i := 0 to 3 do
begin
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
[...]
end;
Nun kommt in unsere Schleife der wichtigste Teil; Als erstes speichern wir die momentane Matrix ab. Dann führen wir die Rotation um unseren Fokusspunkt aus (wie die Rotation umgesetzt wird ist Geschmackssache), Rendern sie Szene, setzen die Matrix zurück und speichern das gerade Gerenderte Szenario im Accum Buffer ab.
glPushMatrix();
//leicht um den Fokuspunkt rotieren
if i <> 0 then
begin
glTranslatef(focus.x,focus.y,focus.z);
glRotatef(-Cam.rot.y,0,1,0);
if i = 1 then glRotatef(FoD_str,-1,0,0);
if i = 2 then glRotatef(FoD_str,0.5,-0.5,0);
if i = 3 then glRotatef(FoD_str,0.5,+0.5,0);
glRotatef(Cam.rot.y,0,1,0);
glTranslatef(-focus.x,-focus.y,-focus.z);
end;
Scene.Render; //Szenerie darstellen
glPopMatrix();
//zum Accum Buffer hinzufügen
glAccum (GL_ACCUM, 1/4);
Schlussendlich wollen wir unsere (im Beispiel) vier (man kann auch mehr oder weniger nehmen) gerenderten, übereinandergelegten Szenerien auch sehen können, also lassen wir das ganze ausgeben.
//In den Accum Buffer gerendertes zurückgeben
glAccum (GL_RETURN, 1.0);
glFlush();
Diese ganzen Bestandteile fügen wir in einen großen, prozeduralen Topf und rühren kräftig.
Endergebnis
Letztendlich sollten wir etwa so etwas heraus bekommen: (FoD_str = Blurstärke gesamt)
procedure Kochtopf; // ;)
var
i : integer;
focus: tvector3f; //fokuspunkt
begin
glPushMatrix();
//hier kann die Szenerie noch an die richtige Stelle verschoben werden,
//falls gewünscht.
glClear(GL_ACCUM_BUFFER_BIT); //Accum Buffer leeren
for i := 0 to 3 do
begin
if (i > 0) and (FoD_str = 0) then break; //falls die verwacklungsstärke = 0 ist, wird die szene nur einmal gerendert
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
glPushMatrix();
//leicht um den Fokuspunkt rotieren
if i <> 0 then
begin
glTranslatef(focus.x,focus.y,focus.z);
glRotatef(-Cam.rot.y,0,1,0);
if i = 1 then glRotatef(FoD_str,-1,0,0);
if i = 2 then glRotatef(FoD_str,0.5,-0.5,0);
if i = 3 then glRotatef(FoD_str,0.5,+0.5,0);
glRotatef(Cam.rot.y,0,1,0);
glTranslatef(-focus.x,-focus.y,-focus.z);
end;
Scene.Render; //Szenerie darstellen
glPopMatrix();
//zum Accum Buffer hinzufügen
if FoD_str = 0 then
glAccum (GL_ACCUM, 1)
else
glAccum (GL_ACCUM, 0.25);
end;
//In den Accum Buffer gerendertes zurückgeben
glAccum (GL_RETURN, 1.0);
glFlush();
glPopMatrix();
Achtung!
Bevor wir uns jetzt auf unseren Quellcode stürzen und diese einfache und schöne Änderung einbringen, sollten wir uns nochmal unseren Quellcode angucken oder ihn zumindest durchdenken; Die Scene.Render Prozedur wird im Beispiel vier mal aufgerufen, was zwei zu beachtende Aspekte bringt. Erstens:
Die Grafikkarte hat das vierfache an Arbeit zu tun. |
Zweitens:
(Ich habe diesen Fehler anfangs im Zusammenhang mit Octrees, Nodes und Frustum gemacht. Kein schöner Anblick für den Grafikfanatiker.)
Blurry End
Das war es eigentlich auch schon wieder, ihr könnt jetzt aufhören eure Daumen anzustarren. Rechts nochmal ein abschließendes Bild zur Erklärung. Ich hoffe, es ist klar geworden, wie einfach diese Technik anzuwenden ist. Außerdem hoffe ich auf Verbesserungsvorschläge, oder aber auch andere Methoden zur darstellung von Tiefenunschärfe, denn vollkommen ist sie nicht. In diesem Sinne: Unscharfen Abend noch.
MFG Custom.