RENDERING

Einleitung

Achtung diese Seite ist überholt! Ich benutze inzwischen einen anderen Algorithmus. Diese Seite hat daher nur noch historischen Wert!

Ich beschreibe hier einen Rendering-Algorithmus, den ich entwickelt habe - oder besser gesagt: den ich mit der Zeit immer weiter ausgebaut habe. "Rendering" meint hier das Erzeugen eines (zweidimensionalen) Bildes, das eine dreidimensionale Szene zeigt. Dieser Algorithmus ist nicht gerade das Non-Plus-Ultra auf dem Gebiet. Da ich aber schon einige Male danach gefragt wurde, wie, bzw. mit welchem Programm denn die Fraktal- oder auch Bezier-Bilder auf meinen Seiten entstanden sind und ich eine "Doku" in dieser Form auch für mich selbst immer hilfreich finde, habe ich mich mal an die Beschreibung gemacht. Dienen soll es (neben mir selbst, wenn ich mich mal wieder frage, wie ich dieses oder jenes eigentlich gemacht habe) Leuten, die wie ich, "mal eben" 3D-Objekte selbst visualisieren wollen. Hier sind neben einem relativ einfachen Ansatz auch einige Fallen erwähnt, an denen ich lange herumgeknobelt habe. Wer bestimmte Nachteile vermeiden will, die "mein" Ansatz prinzipiell mit sich herumschleppt, wird diese hier auch beschrieben finden. Man kann sich dann überlegen, ob man diese nicht besser von Anfang an vermeiden möchte...


Generelle Merkmale

Der im Folgenden erläuterte Algorithmus zeichnet sich durch folgende Features und Nicht-Features aus:
  • Im Vergleich zu dem was Echtzeitbibliotheken (z.B. das von mir auch eingesetzte Java3D) so zu leisten im Stande, sind ist mein Renderingalgorithmus furchtbar laaaaangsaaaaam.

  • Dafür berechnet er den Schattenwurf, was sehr zum räumlichen Eindruck der Darstellung beiträgt (das kann Java3D z.B. nicht :-).

  • Schatten werden jedoch nur von sichtbaren Objekten / Objektteilen geworfen (dazu später mehr). Im Beispiel unten links ist der Effekt gut zu sehen: Das Licht scheint von oben rechts durch den blauen Zylinder hindurch, weil die Rückwand des Zylinders verdeckt und damit für den Algorithmus nicht vorhanden ist. Unten rechts tritt das Problem nicht mehr auf, weil der Zylinder oben geschlossen ist (rote Kappe).
    falscher Schattenwurf
    korrekter Schattenwurf
    (Falscher) Schattenwurf
     
    (Korrekter) Schattenwurf

  • Zudem ist derzeit auch nur eine Lichtquelle vorgesehen, die ihre "Lichtstrahlen" parallel auf die Szenerie wirft - also quasi eine unendlich weit entfernte Lichquelle. Dass es nur eine Lichtquelle gibt ist aber keine prinzipielle Einschränkung, jedoch würden weitere Lichquellen entsprechend mehr Rechenzeit erfordern (für zwei Lichtquellen verdoppelt sich der Rechenaufwand annähernd...).

  • Für das Rendern von Fraktalen hat der Ansatz den Vorteil, dass er direkt auf (Raum-)Pixeln (manchmal auch "Voxel" genannt) arbeitet (ein Raumpixel ist durch seine X- und Y- und zusätzlich durch seine Z-Koordinate bestimmt). Eine Umrechnung in irgendeine Art von Flächen (z.B. Dreiecksflächen) wird nicht benötigt. Umgekehrt müssen Objekte die sich z.B. aus Bezier-Flächen zusammensetzen auch erst in eine Menge von Raumpixeln überführt werden. Ein Nachteil, insbesondere auch für perspektivische Darstellungen ist, dass so eine Repräsentation durch Raumpixel nicht mehr so ohne weiteres (größer) skalierbar ist (man kennt den Effekt von normalen 2D pixelbasierten Grafikformaten, wie Jpeg, PNG, Gif, Tiff, BMP usw. - im Gegensatz zu vektororientierten Formaten, wie z.B. SVG, die ohne Einbußen beliebig  vekleinerbar und vergrößerbar sind).

  • Auch die Perspektive ist eine sogenannte "Parallelperspektive" (man kennt das evtl. noch aus dem Kunstunterricht). Das hat unter anderem zur Folge, dass die Größe in der ein Objekt / Objektteil dargestellt wird nicht von der Entfernung zum Betrachter abhängt. Ich verwende den Begriff "Sichtebene" für eine angenommene Projektionsfläche auf der schließlich das gerenderte 2D-Abbild der Szene entsteht. Da die Abbildung mit parallelen "Strahlen" erfolgt, ist es vollkommen egal, an welcher Z-Position dieses Sichtebene angenommen wird (es findet dadurch keine Größenveränderung des projezierten Abbilds statt).
    Parallele Abbildung
    Parallelperspektivische Abbildung:
    Die 3-dimensionale Szene wird mit parallelen "Strahlen" auf eine "Sichtebene" geworfen um ein 2-dimensionales Abbild zu erhalten

  • Eine Möglichkeit einer "perspektivischen Verzerrung", um den Eindruck einer "Fluchpunktperspektive" zu erzeugen, ist inzwischen auch eingebaut,  ist aber mehr ein Trick und hat gewisse Nachteile. Zudem ist dies eine Vorverarbeitung, bzw. gehört zu der Art und Weise wie man die Daten erzeugt, die dann dem Renderer zum "Fraß" vorgeworfen werden - es wird nämlich praktisch das Objekt selbst und nicht nur die Perspektive "verzerrt". Diese Variante hat einige (unerwünschte) Auswirkungen auf Schatten, Reflektionen und Spiegelungen. Unten sind die normale Parallelperspektive, die hier verwendete Art der Fluchtpunktperspektive und zum Vergleich, ein ähnliches, mit Java3D gerendertes Bild zu sehen. Zu erkennen ist, dass die Verzerrungen soweit richtig aussehen, jedoch sind die Schatten, trotz gleichen Lichteinfallswinkels, gegenüber der parallelperspktivischen Darstellung verändert (leider kann ja Java3D keine Schatten, so dass man es damit nicht vergleichen kann).
    Parllelperspektive
    Fluchtpunktperpektive
    Fluchtpunktperpektive mit Java3D
    Parallelperspektive
     
    Fluchtpunktperpektive (gleicher Lichteinfallswinkel wie links)
     
    Zum Vergleich: ähnliche Perspektive mit Java3D

    Dass schon die Szenenbeschreibung selbst verzerrt erstellt wird und nicht erst die Abbildung der Szene, hat zwei wesentliche Gründe:
    1. den oben bereits angedeuteten Grund, dass eine einmal erstellte Szenenbeschreibung aus "Raumpixeln" nicht ohne Qualitätseinußen vergrößerbar ist. Durch die perspektivische Verzerrung erscheinen aber Teile der Szene größer (im Beispiel oben ist z.B. die Öffnung des Zylinders deutlich größer als in der Parallelperspektive, obwohl das Bild insgesamt etwa die gleiche Größe hat).
    2. werden, in einer perspektivischen Darstellung, Objekte und Objektteile sichtbar, die in einer Parallelperspektive überhauptnicht sichtbar sind. Wie schon beim Schattenwurf erwähnt und wie später noch genauer erläutert wird, werden aber zur Zeit nur sichtbare Teile in die Beschreibung der Szene aufgenommen. Unten ist ein Beispiel dafür zu sehen - die Innenseiten der beiden Säulen sind nur in der Fluchtpunktperpektive sichtbar, während man in der Parallelperspektive nur die Vorderseiten sieht.
      Parllelperspektive
      Fluchtpunktperpektive
      Fluchtpunktperpektive mit Java3D
      Parallelperspektive
      (nur die Vorderseiten der Säulen sind sichtbar)
       
      Fluchtpunktperpektive
      (die Innenseiten der Säulen sind sichtbar)
       
      Nochmal eine ähnliche Perspektive mit Java3D

  • Wie aus der Fotografie bekannt, trägt auch die Tiefenschärfe, bzw. oft eher der Mangel an Tiefenschärfe, sehr zum räumlichen Eindruck bei. Die Schärfe eines Punktes ist dabei abhängig davon, wie weit der Punkt von einer zu definierenden "Schärfeebene" entfernt ist. Bei mir wird zu diesem Zweck auf jeden Punkt ein Gausscher Weichzeichner mit entsprechend angepasstem Radius angewendet. Allerdings ist die Berechnung dafür sehr langsam, da ja jeder (nicht scharfe) Punkt über einen umliegenden Bereich von Nachbarpunkten "verschmiert" werden muss. Meine derzeitige Implementierung verbrät darüber hinaus viel Speicher, aber da gäbe es noch Einsparpotenziale. Das Beispiel unten zeigt Bilder mit unterschiedlicher Tiefenschärfe, wobei die Objekte zusätzlich mit einer Textur überzogen wurden, um den Effekt noch deutlicher zu machen:
    Bild mit geringer Tiefenschärfe
    Bild mittlerer Tiefenschärfe
    Bild ohne Unschärfen
    Bild mit geringer Tiefenschärfe
     
    Bild mit mittlerer Tiefenschärfe  
    Zum Vergleich dasselbe Bild ohne Unschärfen

  • Eine Blauverschiebung und Dunst können hinzugefügt werden. Der Dunst ist dabei ein tiefenabhängiges Aufhellen (Weißverschiebung). Bei der Blauverscheibung werden Teile von Rot und Grün zu Blau - je weiter ein Punkt weg ist desto stärker.
    Blauverschiebung und Dunst
    Blauverschiebung und Dunst
    Zusätzlich zur Tiefenunschärfe ist hier eine tiefenabhängige Weiß- und Blauverschiebung benutzt worden
     
    ...und das Ganze nochmal mit Hintegrund (aber weniger Unschärfe, Blau- und Weißverschiebung)
  • Texturen sind übrigens nicht Teil des Renderings und werden daher hier nicht weiter erläutert. Da aber für jedes Pixel eine eigene Farbe bestimmt werden kann, können natürlich prinzipiell auch Texturen über ein Objekt gelegt werden. Wie das passiert liegt aber vollständig in der Hand der die Szenenbeschreibung erstellenden Funktionen und hat mit dem eigentlichen Rendering daher nichts zu tun.

  • Spiegelungen können ebenfalls berechnet werden, allerdings werden nur Bilder gespiegelt, die sich z.B. auf den sechs Seiten einer die Szene virtuell umgebenden Box befinden (andere Formen, wie Kugeln, Zylinder, etc. sind auch denkbar). Spiegelungen der Objekte untereinander sind nicht möglich. Damit wird bei der Spiegelung auch nicht berücksichtigt, dass ein zu spiegelndes Bild evtl. durch ein anderes Objekt verdeckt ist und somit eigentlich dieses Objekt zu spiegeln wäre. Die Spiegelung ist bei mir ein Vorverarbeitungsschritt, bei dem die Farbe eines Objektpunktes entsprechend angepasst wird und damit eigentlich auch nicht Teil des Renderings.
    Spiegelungen einer umgebenden Box
    Spiegelung an einer umgebenden Box
    Drei Seiten einer ein Objekt umgebenden "Spiegelungs-Box"
     
    Gerendertes Objekt mit Spiegelungen
Generell würde ich heute einiges anders machen. Man muss aber berücksichtigen, dass ich eigentlich ja "nur mal schnell" meine 3D-Fraktale irgendwie nicht nur berechnen, sondern auch ansehen können wollte. Da war zwar schnell was hingeschlunzt, aber so richtig befriedigend war das dann noch nicht ...also weitergetüftelt. So kam dann am Ende der hier beschriebene Algorithmus raus, der eben noch einige "Erbkrankheiten" mit sich herumschleppt.

Richtig "chic", aber auch um einiges aufwändiger, wäre natürlich ein "echter" Ray-Tracer gewesen. Beim Ray-Tracing werden, wie der Name schon andeutet, der Weg virtueller Lichtstrahlen, zwischen den Lichtquellen und dem virtuellen Auge verfolgt, womit prinzipiell beliebige Spiegelungen, Schattenwürfe, etc. berechenbar sind (aus Effizienzgründen werden allerdings normalwerweise einige Einschränkungen gemacht)1).  Zum Thema Ray-Tracing kann aber an anderer Stelle mehr nachgelesen werden.


Szenen- / Objektbeschreibung

Generell arbeitet der Algorithmus mit drei bzw. vier Matritzen, deren Höhe (Anzahl der Werte in X-Richtung) und Breite (Anzahl der Werte in Y-Richtung) der Höhe und Breite des zu erstellenden Bildes entspricht2):
  • Die erste Matrix ist der sogenannte Z-Buffer, der für jedes Pixel die Entfernung zum Betrachter enthält. Genauer muss man von der Entfernung zur Sichtebene sprechen. Die Sichtebene wird dabei, der Einfachheit halber, an Position z=0 angenommen. Die Beschränkung auf einen Z-Wert für jedes Element dieser Matrix hat mehrere Nachteile - so ist es damit z.B. nicht möglich mehrere teiltransparente Ebenen darzustellen. Es ist also nicht möglich, dass man hinter einer Oberfläche noch weitere Objekt(-teile) durchschimmern sieht. Außerdem liegt hier der Grund für die oben schon kurz erwähnte Einschränkung, dass nur sichtbare Objektteile Schatten werfen können, denn von anderen Objekteilen verdeckte Bereiche sind in dem zugrundeliegendem Z-Buffer schlicht nicht repräsentiert. In Wirklichkeit werfen natürlich auch verdeckte Objekte (sichtbare) Schatten. Man stelle sich einen Reihe von seitlich beleuchteten Säulen vor, wobei man nur die vorderste Säule sieht und alle anderen von dieser verdeckt sind. Diese Nachteile könnten z.B. dadurch überwunden werden, dass jedes Element der Matrix statt eines einzigen Wertes eine Liste beliebig vieler Z-Werte speichert. Um den Speicherbedarf und Rechenaufwand möglichst klein zu halten würde man hier zweckmäßiger Weise nur solche Werte abspeichern, die an der Obefläche eines Objekts liegen. Außerdem könnte es sinnvoll sein die Z-Werte einer solchen Liste zu sortieren (von "vorne" nach "hinten", oder umgekehrt). Diese Erweiterung hat - trotz möglicher Optimierungen - den Nachteil den Speicherbedarf sowie den Rechenaufwand enorm in die Höhe zu treiben.
    Z-Buffer
    Z-Buffer: Z-Werte (Entfernung) für alle Pixel (hellblaue Pixel sind nah, rote Pixel weit entfernt, dunkelblaue Pixel sind "leer")

  • Die zweite Matrix nenne ich im Folgenden Normalenmatrix - sie enthält für jeden Bildpunkt einen Normalenvektor, der die Orientierung der Oberfläche an diesem Punkt angibt. Dieser Wert kann auch aus dem Z-Buffer ermittelt (geschätzt) werden, indem die umliegenden Punkte berücksichtigt werden. Dies ist z.B. bei Fraktalen nötig, bei denen man tatsächlich nur eine Menge von Raumpixeln ermittelt (also die Punkte im Raum, die zum Fraktal gehören). Diese Schätzung ist aber insbesondere in den Bereichen scharfer Kanten problematisch. Berechnet man den Z-Buffer z.B. für ein Objekt, welches durch Bezier-Flächen beschrieben ist, kann man die Normalenvektoren besser und präziser direkt aus den Bezier-Flächen ermitteln.
    Normalenvektoren
    Winkel der Normalenvektoren zur Blickrichtung (Flächen die senkrecht zur Blickrichtung stehen sind dunkelblau oder dunkelrot, Flächen die längs zur Blickrichtung liegen sind gelb, grün bis türkis)

    Benötigt werden diese Normalenvektoren sowohl für die Berechnung von Spiegelungen, als auch für die Berechnung des Lichtanteils, der in Richtung des Betrachters reflektiert wird. Das unten dargestellte Schema zeigt den Zusammenhang: je nachdem wie die Oberfläche ausgerichtet ist, erscheinen die Bereiche heller oder dunkler.
    Reflektionsberechnung
    Helligkeitsfaktoren aufgrund von Reflektionen
    (Licht, das abhängig von der Ausrichtung der Oberfläche mehr oder weniger stark in Richtung Beobachter "reflektiert" wird)

  • Die dritte Matrix ist die Farbmatrix - sie gibt für jeden Punkt die Farbe des Objekts an. Damit ist die Farbmatrix praktisch bereits das spätere Bild, es fehlen "nur" noch die Helligkeitsänderungen (Schatten, Reflektionen), die durch den Rendering-Algorithmus zunächst berechnet und die Farben dann damit angepasst werden. Die Farbe kann auch aus der eigentlichen Objektfarbe sowie Spiegelungen erzeugt worden sein. Ist ein Pixel übrigens vollkommen transparent, so sollte es wie ein nicht vorhandenes Pixel behandelt werden und in keiner der Matritzen auftauchen. Halbtransparente Pixel sind zwar nicht prinzipiell verboten, da es aber unmöglich ist mehrere Ebenen im Z-Buffer abzubilden (nur genau ein Z-Wert für jedes Element des Z-Buffers ist möglich) wird hinter einem solchen halbdurchsichtigen Pixel trotzdem nichts anderes durchscheinen (es gibt eben kein "Dahinter").
    Farbmatrix
    Die Farbmatrix entspricht bereits dem Bild ohne Helligkeitsänderungen durch den Renderer
  • Die vierte (optionale) Matrix ist die Material-Matrix, die für jeden Punkt je einen Wert für den Glanz und für die Spiegelungsfähigkeit enthält. Ist die Matrix nicht gegeben, bzw. für einen Punkt kein "Matrial" angegeben wird ein globaler Default-Wert angenommen. Daher erfolgt eine genauere Beschreibung im nächsten Abschnitt. Das "Material" könnte auch den Farbwert beinhalten. Da bei mir aber oft jeder Punkt eine andere Farbe hat, aber zumeist alle Punkte die gleichen Materialeigenschaften besitzen habe ich mich dafür entschieden Material und Farbe vollkommen unabhängig voneinander zu halten.
Weitere Parameter sind "global" für die ganze Szene bzw. das ganze Bild festgelegt und werden im nächsten Abschnitt erläutert.


Globale Parameter

Die folgenden "globalen" Parameter gelten für alle Bildpunkte (sofern diese nicht z.B. durch spezielle Material-Eigenschaften, die in der Material-Matrix für eine Punkt definiert sind überschrieben werden):
  • Grund-Licht:
    Beschreibt die Stärke des Lichts, welches nur von der Entfernung zur Lichtquelle (besser) Lichtebene abhängt (die Normalenvektoren werden hier nicht verwendet). Dies war im allerersten Ansatz das einzige Licht (welches damals auch nur direkt von vorne kam). Man erhält damit aber nur einen sehr, sehr diffusen Eindruck von der räumlichen Gestalt.
    Ground Light Only
    Ground Light Only from Top Right
    Nur Grund-Licht direkt von vorne
     
    Nur Grund-Licht von rechts oben

  • Reflektion und Glanz:
    Geben die Stäke der Reflektion an, die von der Richtung des Lichts bezogen zur Blickrichtung unter Berücksichtigung des Normalenvektors an einem bestimmten Punkt abhängt. Dabei gibt der Reflektions-Wert den Anteil des Lichts an der "reflektiert" wird und der Glanz-Wert bestimmt wie stark das reflektierte Licht von den Winkeln (Blickwinkel, Lichteinfallswinkel und Normalenvektor) abhängt (beide Werte müssen größer Null sein, damit eine Reflektion überhaupt stattfindet).
    Mittlere Reflection/Gloss
    Reflection (hoher Reflection-Wert)
    Reflection (hoher Gloss-Wert)
    Reflection (hohe Refelction- und Gloss-Werte)
    Mittlere Reflektions- (0.5) und Glanz-Werte (2.0)
     
    Hoher Reflektions-Wert (0.9), mittlerer Glanz-Wert (2.0)
     
    Mittlerer Reflektions- (0.5), hoher Glanz-Wert (10.0)
     
    Hohe Reflektions- (0.9) und Glanz-Werte (10.0)

  • Minimum-Licht:
    Minimale Helligkeit für alle (nicht leeren) Bereiche.

  • Diffusionsabstand:
    Gibt an über welche Entfernung Licht "diffus" auch auf umliegende Pixel wirkt. Ist dieser Wert zu gering sieht man Kanten, die dadurch entstehen, dass es ja nur endlich viele Bildpunkte (Breite x Höhe) gibt und dadurch "Stufen" in der Obefläche des Objekts entstehen, die dann, je nach Lichteinfallswinkel, Schatten auf benachbarte Bildpunkte werfen können (siehe auch hier) - ein Problem, an das ich zunächst garnicht gedacht hatte und das mich dann fast zur Verzweiflung getrieben hat :-) Welcher Wert zu gering bzw. hoch genug ist, hängt nicht (stark) vom konkreten Objekt ab. Ein Wert von 10 hat sich im Allgemeinen als ausreichend erwiesen.
    Zu geringe "Diffusion"
    Zu geringe "Diffusion"

  • Materialstärke:
    Beschreibt eine angenommene Dicke der Oberfläche in Z-Richtung (Tiefe). Dieser Wert ist insbesondere für den Schattenwurf von Bedeutung. Nimmt man an, dass die im Z-Buffer repräsentierte Oberfläche unendlich dünn ist, kann es wegen der begrenzten Auflösung in Bildpunkten zum Durchscheinen des Lichts zwischen den Bildpunkten kommen. Das führt dann dazu, dass Schatten von kleinen hellen Punkten unterbrochen sind (was sehr störend ist).

  • Hintergrundfarbe:
    Die Farbe, die für Bildpunkte gewählt wird, die "leer" sind. Sofern das Ausgabeformat Transparenzen unterstützt, kann hier auch mit einer duchsichtigen oder teildurchsichtigen Farbe gearbeitet werden.

  • Mirror:
    Stärke mit der Spiegelungen wirken (gespiegelt werden können z.B. Bilder, die sich auf den sechs Seiten einer virtuell die Szene umgebenden Box befinden, siehe auch das Beispiel oben). Diese Art der Spiegelung ist bei mir eine Vorverarbeitung der die Farbe der Bildpunkte (Farb-Matrix) manipuliert und damit nicht Teil des eigentlichen Rendering-Algorithmus' ist.

  • Schärfeebene:
    Eine Z-Koordinate für die Ebene in der Punkte scharf abgebildet werden.

  • Maximaler Blur-Radius:
    Maximaler Radius für den Gausschen Weichzeichner, der auf Punkte angewendet wird, die einen maximalen Abstand zur Schärfeebene haben. Ist dieser Abstand 0 wird keine Tiefen-(Un-)Schärfe berechnet.

  • Blauverschiebung:
    Ein Faktor der angibt wie stark die weitest entfernten Punkte ins Bläuliche verschoben werden

  • Dunst (Weißverschiebung)
    Ein Faktor der angibt wie stark die weitest entfernten Punkte aufgehellt werden sollen.


Vorverarbeitung

  • Perspektivische Verzerrung:
Der Algorithmus ist prinzipiell für parallelperspektivische Darstellungen konzipiert. Um dennoch eine natürlichere Fluchtpunktperspektive zu erzeugen, wird ein Trick eingesetzt: Die Eingangs-Matritzen (siehe oben) für den Algorithmus (siehe unten) werden für ein entsprechend verzerrtes Objekt erzeugt (ich benutze dafür, wie auch für Rotationen und Translationen sogenannte "Transformationen", die gegebene X-, Y- und Z-Werte in entsprechend transformierte Koordinaten umrechnen).
Für die perspektivische Verzerrung wird die "Linsentransformation" benutzt. Diese macht Folgendes:
Es wird ein Fokussierungspunkt gewählt, der (in Z-Richtung) vor der abzubildenden Szene liegt, wobei der Abstand in Z-Richtung (die "Brennweite") das Maß der Verzerrung (Weitwinkel-/Teleoptik) bestimmt. Um die Größenverhältnisse in etwa beizubehalten wird ungefähr in derselben Entfernung dahinter eine Projektionsfläche "aufgespannt" (rein virtuell natürlich). Jetzt wird für jeden Punkt berechnet, wo auf der Projektionsfläche ein Strahl, der von diesem Punkt durch den Fokussierungspunkt geht, aufträfe. Von diesem Projektionspunkt werden die X- und Y-Koordinate für den zu transformierenden Punkt übernommen (die Z-Koordinate bleibt dagegen erhalten, da sonst ja alle diegleiche Z-Koordinate hätten - nämlich die der Projektionsfläche). Da das Objekt dabei auch noch um 180° gedreht wird (oben/unten und rechts/links werden vertauscht), sollten X- und Y-Koordinate zusätzlich negiert werden, damit die Szene wieder richtig herum erscheint.
Linsentransformation
Linsentransformation
"Linsentransformation": Die Szene wird durch einen Fokussierungspunkt auf eine Projektionsebene projeziert (die Szene erscheint um 180° gedreht und ist perspektivisch verzerrt)
  
Die verzerrten Objekte entstehen indem die X- und Y-Koordinaten der Projektion negiert werden (Drehung um 180°) und die alte Z-Koordinate wieder hergestellt wird

Dass diese "Fluchtpunktperspektive" nicht wirklich korrekt ist merkt man insbesondere an diesen Dingen:
  1. Der Schattenwurf ist nicht korrekt, denn durch die Verzerrung des Objekts selbst sehen weiter hinten liegende Teile ja nicht nur kleiner aus, sondern sind tatsächlich kleiner und werfen entsprechend kleinere Schatten. Zudem verändern sich auch die Winkel. Im Beispiel oben kann man schön erkennen, dass der Schatten im perspektivisch verzerrten Bild eine andere Ausdehnung hat, obwohl der Lichteinfallswinkel für beide Bilder gleich gewählt wurde.
  2. Die Reflektionen sind nicht physikalisch korrekt, weil auch die Normalenvektoren, also die Ausrichtung der Oberflächen durch die Transformation verzerrt werden.
  3. Entsprechend werden auch die Spiegelungen (siehe auch nächsten Punkt) nicht physikalisch korrekt berechnet.
In der Praxis fallen diese Ungenauigkeiten, bei den von mir erzeugten Bildern aber kaum auf (der Schattenwurf ist ja ohnehin nicht korrekt, da nur sichtbare Teile berücksichtigt werden; ebenso sind die Spiegelungen nicht korrekt, weil Objekte sich nicht gegenseitig spiegeln können) - im Gegenteil, die räumliche Wirkung der Bilder ist oft besser. Würde man erst das gesamte Rendering auf einem unverzerrten Objekt machen und erst danach eine Verzerrung der Bildpunkte durchführen, könnte man Probleme mit der Auflösung bekommen, denn näher am betrachter liegende Teile werden ja dabei eher größer. Man müsste also zusätzliche Pixel interpolieren um "Löcher" im Objekt zu vermeiden. Das sähe dann aber ähnlich (schlecht) aus, wie wenn man ein normales pixelbasiertes (2D) Bild vergrößert. Hier rächt sich etwas, dass der Algorithmus letztlich auf (Raum-)Pixeln arbeitet und nicht auf beliebig skalierbaren Flächen (z.B. Dreiecksflächen).

  • Spiegelungen:
Das meiste dazu ist oben schon gesagt worden, trotzdem hier nochmal etwas ausführlicher:
Für jeden Punkt wird abhängig vom Normalenvektor an dieser Stelle berechnet welcher Punkt eines oder mehrerer Bilder (die z.B. in der Form einer, die Szene umgebenden Box, angeordnet sind - siehe Beispiel) gespiegelt werden soll. Die Farbe dieses zu spiegelnden Punktes wird mit der Orignal-Farbe des Objektpunktes verrechnet und ergibt die neue Farbe, die in die Farb-Matrix eingetragen und dem Rendering-Algorithmus übergeben wird. Ähnlich wie oben, bei der perspektivischen Verzerrung beschrieben, werden Schnittpunkte von "Sichtstrahlen", mit einem "Spiegelobjekt" berechnet. Das "Spiegelobjekt" enthält dabei die zu spiegelnden Bilder und hat z.B. die Form einer sechsseitigen Box. Ein "Sichtstrahl" läuft nun vom Auge (der Sichtebene) zu einem Punkt auf der Oberfläche eines Objekts und von dort, abhängig vom Auftreffwinkel und Normalenvektor der Oberfläche an diesem Punkt weiter zu dem "Spiegelobjekt".
Spiegelungsberechnung
Spiegelungsberechnung
(das Bild auf der Sichtebene entsteht erst später, die Farbe des gespiegelten Punkts wird zunächst dem spiegelnden Punkt zugewiesen)


Der Algorithmus im Detail

Das Objekt / die Szene wird beschrieben durch folgende Eingangswerte:

Um die Beschreibung des Algorithmus' möglichst kompakt zu halten, werden hier vorab ganz kurz zentrale Zwischenergebnisse veranschaulicht (die Details der Berechnung finden sich dann unten):
  • Das zentrale Zwischenergebnis des Algorithmus sind die Helligkeitsfaktoren (Matrix), mit denen dann die Farbwerte der Farbmatrix angepasst werden.
    Matrix der Helligkeitsfaktoren
    Matrix der Helligkeitsfaktoren helle Bereiche erscheinen rot, dunkle Bereiche blau (Licht kommt von rechts oben)

  • Ebenfalls an etlichen Stellen verwendet wird die "Projektionsfläche". Dies ist eine weitere Matrix, die aber eine anderer Größe hat, als alle anderen bisher erwhänten Matritzen. Sie enthält eine Art Z-Buffer, der aus dem Originalen Z-Buffer entsteht indem die Pixel in Richtung des Lichts gedreht werden - also einen Z-Buffer für die Licht- statt der Sichtebene (Genaueres siehe unten). Unten sind zwei Beispiele für unterschiedliche Lichteinfallswinkel zu sehen.
    Matrix der in Richtung des Lichts gedrehten Pixel
    Matrix der in Richtung des Lichts gedrehten Pixel
    Matrix der, in Richtung des Lichts, gedrehten Pixel (hier ist zu erkennen, dass der Zylinder keine Rückseite hat, was zu dem Fehler beim Schattenwurf führt)
     
    Hier die Matrix, für den Fall, dass das Licht genau von rechts kommt. Dadurch ist noch deutlicher zu erkennen, dass der Zylinder keine Rückseite hat

    Die folgende Grafik zeigt für eine Zeile von 5 Bildpunkten des Z-Buffers und einen bestimmten Lichteinfallswinkel die resultierende "Projektionsfläche". Zu sehen ist u.a., dass nicht alle Pixel des Z-Buffers aus der Richtung des Lichts zu "sehen" sind. Diese Pixel liegen daher im Schatten.
    Projektion in Richtung des Lichts
    Projektion des Z-Buffers in Richtung des Lichts. Graue Pixel sind aus Richtung des Lichts nicht zu sehen und liegen daher im Schatten.

    Die Auflösung der Projektionsebene sollte ggf. außerdem an den Lichteinfallswinkel angepasst werden - d.h. die Matrixelemente bzw. "Pixel" der Projektionsebene können in X- und Y-Richtung virtuell kleiner oder größer sein als im Z-Buffer. Diese Anpassung erfolgt zweckmäßiger Weise so, dass ein Pixel des Z-Buffers etwa einem Pixel in der Projektionsebene entspricht.
Ablauf:
  1. Initialisierung:
    Die Matrix, die für jeden Bildpunkt den Helligkeitsfaktor enthält, wird angelegt. Dies ist das entscheidende Zwischenergebnis des gesamten Algorithmus'. Initial sind alle Werte dieser Matrix leer (z.B. "null", aber nicht "0"). Am Ende werden mit diesen Faktoren schließlich die Helligkeitswerte jedes Farb-Pixels (siehe Farb-Matrix) angepasst.
    Außerdem wird die "Projektionsfläche" initialisiert. Dabei handelt es sich, wie schon beschrieben, um eine weitere interne Matrix, die die Raumpixel des Z-Buffers aufnimmt, nachdem sie in Richtung des Lichts gedreht wurden (aus den Koordinaten (x,y,z) werden durch diese Projektion die Koordinaten (xr,yr,zr)). Die Projektionsfläche hat damit i.d.R. eine andere Größe als alle zuvor genannten Matritzen! Prinzipiell enthält jedes durch Drehung des Ursprungspixels entstandene Raumpixel der Projektionsfläche den Abstand zum Licht (genauer der Lichtebene)3). Damit ist die Projektionsfläche auch eine Art Z-Buffer. Allerdings enthält sie zweckmäßiger Weise für jedes ihrer Raumpixel auch einen Rückverweis auf die X- und Y-Koordinaten des originalen Pixel des Z-Buffers, sowie der Normalen- und der Farb-Matrix4).
    Es hat sich zudem gezeigt, dass es für den Schattenwurf notwendig ist, dass man nicht nur die direkt im Z-Buffer repräsentierten Punkte auf diese Projektionsfläche projeziert, sondern für jeden Punkt auch eine Anzahl (unsichtbarer) Punkte die virtuell dahinterliegen. Auf der Projektionsfläche sind diese zusätzlichen Punkte dann u.U. nicht mehr durch den ersten, oder einen anderen Punkt verdeckt und tragen somit zum Schattenwurf bei. Zur Bestimmung der Anzahl dieser zusätzlichen Punkte wird die "Materialdicke" herangezogen (siehe oben). Die Materialdicke gibt dabei direkt die Anzahl der Raumpixel in der Tiefe vor.

  2. Berechnung des direkten Lichts:
    Jeder Raumpixel des Z-Buffers, der durch ein Raumpixel auf der Projektionsfläche repräsentiert ist bekommt den Helligkeitsfaktor 1. Für alle anderen Raumpixel des Z-Buffers ist damit klar, dass sie nicht direkt vom Licht angestrahlt sind, da sie durch andere Raumpixel "abgeschattet" werden (diese abgeschatteten Raumpixel werden in diesem Schritt nicht verändert haben damit weiterhin keinen Helligkeitsfaktor).

  3. Diffuses Licht berechnen:
    Für jedes Raumpixel, das nicht direkt angestrahlt ist (also bisher noch keinen Helligkeitsfaktor zugewiesen bekommen hat), wird der direkt angestrahlte Punkt gesucht der dieses Raumpixel "abschattet". Das ist genau jenes Raumpixel, welches auf der Projektionsfläche an genau derselben Stelle repräsentiert ist, an der auch das abgeschattete Pixel (nach Drehung in Richtung des Lichts) liegen würde. Zwischen diesen beiden Pixeln wird der Abstand a berechnet. Der Helligkeitsfaktor des abgeschatteten Pixels ergibt sich dann durch:

    HelligkeitsFaktor(x,y) = max(0, 1 - (a2 / DiffusionsAbstand2)),

    wobei der oben bereits erläuterte "Diffusionsabstand" benutzt wird. Diese Formel ist übrigens, wie die folgenden auch, nirgendwo in Stein gemeißelt und könnte auch anders gewählt werden...

  4. Reflektiertes Licht berechnen:
    Für jeden direkt beleuchteten Punkt (also alle Punkte die auf der Projektionsfläche repräsentiert sind), wird der Winkel berechnet, mit dem ein Lichtstrahl, der ausgehend von der Lichtquelle von diesem Punkt relektiert wird auf die Sichtebene trifft. Die Reflektionsrichtung ist dabei abhängig vom Normalenvektor an diesem Punkt5) ("Einfallswinkel = Ausfallswinkel")6). Die so berechneten Winkel für alle Punkte werden bei mir in einer weiteren Matrix (der Winkel-Matrix) mit derselben Größe wie die Projektionsfläche zwischengespeichert. Danach wird für jeden Helligkeitsfaktor, unter Berücksichtigung der Werte für Reflektion und Glanz, folgende Anpassung vorgenommen:

    HelligkeitsFaktor(x,y)' = HelligkeitsFaktor(x,y) * ((1.0 - Reflektion) + normGauss(angle, pi/2.0, 1.0/Glanz) * Reflektion)

    Wobei für jeden Punkt (x,y) jeweils der Winkeln (angle) genommen wird der in der Winkel-Matrix an der Position (xr,yr) des in Richtung des Lichts gedrehten Originalpixels (aus dem Z-Buffer) zu finden ist (die Winkel-Matrix wird nur für diesen einen Berechnungsschritt benötigt). Die Funktion normGauss ist die Gauss-Funktion, so normiert, dass an der Stelle x = mean der Wert 1 herauskommt, wodurch sich die normale Gaussfunktion vereinfacht zu:

    normGauss = exp(-(x-mean)2 / (2*var))

  5. Grund-Licht berechnen:
    Die Helligkeitsfaktoren aller Pixel werden mit folgender Formel angepasst:

    HelligkeitsFaktor(x,y)' = (1.0 - GrundLicht) * (l * HelligkeitsFaktor(x,y)) + GrundLicht * l,
    wobei gilt:
    l = 1.0 - (zr - minZ) / Range,
    es gilt zudem:
    • zr ist die Z-Koordinate des in Richtung des Lichts gedrehten Pixels,
    • minZ ist der minimal mögliche Wert für zr (Punkte mit diesem oder einem kleineren Abstand zum Licht erhalten das maximale Grund-Licht, welches durch den Parameter GrundLicht bestimmt ist)
    • Range ist die Differenz zwischen dem minimalen und dem maximalen Wert für zr (Punkte mit dem Abstand minZ+Range und mehr zum Licht erhalten das minimale Grund-Licht - also garkeins)

    Range
    und minZ können dabei entweder automatisch aus den Werten der Projektionsfläche ermittelt werden oder aber anderweitig vorgegeben werden (z.B. indem diese Werte als Zusatzattribute des Z-Buffer abgelegt wurden).

  6. Minimal-Licht berechnen:
    Die Helligkeitsfaktoren aller Pixel werden so angepasst, dass sie mindestens den vorgegebenen Minimalwert haben (siehe oben). Dazu wird einfach folgende lineare Transformation benutzt:

    HelligkeitsFaktor(x,y)' = HelligkeitsFaktor(x,y) * (1.0 - minLight)) + minLight

  7. Bild erzeugen:
    Ein Bild wird angelegt, das dieselbe Höhe und Breite hat wie auch der Z-Buffers, die Normalenmatrix und die Farb-Matrix. Jedes Pixel bekommt die Farbe die durch das entsprechende Element der Farbmatrix vorgegeben ist, wobei diese Farbe zuvor noch mit dem entsprechenden Helligkeitsfaktor manipuliert wird. Dabei ergibt sich z.B. der neue Wert für den Grün-Kanal eines Pixels durch:

    green(x,y)' = min(255, max(0, green(x,y) * HelligkeitsFakor)),

    wobei die Werte eines Farbkanals hier im Bereich 0 bis 255 liegen können. Für die Kanäle Rot und Blau gilt entsprechendes. Das Bild kann dann z.B. in einem gängigen Grafik-Format gespeichert werden - wobei auch ich dafür dann eine der bekannten Libraries nutzen würde :-)

Nachbearbeitung

Zusätzlich sind einige Nachbearbeitungsschritte (bei mir intern als "Filter" bezeichnet) möglich. Diese benötigen zumeist nur das gerenderte Bild - können aber auch den Z-Buffer verwenden, wenn zusätzlich die Z-Koordinate benötigt wird. Die Filter werden normalerweise in der hier angegebenen Reihenfolge auf das Bild angewendet (mögliche Abweichungen von dieser Reihenfolge sind jeweils angegeben):
  • Hue-, Saturation- und Brightness-Anpassung:
    Durch eine entsprechende Farbraumkonvertierung werden alle Pixel-Farben angepasst. Dies erfolgt vor den folgenden Schritten, damit nur die eigentlichen Objektpixel verändert werden, der Hintergrund aber unverändert bleibt (hätte man aber auch anders machen können).

  • Blauverschiebung und Dunst:
    Abhängig von der Tiefe eines Punktes und den Einstellungen werden die Farben aufgehellt und ins Bläuliche verschoben.

  • Setzen eines Hintergrundbildes:
    Optional werden alle (semi-)transparenten Pixel mit der Farbe des entsprechenden Pixels eines (ggf. skalierten und verschobenen) Hintergrundbildes verrechnet (dieses kann selbst wieder transparente Pixel enthalten, so dass der nächste Schritt auch dann noch Sinn macht).

  • Setzen einer Hintergrundfarbe:
    Alle (immernoch) (semi-)transparenten Pixel können mit eine Hintergrundfarbe verrechnet werden (unter dem Punkt "Tiefenschärfe berechnen" ist ein Beispiel zu sehen, in dem ein Schachbrettmuster als Hintergrundbild verwendet wurde). Optional können sowohl Hintergrundfarbe als auch Hintergrundbild bereits als erste Filter angewendet werden, so dass sich alle Filter und nicht nur die Tiefenschärfeberechnung auch auf den Hintergrund auswirken.

  • Tiefenschärfe berechnen:
    Die Berechnung der Tiefenschärfe benötigt nur das zuvor angelegte Bild, sowie den Z-Buffer (für jeden Punkt wird neben der X- und Y-Koordinate auch die Z-Koordinate zur Ermittlung des Abstands von der Schärfeebene benötigt). Daher ist dieser Schritt intern als eine Nachbearbeitung implementiert. Prinzipiell wird für jeden Punkt ein Weichzeichnungs-Radius bestimmt, der umso größer ist je weiter der Punkt von der Schärfeebene entfernt ist. Statt eines linearen Zusammenhangs zwischen diesem Radius und dem Abstand zur Schärfeebene wird eine (re-normierte) Arcustangens-Funktion verwendet. Der Radius für den Punkt an Position (x,y) ergibt sich damit durch:

    Radius(x,y) = atan(abs(z-Schärfeebene)/maxDist * 4) / atan(4) * MaxBlurRadius)

    Atan
    Verwendeter Ausschnitt der Arcustangens-Funktion (mit max. Distanz = 1 und max. Blur-Radius = 1)

    Die Normierung auf den Wert von atan(4) ist dabei eine reine Konvention, damit der maximale Blur-Radius auch tatsächlich, für die Punkte erreicht wird, die am weitesten von der Schärfeebene entfernt sind. Um Probleme, die durch den Wert 0 für den Radius auftreten können zu vermeiden, ist es sinnvoll diesen Wert noch +1 zu nehmen. Dadurch ergibt sich insbesondere auch ein schönerer Übergang zwischen ganz scharfen und leicht unscharfen Bereichen (diese Optimierung ist in den unten gezeigten Beispielen übrigens noch nicht enthalten, was man jeweils an der linken vorderen Ecke des Rechtecks erkennen kann).
    Mit dem so berechneten Radius7) wird ein Gausscher Weichzeichner auf den Punkt (x,y) angwendet. Dabei wird der Wert jedes Farbkanals (z.B. Rot, Grün, Blau und ggf. Alpha) auf die umliegenden Punkte, mittels einer Gewichtungsfunktion, verteilt (die Punkte in der Mitte bekommen den größten Anteil, die Punkte weiter außen bekommen abhängig vom Radius immer weniger ab). Als Gewichtungsfunktion wird eine zweidimensionale Normalverteilung verwendet:


    1
    -
    dx2+dy2

    2 var(x,y)
    Gewicht(x+dx,y+dy) = 

     e



    2 * PI * var(x,y)



    Zweidimensionale Normalverteilung
    Gewichtsfunktion (2D-Normalverteilung) bei Radius = 9 (im Intervall x = [-9...+9], y = [-9...+9]) und Varianz = (9/3)2 = 9

    wobei die Varianz var(x,y) = (Radius(x,y)/3)2 gewählt wird, so dass nur Bereiche der Funktion mit sehr kleinen Werten (Gewichten) außerhalb des Radius liegen, die wegen ihrer Kleinheit aber gefahrlos abgeschnitten werden können, ohne dass dies sichtbare Auswirkungen hätte. Die Variablen dx und dy iterieren jeweils über das Intervall [-Radius(x,y)...+Radius(x,y)]. Hier ist wichtig, dass der Radius nie Null ist, da die Funktion für var = 0 nicht definiert ist. Da gerade diese Funktion sehr oft ausgewertet werden muss und dabei recht langsam ist, verwende ich mittlerweile Look-Up-Tables, in denen die Werte für (ganzahlig gerundete) Radien, sowie quadrierte Abstände vom Zentrum (dx2+dy2) abgelegt sind.

    Wie sich gezeigt hat, birgt dieses Verfahren noch ein paar Fallen. Es reicht leider nicht, einfach, wie beschrieben, einen Gausschen Weichzeichner, der für jeden Punkt, abhängig von dessen Abstand zur Schärfeebene, einen anderen Radius verwendet zu nehmen8), weil dann an Übergängen zwischen scharfen Objektteilen zu sehr unscharfen Teilen Helligkeitsartefakte auftreten. Das kommt daher, dass Punkte die sehr unscharf sind viel ihrer Helligkeit an umliegende Punkte abgeben, während schärfere Punkte ihre Helligkeit in einem engeren Bereich konzentrieren. An Übergängen zwischen mehr und weniger scharfen Bereichen entsteht so ein Ungleichgewicht, welches sich in hellen und dunklen "Auren" äußert. Der Effekt ist unten im linken Bild zu erkennen. Hier hilft es für jeden Punkt die Summe aller Gewichte, die für diesen Punkt zur Anwendung kommen, zu summieren (also auch die für die Weichzeichnung von Punkten in der Umgebung, die auf den fraglichen Punkt "ausstrahlen"). Die Helligkeit jedes Punktes (also alle Farbkanäle) wird danach mit dieser Gewichtssumme normiert. Dies erfordert leider, dass man eine weitere Matrix anlegt in der die Gewichte für jeden Punkt gesammelt, d.h. aufsummiert werden.

    Ohne Normierung der Gesamtgewichtungen
    Ohne Adaption der Gaussfunktion
    Mit Gesamtgewichtsnormierung und Adaption der Gaussfunktion
    Mit Gesamtgewichtsnormierung und Adaption der Gaussfunktion
    Ohne Normierung der Gesamtgewichte (und ohne Gauss-Adaption): helle und dunkle "Auren" zeigen sich
     
    Ohne Gewichts-Adaption: schärfere Bereiche werden in Randzonen vom Hintergrund (zu stark) überstrahlt
     
    Mit Normierung der Gesamtgewichte und Gewichts-Adaption: schön erkennbarer Verlauf der Tiefenschärfe
     
    Kein negativer Einfluss auf den Hintergrund (gleichbleibend unscharf, auch an den Objekträndern)

    Aber selbst dann bleibt noch ein weiteres Problem: Punkte die sehr unscharf sind strahlen auch auf scharfe Punkte in ihrer Nähe aus und überstrahlen diese dadurch teilweise recht stark. Dies ist oben im zweiten Bild zu erkennen. Die Objektränder scheinen, trotz ihres unterschiedlichen Abstands, fast gleichmäßig unscharf zu sein. Diese Unschärfe kommt aber hauptsächlich durch die Überstrahlung durch den durchweg maximal unscharfen Hintergrund (das Problem wird dadurch verstärkt, dass der Hintergrund sehr hell ist). Um dieses Problem etwas abzumildern verwende ich folgenden Trick: die Gaussfunktion wird mit einem Faktor multipliziert, so dass breitere Verteilungen noch weiter abgeflacht werden, was zur Folge hat, dass schärfere Punkte weniger überstrahlt werden können:

    Adaptiertes_Gewicht(x,y) = Gewicht(x,y) * sqrt(Gewicht(0,0)

    Also Normierungsfaktor dient der Maximalwert der Gewichtsfunktion, der ja an der Stelle (0,0) auftritt. Die Verwendung der Wurzel dieses Wertes mildert das ganze nur wieder etwas ab. Andere Ideen (z.B. Adaption der Gewichte je nachdem wie die Werte in der Nachbarschaft sind) hatten teilweise zur Folge, dass in der Umgebung scharfer Objekteile z.B. auch der Hintergrund schärfer wurde. Dies ist bei dem oben beschriebenen Verfahren, wie man im rechten Beispiel sieht, nicht der Fall.

    Beide Workarounds entbehren sicherlich weitgehend jeder physikalischen Grundlage, ergeben aber recht ansehnliche Resultate...

Anwendungen

Die 3D-Fraktale zu deren Visualisierung der Algorithmus ursprünglich entstanden ist sind hier näher beschrieben. Später habe ich dann auch noch Objekte aus Bezier-Flächen damit gerendert, wie hier beschrieben ist (einige der erzeugten Bilder sind auf den Seiten auch zu sehen). Programme (3D-Fraktal-Generator, Bezier-Flächen-Programm, 3D-Text-Erzeugungsprogramm), die den hier beschriebenen Renderer benutzen sind hier zum Download bereitgestellt.


1)
Übrigens werden die Lichtstrahlen dabei in der umgekehrten Richtung "verfolgt", was den großen Vorteil hat, dass man nur die Lichtstrahlen tatsächlich berechnet, die wirklich das "Auge" erreichen.
2)
Für die Erklärung ist es einfacher von drei Matritzen zu sprechen; softwaretechnisch kann alles in einer Matrix enthalten sein, bei der jedes Element die drei Anteile Z-Koordinate, Normalenvektor und Farbe für diesen Punkt hat (evtl. plus weiterer Attribute).
3)
Da auch die Projektionsfläche eine Matrix ist, müssen die Koordinaten xr und yr entsprechend ganzahlig gerundet werden, um das Element der Projektionsfläche zu finden, welches zu diesen Koordinaten den Z-Wert (plus weiterer Daten) enthält.
4)
Wie jeder Z-Buffer enthält auch die Projektionsfläche immer nur die Pixel die den kleinsten Abstand zum Betrachter bzw. hier dem Licht haben - es ist also i.d.R. nicht jedes Raumpixel des Z-Buffers auch durch ein Pixel auf der Projektionsfläche repräsentiert, da mehrere Z-Buffer-Elemente auf denselben Punkt der Projektionsfläche "fallen" können.
5)
Hier kann man den Rückverweis von den gedrehten Raumpixeln der Projektionsfläche auf die Originalkoordinaten gut gebrauchen.
6)
Die Richtung des an einem Punkt reflektierten Lichtstrahls kann dabei mit Hilfe von Ebenen und Linien, sowie den Schnittpunkten ermittelt werden.
7)
Es wird dabei einfach ein rechteckiger Bereich von xi = [x-radius...x+radius] und yi = [y-radius...y+radius] abgearbeitet. Die Bezeichnung "Radius" ist also etwas irreführend. Die "Rundheit" der Unschärfe kommt allein durch die Form der zweidimensionalen Normalverteilung zur Gewichtung.
8)
Wobei eine Normierung der Summe aller Gewichte des Gausschen Weichzeichners für einen Punkt auf 1 voraussgesetzt wird.

Freue mich über eure Meinungen, Anregungen, usw.
Mail-Adresse.

Feedback-Formular


Patrick Rammelt (11/2010)