- Werbung -

Das Thema 3D-Grafik ist momentan in Interesse durch die aktuellen Trends zu 3D-Kino und 3D-Fernsehen. Ein Erzeugen von solchen Bildern ist auf vielfältige Art und Weise möglich, eine davon bietet PovRay.

Das Programm PovRay

Der Name PovRay ist eine Abkürzung für Persistance of Vision Raytracer. Persistance of Vision bedeutet sinngemäß „Die Beharrlichkeit / Trägheit des Sehens / Nachbildes (auf der Netzhaut)“ oder „Die Fortdauer des Eindrucks“ oder „Nachleuchten eines Bildes“. Diese Mehrdeutigkeit der Interpretation ist offenbar von den Autoren erwünscht. Raytracing enthält die Vokabeln Ray (Strahl) und to trace (verfolgen), bedeutet also soviel wie „Stahlenverfolger“. Raytracing stellt einen auf der Methode der Strahlenaussendung basierenden Algorithmus zur Berechnung der Sichtbarkeit von dreidimensionalen Objekten von einem bestimmten Punkt aus. Die Grundidee ist die der Umkehrung des Sehens in der Strahlenoptik. Physikalisch gesehen, reflektiert jeder Punkt eines Objekts das von einer oder mehreren Lichtquellen stammende Licht in alle Richtungen. Sieht man den Gegenstand, so gelangt von diesen Strahlen ein Bündel ins Auge. Zur Berechnung der Sichtbarkeit ist dieses Modell nicht gut geeignet, weil es zu rechenintensiv wäre, alle Strahlen, die ein Objekt aussendet, zu berechnen Ein Raytracer kehrt deswegen die Situation (den Strahlengang) um, es wird für jedes Pixel die Richtung, aus der das Licht ins Auge fällt, berechnet und anschließend die Intensität und Farbe des Pixels bestimmt. Hierbei machen Verdeckungen und auch Reflexionen die Berechnung aufwändig, insgesamt wird so aber der Rechenaufwand verringert.
Diese knappe Darstellung zeigt, wie viel Physik in einem solchen Programm steckt. Befasst man sich mit den Optionen, die es bietet, kommt man beispielsweise auf Oberflächeneigenschaften wie den Unterscheid von diffuser und gerichteter Reflexion. Wenn transparente Objekte erzeugt werden, kommen die Gesetze der Optik besonders deutlich zum Ausdruck, etwa wenn man einen Glasquader vor einem Bild modelliert. Die spektrale Zerlegung wird aber nicht modelliert: Ein virtuelles Prisma zeigt keine Farben!

PovRay ist ein 3-D-Computergrafikprogramm, welches es ermöglicht, dreidimensional wirkende Szenen am Computer „fotorealistisch“ zu erstellen, Bilder lassen sich in sehr hoher Auflösung und sehr guter Farbbrillanz „rendern“. Hierzu dient eine eigene Skriptsprache. Darzustellende Gegenstände werden aus Einzelteilen zusammengesetzt, die durch mathematische Funktionen beschrieben und über Schlüsselworte implementiert werden. Beispielsweise gibt es zum Erzeugen einer Kugel die Anweisung sphere {Parameter}. Neben den Objekten muss man eine in eine bestimmte Richtung weisende Kamera und mindestens eine Lichtquelle, die die Gegenstände beleuchtet, definieren.

Die nun von PovRay zu berechnende Abbildung dieser räumlichen Szene in ein Bild ist ein recht komplexer Vorgang, denn die in der Szene vorhandenen Körper werden nicht nur direkt durch die vorhandenen Lichtquellen beleuchtet, es gibt durchaus auch indirekte Beleuchtung, die bspw. durch Reflexionen an anderen Körpern entsteht. Auch Schattenräume sind zu berücksichtigen, ebenso die mögliche Transparenz einiger Gegenstände. All diese Erscheinungen werden beim Einsatz von PovRay einbezogen und erklären, warum das Rendern auf hoher Qualitätsstufe relativ lange dauern kann.

Ein erstes Beispiel

 Nach dem Start von PovRay wird zunächst ein neues, leeres Dokument erzeugt, dann stellen wir die Größe in Pixel des später zu erzeugendes Bildes (Pop-down-Menü, links oben) auf bspw. 800X600 ein, es ist die Einstellung „AA“ („Anti-Aliasing“) möglich, was eine höhere Bildqualität, aber auch erhöhten Rechenaufwand zur Folge hat.
Nun fügen wir als erste Zeile der neuen Datei #include "colors.inc" ein. Hiermit erreichen wir, dass nunmehr Farben mit vordefinierten Namen benutzt werden können (ansonsten müssten Farben über ihren RGB-Anteil definiert werden). Es ist leicht einsehbar, dass eine Farbgebung {color Gold} anstelle von {color rgb <0.8, 0.498039, 0.196078>} deutlich vorteilhafter zu handhaben ist. Welche Farbbezeichnungen das in Einzelnen sind, kann man durch Öffnen der Include-Datei, die im Dokumentenordner abgespeichert wird, nachlesen.

Nun setzen wir die Hintergrundfarbe fest, z. B. durch background {White} Wie oben beschrieben, müssen nun eine Kamera und mindestens eine Lichtquelle platziert werden. Dies geschieht mittels der Anweisungen:
camera {location <10,10,-10> look_at <0,0,0> angle 50}
light_source { <-20,10,-20> color White }

Die in spitzen Klammern angegebenen Zahlen haben die Bedeutung von Koordinaten im dreidimensionalen Raum, es wird das nebenstehend gezeichnete (linkshändische) Koordinatensystem benutzt, die übrigen Parameter (location – Standpunkt des Betrachters bzw. der Kamera, look_at – Blickpunkt, auf den die Kamera gerichtet ist, angle – Öffnungswinkel der Kamera) erklären sich nahezu selbst.

Nun fügen wir der Szene eine Ebene hinzu, dies geschieht mittels plane {<0, 1, 0> 0.5 pigment { color Red}}. <0, 1, 0> ist der Normalenvektor, er steht orthogonal auf der Ebene, diese wiederum liegt also zur XZ-Ebene im obigen Koordinatensystem parallel. Die Zahl 0.5 gibt den Abstand zum Ursprung an. Mit pigment {…} wird die Farbe und fakultativ die Transparenz festgelegt. Für Letzteres ergänzt man hinter dem Parameter color den optionalen Parameter transmit mit Werten zwischen 0 und 1.

Zum Abschluss ergänzen wir die Szene um eine Kugel sphere {<1, 2, -2> 3 pigment {color Aquamarine transmit 0.7} }, wobei wir auch einen Parameter namens texture einsetzen können, um vorgefertige Farbmuster (Texturen) verwenden zu können. Für Texturen gibt es, wie auch für die Farben, eine Include-Datei („textures.inc“), die ebenfalls eingebunden werden sollte. <1, 2, -2> bezeichnet die Koordinaten des Kugelmittelpunktes, die Zahl 3 gibt den Radius an.
 

Eine Textur wird durch die folgenden 3 Bestandteile vollständig beschrieben:

  1. Farbeigenschaft:, inklusive Durchsichtigkeit: pigment{ ... }
  2. Oberflächenstruktur (Rauheit): normal { ... }
  3. Helligkeits-, Glanz- und Reflexionseigenschaften: finish { ... }

Beispiel:
sphere { <0,2,-2> 3 texture { Lightning2} 
                                normal { ripples 0.1}
                                finish { diffuse 0.9 phong 0.5}}

erzeugt eine leicht lichtdurchlässige, lilafarbene Kugel mit Schlieren auf der Oberfläche und Glanzlichtern.

Für einen ersten Einstieg in die Arbeit mit PovRay mag das nun genügen. Man findet bspw. unter der URL www.f-lohmueller.de einen sehr gut gestalteten Kurs mit einigen Beispielen zum Nachmachen, Abändern, Ausbauen usw.

In dem weiteren Verlauf der Beschäftigung mit PovRay werden sicherlich Variablen, Entscheidungen (if- und auch switch-Anweisungen) sowie Wiederholungen (while-Schleifen) auftreten, womit man schon die Grundelemente einer Programmiersprache hat. Mittels der declare-Anweisung definiert man seine eigenen Objekte und kann diese mehrfach in einer Szene verwenden.

Ein zweites Beispiel

 Einige der oben erwähnten Möglichkeiten sind im zweiten Beispiel zusammengefasst, in welchem deren Verwendung demonstriert wird.

Zunächst nehmen wir grundlegende Einstellungen vor und führen die notwendigen Include-Dateien auf, um sinnvoll mit Farben und Texturen arbeiten zu können.
global_settings {assumed_gamma 1.0 }
#default{ finish{ ambient 0.1 diffuse 0.9 }}
#include "colors.inc"
#include "textures.inc"

Um die Verwendung einer Mehrfachauswahl zu demonstrieren, werden fünf unterschiedliche Kamerapositionen definiert, wobei mit Hilfe des Selektors Kamera ausgewählt wird. Mittels case werden die Positionen für die Werte eins bis vier festgelegt, der else-Teil bestimmt die Lage bei Eingabe anderer Ziffern. Hierbei werden die für die Kamera wichtigen Parameter in den Variablen Position, Auf und Winkel gespeichert. Prinzipiell deklariert man mit #declare globale, mittels #local lokale Variablen, z. B. in einem Makro oder einer Include-Datei.
#declare Kamera =2;
#switch ( Kamera )
#case (1) //Breitseite
  #declare Position = < 2,4,-4> ;
  #declare Auf = < 2, 1, 3> ;
  #declare Winkel = 55 ;
#break
#case (2) //seitlich, mehrere Gläser
  #declare Position = < 5.00, 5.00, -5.00> ;
  #declare Auf = < 0.5,1.5,2> ;
  #declare Winkel = 12 ;
#break
#case (3) //ein Glas, groß
  #declare Position = < 0,5,0>;
  #declare Auf = < 0,0,2> ;
  #declare Winkel = 15 ;
#break
#case (4) //Schmalseite
  #declare Position = < -5,5,3>;
  #declare Auf = < 2,0,3> ;
  #declare Winkel = 50 ;
#break
#else
  #declare Position = < -3, 3, -7> ;
  #declare Auf = < 0, 1, 1> ;
  #declare Winkel = 25 ;
#end 

Es wird die Position zweier Lichtquellen festgelegt, die zweite, weit entfernte, soll die Sonne darstellen.
light_source { <0,5,5> color White } // Lampe
light_source{<-1000,1000,-1000> color White} // Sonnenlicht

Ein Objekt Tisch setzt sich aus einer Holzplatte (box) mit vier Füßen (cylinder) zusammen. An dieser Stelle wird der Tisch noch nicht gezeichnet, sondern nur beschrieben. Der Zeichenvorgang wird mittels der Anweisung object(Tisch) ausgeführt (s.u.).
#declare Tisch = object {
union {
  box{ <-1,1,1> <5,1.2,5> texture {Yellow_Pine} } // oder DMFWood4, Sandalwood
  cylinder { <-0.8,0,1.2> <-0.8,1,1.2> 0.1 pigment {color Gray}}
  cylinder { <-0.8,0,1.2> <-0.8,1,1.2> 0.1 pigment {color Gray} translate <5.6,0,0>}
  cylinder { <-0.8,0,1.2> <-0.8,1,1.2> 0.1 pigment {color Gray} translate <0,0,3.6>}
  cylinder { <-0.8,0,1.2> <-0.8,1,1.2> 0.1 pigment {color Gray} translate <5.6,0,3.6> }}

Analog dazu wird ein Objekt Glas definiert, das sich aus einem ausgehöhlten Kegelstumpf („Differenz“ zweier Kegelstümpfe) und einem weiteren, innen passenden, Kegelstumpf, der Flüssigkeit, zusammensetzt. (Der „innere“ Kegel, der herausgeschnitten wird, muss eine etwas größere Höhe als der äußere besitzen, sonst scheint das Glas oben nicht offen zu sein.)
#declare Glas = object {
union {
difference {
  cone { <0,1.2,1.5> 0.055 <0,1.5,1.5> 0.06 }
  cone { <0,1.22,1.5> 0.05 <0,1.51,1.5> 0.055}
  texture { NBglass}// alternativ: material {M_NB_Glass} }
  cone { <0,1.22,1.5> 0.05 <0,1.35,1.5> 0.055 texture {Water} finish {reflection 0.9}}}}

Bis zu dieser Stelle wurde noch nichts gezeichnet, dies erfolgt nun. Zuerst verlegen wir einen grauen Teppichboden …
plane { <0,1,0>, 0
          texture{ pigment{ color Gray60}
                        normal { bumps 0.5 scale 0.01 }
                        finish { phong 0.15 } } }

… und stellen einen Tisch darauf.
object{Tisch}

Nun erzeugen wir mittels einer while-Schleife vier Gläser vom oben beschriebenen Typ und verschieben sie an unterschiedliche Positionen auf der Tischplatte.
#declare zaehler = 0;
#while (zaehler < 4)
  object{Glas translate <0,0,zaehler / 2>}
  #if (zaehler = 1)
    object{Glas translate <zaehler,0,zaehler>}
  #end
  #declare zaehler = zaehler + 1;
#end

Unser oben definiertes Objekt Glas ist unflexibel, wird haben nur eine Form, die mehrfach dupliziert und verwendet werden kann. Um mehrere Variationsmöglichkeiten nutzen zu können, verwenden wir die Struktur #macro …#end, welche eine Parameterübergabe erlaubt, um die Glassorte und damit auch die Farbe des Glases bestimmen zu können. Selbstverständlich ist der Einsatz weiterer Parameter möglich.
#macro FarbigesGlas (Farbe)
union {
difference {
  cone { <0,1.2,1.5> 0.055 <0,1.5,1.5> 0.06 }
  cone { <0,1.22,1.5> 0.05 <0,1.51,1.5> 0.055}
  texture {Farbe} } }
#end

object {FarbigesGlas (Green_Glass) translate <1,0,2>}

Oder auch:
#declare Farbe = Ruby_Glass;
object {FarbigesGlas (Farbe) translate <1,0,2.5>}

Das Bild zeigt unser Produkt aus der Kameraposition 4.

Mit einfachen Mitteln können Animationen erstellt werden; sie bestehen aus einzelnen Bildern, die zu einem Video zusammengesetzt werden. Dies ist beispielsweise in [FL10] gut nachvollziehbar erläutert.

Eine gute theoretische Grundlage zum Thema 3D-Grafikprogrammierung, in der alle wichtigen Begriffe erklärt sind, findet man in [Mo08].

An anderer Einstieg in das Erstellen eigener Bilder besteht in der Arbeit mit vorgegebenen Szenen, wie sie im Menü "Insert" - "Basic templetes" - "Ready_made scenes" zu finden sind. Hier werden Grundszenerien mit Kamera, Lichtquellen und Hintergrund (Boden u. Himmel) geboten, die nach eigenen Vorstellungen erweitert werden können.
Literatur

RO0X R. Oldenburg: 3D-Grafiken und Animationen mit POVRay, X-Lab Göttingen
RO06 R. Oldenburg: Rekonstruktion von 3D-Koordinaten aus Bildern. In: Istron Bd. 9, Hildesheim 2006
FL10 F. A. Lohmüller: Beschreibungen und Beispiele zum Raytracer POV-Ray, http://www.f-lohmueller.de/pov_tut/pov__ger.htm#basic
Mo08 B. Möller: Graphikprogrammierung, http://www.informatik.uni-augsburg.de/lehrstuehle/dbis/pmi/lectures/ws0809/graphikprogrammierung/script/grafikprog.pdf