Tiefenunschärfe: Unterschied zwischen den Versionen

Aus DGL Wiki
Wechseln zu: Navigation, Suche
K (hat „tiefenunschärfe“ nach „Tiefenunschärfe“ verschoben: Korrekte Großschreibung)
(kein Unterschied)

Version vom 25. Dezember 2009, 00:36 Uhr

Hinweis: Dieser Artikel wird gerade Offline bearbeitet!

Bitte haben Sie etwas Geduld und nehmen Sie keine Änderungen vor, bis der Artikel hochgeladen wurde.

(weitere Artikel)
WIP Offline.jpg

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 zu mindest 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.

Beispiel aus der Realität anhand von Schachfiguren

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 und den zweiten Punkt 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.
Hinweis: Diesem Artikel sollten folgende Bilder beigefügt werden:

(Mehr Informationen/weitere Artikel)
Bildwunsch.jpg
Entfernung der beiden zweiten Punkte in Abhängigkeit zum Radius

Wie wir das ganze ins dreidimensionale umsetzen können, erkläre ich jetzt.

Umsetzung

Endergebnis

Letztendlich sollten wir etwa so etwas heraus bekommen: (FoD_det = Genauigkeit, FoD_str = Blurstärke gesamt)

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 FoD_det 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();

    //verwackeln
    if i <> 0 then
    begin
      glTranslatef(Cam.focus.x,Cam.focus.y,Cam.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(-Cam.focus.x,-Cam.focus.y,-Cam.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, 1/(FoD_det+1));
  end;
  //In den Accum Buffer gerendertes zurückgeben
  glAccum (GL_RETURN, 1.0);
  glFlush();

  glPopMatrix();