Tiefenunschärfe: Unterschied zwischen den Versionen
Custom (Diskussion | Beiträge) |
Custom (Diskussion | Beiträge) K (→Blurry End) |
||
(10 dazwischenliegende Versionen von 2 Benutzern werden nicht angezeigt) | |||
Zeile 1: | Zeile 1: | ||
= Realität und Virtualität = | = 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: | + | Bevor wir zum Programmieren übergehen erstmal ein paar Kleinigkeiten vorweg. Wen diese nicht interessieren, der kann den gesamten Punkt 1 eigentlich überspringen und direkt bei 2.1 anfangen. Ansonsten geht es los mit folgender Frage: |
== Was ist Tiefenunschärfe? == | == Was ist Tiefenunschärfe? == | ||
[[Bild:Schach.jpg|thumb|right|Beispiel aus der Realität anhand von Schachfiguren]] | [[Bild:Schach.jpg|thumb|right|Beispiel aus der Realität anhand von Schachfiguren]] | ||
− | Die sogenannte Tiefenunschärfe ist ein Effekt, den | + | 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 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 | + | 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, 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 === | ||
− | 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 | + | Nach spätestens einer Minute anstrengender Verrenkungen der Augen und Finger, 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 zu fokussieren.''' (Kameras haben übrigens das selbe Problem) |
== Virtuelle Tiefenunschärfe == | == 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 | + | 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 ohne jegliche Shader in OpenGL umsetzen kann. |
= Programmierung = | = Programmierung = | ||
Zeile 25: | Zeile 25: | ||
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. | 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 === | === 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 | + | 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 man Blur? Genau. Man mischt nah aneinander liegende Farbwerte miteinander. Da wir das aber wie gesagt nicht per Pixel machen wollen (wegen der Suppenschüsseln), 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 einen Fokuspunkt zu rotieren. |
Der erste Schritt wäre, den Accum Buffer vorzubereiten: | Der erste Schritt wäre, den Accum Buffer vorzubereiten: | ||
Zeile 51: | Zeile 51: | ||
</source> | </source> | ||
− | 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 | + | 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 die Szene, setzen die Matrix zurück und speichern das gerade Gerenderte Szenario im Accum Buffer ab. |
<source lang="cpp"> | <source lang="cpp"> | ||
glPushMatrix(); | glPushMatrix(); | ||
Zeile 91: | Zeile 91: | ||
focus: tvector3f; //fokuspunkt | focus: tvector3f; //fokuspunkt | ||
begin | begin | ||
− | glPushMatrix(); | + | glPushMatrix(); |
//hier kann die Szenerie noch an die richtige Stelle verschoben werden, | //hier kann die Szenerie noch an die richtige Stelle verschoben werden, | ||
Zeile 131: | Zeile 131: | ||
glPopMatrix(); | glPopMatrix(); | ||
+ | end; | ||
</source> | </source> | ||
Zeile 138: | Zeile 139: | ||
Zweitens: {{Warnung|Berechnungen, wie zum beispiel das Bewegen von Spielfiguren dürfen auf gar keinen Fall innerhalb der Scene.Render Prozedur stattfinden! Das würde dazu führen, dass die Berechnungen vier mal statt einmal ausgeführt werden würden! }} | Zweitens: {{Warnung|Berechnungen, wie zum beispiel das Bewegen von Spielfiguren dürfen auf gar keinen Fall innerhalb der Scene.Render Prozedur stattfinden! Das würde dazu führen, dass die Berechnungen vier mal statt einmal ausgeführt werden würden! }} | ||
(Ich habe diesen Fehler anfangs im Zusammenhang mit Octrees, Nodes und Frustum gemacht. Kein schöner Anblick für den Grafikfanatiker.) | (Ich habe diesen Fehler anfangs im Zusammenhang mit Octrees, Nodes und Frustum gemacht. Kein schöner Anblick für den Grafikfanatiker.) | ||
− | [[Bild:TiefenunschaerfeBeispiel.png|thumb|right|Beispiel zur | + | [[Bild:TiefenunschaerfeBeispiel.png|thumb|right|Beispiel zur Tiefenunschärfe]] |
+ | |||
= Blurry End = | = 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. | + | 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 [[Benutzer:Custom|Andre M. Fuchs alias Custom]]. | ||
+ | |||
+ | [[Kategorie:Anleitung]] [[Kategorie:Technik_oder_Algorithmus]] |
Aktuelle Version vom 25. Dezember 2009, 15:50 Uhr
Inhaltsverzeichnis
Realität und Virtualität
Bevor wir zum Programmieren übergehen erstmal ein paar Kleinigkeiten vorweg. Wen diese nicht interessieren, der 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 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, 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 der Augen und Finger, 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 zu fokussieren. (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 ohne jegliche Shader 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 man Blur? Genau. Man mischt nah aneinander liegende Farbwerte miteinander. Da wir das aber wie gesagt nicht per Pixel machen wollen (wegen der Suppenschüsseln), 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 einen 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 die 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();
end;
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.