Autor Comedy Kreativer Physiker Lehrer Bastler Interessierter
Kreativer Physiker
Rund | CSG | Beispiel

Aus Formen werden Figuren

Die in den Grundlagen vorgestellten Formen ermöglichen schon eine Vielzahl an Motiven. Allerdings sind damit noch lange nicht alle Formen abgedeckt. In diesem Kapitel werden daher Formen wie Torus, Prisma und Isosurface vorgestellt. Außerdem geht es darum mit CSG (Constructive Solid Geometry) verschiedene Formen zu Figuren zusammen zu setzen.
Rund | CSG | Beispiel

Hübshe Rundungen

Bisher haben wir immer mit den Grundformen sphere, box, cylinder und cone gearbeitet. Damit sind die Möglichkeiten von "POV-Ray(TM)" aber noch lange nicht erschöpft. Daher lernen wir in diesem Abschnitt weitere Formen und Figuren kennen, bei denen es sich um torus, superellipsoid, lathe, prism und isosurface handelt. Bei vielen dieser Formen helfen Grundkenntnisse in Mathe, weshalb mancher von euch jetz denkt:

Zitat "You know you have been raytracing too long when...
...You find yourself wishing you'd paid attention in math class to all those formulae you thought you'd never have any use for in real life.
"
- Jeff Lee (Forum auf www.povray.org) -

Torus: Wir backen einen Donut

Wer sich ein wenig mit Mathematik auskennt, der wird wissen, dass ein Donut beim Bäcker in der Mathematik einem Torus entspricht. Um genau diese Figur soll es jetzt gehen. Bevor wir uns jetzt genauer anschauen, wie so ein Torus erstellt wird, schicke ich ein Beispiel voraus:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 5, -10> look_at <0,0,0> }
        light_source{ <0, 6, -10> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        torus{ 2,.8 pigment{Blue} translate 2*y }
        torus{ 4,.3 pigment{Red} }
        

Noch ohne das Bild zu erstellen fällt auf, dass ein Torus lediglich zwei Angaben benötigt. Um zu verdeutlichen, was die beiden Angaben bewirken, haben wir gleich zwei verschiedene Tori erstellt. Bevor wir den Code näher betrachten, schauen wir uns zunächst das Ergebnis an:

Beispiel für Torus

Zu sehen ist ein großer, roter Torus, mit schmalem Bauch und ein darüber schwebender kleinerer Torus, mit dickem Bauch und blauer Farbe. Genau das sind die zwei Angaben, die benötigt werden, um einen Torus zu erstellen. Die erste Zahl gibt dabei an, wie groß der Torus selbst wird, wobei die zweite Angabe über die Dicke des Bauches bestimmt. Mehr Angaben gibt es nicht, weshalb ein Torus immer am Mittelpunkt erstellt wird. Will man seine Postition ändern, so muss diese über die bekannten Transformationen wie translate oder rotate geschehen.
Ein Donut entspricht übrigens einem Torus mit ziemlich dickem Bauch, weshalb der blaue Torus schon fast als solcher durchgehen könnte. Allerdings muss noch die Zucker- oder Schoko-Glasur auf die Figur kommen. Das passiert über die im letzten Kapitel behandelten Texturen.

Superellipsoid: Die runde Ecke

Als nächstes wollen wir uns den Hyperparaboloid anschauen. Dabei braucht ihr euch aber wenig Sorgen machen, der Name klingt komplizierter als es der Körper wirklich ist. Doch schauen wir uns doch einfach mal ein Beispiel an:

        #version 3.6;
        #include "colors.inc"
        #include "textures.inc"

        camera{ location <3, 4, -3> look_at <0,2,0> }
        light_source{ <0, 6, -10> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        superellipsoid { <.2,.8> texture{Chrome_Metal} translate <-6,2,3> }
        superellipsoid { <.4,.4> texture{Chrome_Metal} translate <-3,2,4> }
        superellipsoid { <.8,.2> texture{Chrome_Metal} translate <0,2,5> }
        

In diesem Bild erstellen wir drei dieser Hyperparaboloiden, die jeweils im Glaze des Chromes strahlen. Auffällig ist auch hier, dass nur zwei Angaben möglich sind. Um zu beschreiben, was diese bewirken, schauen wir uns zunächst das entstandene Bild an:

Beispeil eines Hyperparaboloiden

Während die ganz linke Form an einen etwas deformierten Zylinder erinnert, handelt es sich in der Mitte schlicht um ein Rechteck mit abgerundeten Ecken. Der Körper ganz links hat dagegen eine abgerundete, angespitzte Front.

Stark vereinfacht lässt sich sagen, dass die erste Angabe die Stärke der Rundung in x-Richtung vorgibt, währed durch den zweiten Wert die Rundung in z-Richtung beeinflusst wird. Würden wir unserem ersten Hyperparaboloiden daher die Werte <1,.1> geben, würden wir tatsächlich einen Zylinder mit abgerundeter Kante erhalten, währed <1,1> eine Kugel erzeugt.
Die Größe des Hyperparaboloiden ist dabei immer 2x2x2 Einheiten. Will man den Hyperparaboloiden in einer anderen Größe erstellen, so muss man seine Größe mit scale anpassen.
Auch wenn sich mit dieser einfachen Beschreibung das Erzeugen von Hyperparaboloiden schon recht gut verstehen lässt, will ich für die Mathematiker unter euch noch einen Hinweis los werden. So wird der Hyperparaboloid durch folgende Formel vorgegeben:

Formel vom Hyperparaboloiden

Dabei sind m und n die vorgegeben Parameter aus der Definition superellipsoid{<m,n>}. Sie bestimmen die Form des Hyperparaboloiden und wie man an der Gleichung leicht sieht, müssen sie ungleich Null sein, die sie in der Gleichung im Nenner stehen. Sehr kleine Werte von m und n sind zwar mathematisch zugelassen, verursachen jedoch unerwartete, meist unerwünschte Ergebnisse. Ihr könnt aber gerne mal damit herum spielen.

Lathe: Runder geht es nicht

In diesem Abschnitt soll es um Rotationskörper gehen. Falls euch das jetzt kein Begriff ist, will ich nicht lange versuchen das mit Worten zu beschreiben, sondern ich schicke einfach ein kleines anschauliches Beispiel voraus:

Beispeil für Rotationskörper

Auf dem Bild sehen wir eine unförmigem Figur, die entfernt an ein Glas oder eine Vase erinnert. Doch was auch immer das Bild darstellen soll, die Figur ist in jedem Fall rotationssymmetrisch bezüglich der y-Achse. Genau das definiert einen Rotationskörper. Ihr seht also, so schlimm wie es sich anhört ist es gar nicht. Zumindest noch nicht, denn schauen wir uns einmal an, wie obiges Bild entstanden ist.

        #version 3.6;
        #include "colors.inc"

        camera{ location <3, 4, -5> look_at <0,3.5,0> }
        light_source{ <0, 6, -10> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        lathe { quadratic_spline 11,
        <2,0>, <1.6,1>, <1.5,2>, <1.6,2.5>, <1.8,3>, <1.6,3.5>, <1.1,4>,
        <0.8,4.5>, <1,5>, <1.5,6>, <2,7> pigment{Red} scale.4 translate 1*y }
        

In dieser Szene gibt es, wie bereits gesehen, außer dem Hintergrund und dem Boden nur diesen einen Rotationskörper, der sich mit dem Befehl lathe erzeugen lässt. Es folgt als erster Eintrag die Art der Verbindungslinien. In unserem Fall ist das quadratic_spline, also wird "POV-Ray(TM)" versuchen die angegeben Punkte durch Parabeln (quadratische Funktionen) miteinander zu verbinden. Als nächstes muss die Zahl der Punkte angegeben werden, in unserem Fall sind das 11 Stück. Es folgt dann die Angabe eben dieser 11 Koordinaten, wobei nur ein x- und ein y-Wert anzugeben ist. Die z-Koordinate ergibt sich automatisch aus der Symmetrie der Figur.
Da wir als Verbindung quadratische Funktionen gewählt haben, bekommt die Figur diese geschwungene Form. Zu beachten ist dabei jedoch, dass der erste angegebene Punkt nicht in der Figur auftaucht. Dieser wird lediglich verwendet, um den Verlauf der Kurve zu bestimmen. Daher hat er durchaus einen Einfluss auf die Form der Figur, auch wenn er nicht direkt dargestellt wird.

Außer quadratischen Funktionen gibt es noch die linear_spline, also einen linearen Verlauf zwischen den Punkten, sowie die cubic_spline, die unsere Punkte mit kubischen Funktionen (x3) verbindet.
Bei der linearen Verbindung wird dabei jeder definierte Punkt angezeigt, währed die kubische Funktion sowohl den ersten als auch den letzten Punkt lediglich zur Bestimmung des Kurvenverlaufs nutzt. Der Übersicht wegen gibt es das Ganze zusammengefasst in folgender Tabelle:

Angabe Art der Kurve Besonderheiten
linear_spline linearer Verlauf keine
quadratic_spline Parabeln erster Punkt wird nicht dargestellt
cubic_spline kubische Funktion erster und letzter Punkt werden nicht dargestellt
bezier_spline quadratische Funktion angegebene Punkte geben lediglich die Krümmung vor

Die bezier_spline wurde bisher nicht betrachtet. Bei dieser handelt es sich um eine Sonderform der quadratic_spline. Hier geben die Punkte lediglich die Krümmung der entsprechenden quadratischen Funktion vor. Das Ergebnis kann man sich wie ein Seil vorstellen, das an zwei Punkten eingespannt ist. Je nach Lage der einzelnen Punkte wird nun mehr oder weniger stark nach links oder rechts an dem Seil gezogen, wobei sich entsprechend Bäuche ausbilden.
Doch schauen wir uns die Unterschiede am Besten direkt im Vergleich an:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 5, -6> look_at <0,4,0> }
        light_source{ <0, 6, -10> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        lathe { linear_spline 8,
        <2.2,0>, <1,.5>, <.3,1>, <.3,2> <.3,3>, <.5,3.5>, <1.5,5>, <2,7>
        pigment{Red} scale.4 translate<-4,1,3> }

        lathe { quadratic_spline 8,
        <2.2,0>, <1,.5>, <.3,1>, <.3,2> <.3,3>, <.5,3.5>, <1.5,5>, <2,7>
        pigment{Blue} scale.4 translate<-1.5,1,3> }

        lathe { cubic_spline 8,
        <2.2,0>, <1,.5>, <.3,1>, <.3,2> <.3,3>, <.5,3.5>, <1.5,5>, <2,7>
        pigment{Yellow} scale.4 translate<1.5,1,3> }

        lathe { bezier_spline 8,
        <2.2,0>, <1,.5>, <.3,1>, <.3,2> <.3,3>, <.5,3.5>, <1.5,5>, <2,7>
        pigment{Green} scale.4 translate<4,1,3> }
        

Dabei haben wir exakt die selben Koordinaten jeweils mit linearen, quadratischen und kubischen Linien verbunden. Das Ergebnis sind ganz verschiedene Kelche. Beim roten Kelch sind die Punkte dabei durch Geraden, beim blauen durch Parabeln, beim gelben durch kubische Funktionen und beim grünen Kelch als spezielle quadratische Funktionen verbunden. Es ergibt sich dabei ein ganz interessantes Bild:

Drei Rotationskörper

Prism: Wir bauen ein Vieleck

In diesem Abschnitt geht es um eine Art von Gegenständen, die ganz ähnlich zu den Rotationskörpern erstellt werden. Allerdings sind sie nicht rund, sondern viel mehr kantig:
Es geht um Vielecke, auch Prismen genannt. Schauen wir uns also die Definition eines Vielecks an, um am konkreten Beispiel zu sehen, was möglich ist.

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 6, -5> look_at <0,3,0> }
        light_source{ <0, 6, -10> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        prism{ linear_sweep linear_spline 0, 2, 7
        <-1,0>, <-2,1>, <-1,2>, <1,2>, <2,1>, <1,0>, <-1,0>
        pigment{Blue} translate 4*z }
        

Dabei haben wir beim Prisma zunächst den linear_sweep, der angibt welchen Verlauf das Prisma entlang der y-Achse hat. In unserem Fall ist das ein linearer Verlauf. Alternativ kann man auch einen conic_sweep wählen, also eine Pyramide mit einem Vieleck als Grundfläche erstellen.
Anschließend wählen wir mit welcher Linienart die einzelnen Punkte verbunden werden. Der Einfach heit halber haben wir einen linearen Verlauf gewählt. Prinzipiell sind hier jedoch alle Kurven möglich, die wir beim Rotationskörper kennen gelernt haben.
Die drei Zahlen stehen für die y-Koordinate der untersten Ebene, sozusagen dem Boden. In unserm Fall ist das die "0". Gefolgt wird sie von der Angabe der obersten Ebene, also dem Dach des Prismas. Wir haben hier "2" gewählt, unser Prisma reicht also von y=0 bis y=2 und ist damit zwei Einheiten hoch. Zuletzt kommt nun noch die Angabe der Punkte die das Prisma aufspannen. Wir haben sieben Punkte gewählt, die dann in der nächsten Zeile definiert werden. Wichtig ist dabei, dass die letzte Koordinate mit der ersten übereinstimmt. Ein Prisma muss nämlich immer geschlossen sein. Damit haben wir das Prisma im Prinzip verstanden. Bevor ich jetzt noch ein paar Detail anspreche, wollen wir uns zunächst das Bild anschauen, das wir gerade erzeugt haben:

Beispiel Prisma

Wie erwartet sehen wir ein Sechseck, das zwei Längeneinheiten hoch ist. An sich ist das recht unspektakulär, doch stellt es immerhin nur die einfachste Version eines Prismas dar. Bevor wir jetzt übergehen und ein paar aufwendigere Gebilde erzeugen wollen, seien ein paar Hinweise vorweg geschickt:

  1. quadratic_spline: Wie beim Rotationskörper wird die erste Koordinate nur zur Bestimmung der Kurvenform verwendet. Um das Prisma zu schließen muss also der letzte und der zweite Punkt übereinstimmen.
  2. cubic_spline: Hier wird der erste und der letzte Punkt nicht angezeigt, es müssen also der vorletzte und der zweite Punkt übereinstimmen.
  3. conic_sweep: Für die Höhe der Pyramide ist immer eine Einheit (1LE) vorgesehen. Größere Werte führen zu unerwarteten Figuren. Will man eine Pyramide mit mehr als 1LE Höhe, so muss sie durch scale entsprechend angepasst werden.
  4. conic_sweep: Will man eine Pyramide mit abgeflachter Spitze haben, so muss der erste Wert größer als Null (z.B. 0.2) gewählt werden.
  5. Prisma mit Ausschnitt: Ein Prisma mit einem ausgeschnittenen Innenraum lässt sich sehr einfach erzeugen. Dazu muss nach dem geschlossenen Prisma einfach noch einmal ein darin liegendes Prisma durch weitere Punkte definiert werden.

Jetzt aber genug der Theorie, schauen wir uns einmal ein paar Beispiele an, um die Anmerkungen besser zu verstehen. Dazu erstellen wir folgendes Bild:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 8, -5> look_at <0,5,3> }
        light_source{ <0, 15, -5> color White }
        light_source{ <0,1,-8> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        prism{ linear_sweep linear_spline 0, 1, 21
        <2,-4>, <-2,-4>, <-6,0>, <-2,4>, <2,4>, <6,0>, <2,-4>
        <1.5,-3>, <-1.5,-3>, <-5,0>, <-1.5,3>, <1.5,3>, <5,0>, <1.5,-3>
        <1,-2>, <-1,-2>, <-3,0>, <-1,2>, <1,2>, <3,0>, <1,-2>
        pigment{SeaGreen} translate<-4,0,12> }

        prism{ conic_sweep quadratic_spline 0, 1, 6
        <2,0>, <1,-1>, <-3,0>, <1,2>, <3,0>, <1,-1>
        pigment{Khaki} scale<1.5,3,1.5> translate<0,1,18> }

        prism{ conic_sweep cubic_spline 0.1, 1, 7
        <2,-2>, <1,-2>, <-1,-3>, <-2,0>, <3,2>, <1,-2>, <0,-1>
        pigment{Wheat} scale<1.2,3,2.2> rotate<180,20,0> translate<5,3,10> }
        

Das erste Prisma ist dabei in drei Teile gegliedert. Die erste Zeile an sich ist schon ein geschlossener Körper. Eine Zeile darunter definieren wir dann ein weiteres Prisma, das im Inneren des ersten liegt und daher von diesem Abgezogen wird. Das dritte Prisma liegt nun im Hohlraum und wird daher wieder als Gegenstand dargestellt.
Die beiden anderen Prismen sind Pyramiden mit verschiedenen Grundflächen, wobei die zweite Pyramide eine abgeschnittene Spitze enthält. Damit ergibt sich folgendes Bild:

Mehrere Prismen

Zu erkennen ist, dass die gelbe Pyramide auf dem Kopf steht. Dies ist noch so eine Besonderheit des conic_sweep. Es wird immer die untere Ebene auf einen Punkt konzentriert. Daher müssen wir diesen auch >0 wählen, wenn wir ein abgeflachtes Ende der Pyramide haben wollen. Die weiße Pyramide ist daher um 180° gedreht und steht richtig herum auf dem Boden. Der Umgang mit einem Prisma ist also durchaus mit ein paar Besonderheiten versehen. Mit ein wenig Übung bekommt man diese aber gut in den Griff.

Isosurface: Die schönsten Berge

Wenn ihr die Definitionen der vorangestellten geometrischen Körper als kompliziert und schwer verständlich empfunden habt, solltet ihr diesen Abschnitt überspringen und direkt mit dem nächsten weiter machen.
Das hier vorgestellte isosurface wird nämlich über konkrete, mathematische Funktionen definiert. Man sollte also zumindest etwas Wissen über Funktionen mitbringen. Wenn man das isosurface jedoch beherrscht, kann man damit jedoch richtig schöne Figuren erzeugen. Doch was ist ein isosurface?

Hier haben wir schon das erste Problem. Ich kann euch jetzt nicht einfach ein Bild mit DEM isosurface schlechthin zeigen. Diese Klasse von Figuren ist nämlich Äußerst felxibel, man kann kann damit eine Vielzahl ganz verschiedener Figuren generieren. Schauen wir uns daher doch zunächst einmal die Definition an:

        isosurface{ function{...} contained_by{box{<x1,y1,z1>, <x2,y2,z2>}}
        max_gradient 5 }
        

Zunächst muss dabei die Funktion angegeben werden. Das könnte dann z.B. x2 + y2 sein, oder jede andere mathematische Funktion.
Anschließend geben wir den Gegenstand an, der die Funktion enthält. Möglich ist dabei eine box oder eine sphere. Die Form beeinflusst dabei die Ränder des isosurface, währed die Größe tatsächlich beeinflusst wie groß die generierte Figur wird. Zuletzt folgt die maximale Steigung, wobei diese Angabe auch weggelassen werden kann. Trotzdem sollte man sie angeben, da "POV-Ray(TM)" die zu generierende Form nur näherungsweise bestimmt und durch die Angabe der maximalen Steigung das Ergebnis ansehnlicher wird. Jetzt aber genug der Theorie, gehen wir ans Werk:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 6, -8> look_at <0,4,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        isosurface{ function{pow(x,2)+y} contained_by{box{-4, 4}}
        max_gradient 5 pigment{Magenta} rotate -30*y translate<-6,4,10>}

        isosurface{ function{pow(x,2)+pow(z,2)+y} contained_by{box{-4, 4}}
        max_gradient 8 pigment{Red} translate<6,4,7>}

        isosurface{ function{pow(x,2)+sin(z+2)+y}
        contained_by{box{<-4,-4,-12>, <4,4,12>}} max_gradient 8
        pigment{Yellow} rotate 90*y translate<0,4,16>}

        isosurface{ function{abs(x)+y} contained_by{sphere{0, 3}}
        max_gradient 8 pigment{SeaGreen} rotate 30*y translate<0,4,12>}
        

Auf eine genaue Erklärung des erzeugten Bildes will ich hier verzichten, da sprechen Bilder einfach für sich. Bevor wir und jedoch das Ergebnis anschauen, noch zwei Hinweise:

  1. Bei der Angabe der Koordinaten für die box und sphere haben wir auf die Kurzschreibweise zurück gegriffen. Dabei gilt "-4 = <-4,-4,-4>" - ein Vektor mit lauter gleichen Einträgen lässt sich auch durch eine einzelne Zahl darstellen.
  2. Potenzen lassen sich über pow(x,m) darstellen, wobei mit m der Exponent angegeben wird. Daher ist pow(x,2) also eine Parabel. Natürlich sind auch Funktionen wie sin(x), ln(x) oder cosh(x) möglich, wobei sich eine Liste aller Funktionen im Anhang findet.

Doch jetzt genug geredet, schauen wir uns das Bild an und genießen das Ergebnis unserer Mühen!

Verschiedene isosurface

Constructive Solid Geometry

Beim Raytracing können nicht nur einzelne Objekte platziert werden, sondern es ist auch möglich, diese miteinander zu verbinden. Dazu gibt es verschiedene Möglichkeiten. So bildet ein sphere_sweep eine Linie aus vielen Kugeln, während der blob zwei Objekte mit einer Art "Gummiband-Effekt" verbindet. Dazu lassen sich per Constructive Solid Geometry (CSG) Objekte beliebig zusammenfassen. Dazu stehen die Funktionen union, merge, intersection und difference zur Verfügung. Damit machen wir uns nicht nur das Leben leichter, sondern es werden völlig neue Möglichkeiten eröffnet. Damit setzt "POV-Ray(TM)" der Kreativität keinerlei Grenzen. Eine Warnung sei allerdings noch gegeben:

Zitat "You know you have been raytracing too long when...
...Your friends are used to the fact that you will suddenly stop walking in order to look at objects and figure out how to do them as CSGs.
"
- Jeff Lee (Forum auf www.povray.org) -

Sphere_sweep: Röhren und Schläuche

Wie Eingangs erwähnt, besteht der sphere_sweep aus Kugeln. Doch wie werden diese angeordnet und was für eine Form kommt dabei heraus? Um diese Fragen zu beantworten schauen wir uns zunächst an wie sich diese Form erstellen lässt:

        sphere_sweep{ linear_spline 5
        <-2,5,2>,1, <2,5,1>,1 ,<2,1,0>,.6 ,<-2,1,0>,.6 ,<-5,2,6>,1
        tolerance 1.2e-4 }
        

Zunächst muss eine Linienart sowie die Anzahl der verwendeten Punkte angegeben werden. Das gibt uns schon eine Idee um was es hier geht: Wir geben eine Linie vor, die von "POV-Ray(TM)" dann mit Kugeln nachgezeichnet wird. Man kann sich das vorstellen, wie eine dünne Bleistiftlinie, die wir vorgehen und das Programm tupft die Linie mit einem Runden Tinten-Stift nach. Mit dem einzigen Unterschied, das hier alles in 3D passiert.
Entsprechend der Erklärung geben wir jetzt die Punkt an, die unsere Bleistiftlinie vorgeben. Es folgt der Radius der Kugel, was in obigem Beispiel die Dicke des verwendeten Tinten-Schreibers vorgibt. Zuletzt kommt dann noch die Angabe der Toleranz. Damit "erlauben" wir dem Programm beim nachzeichnen etwas zu "zittern", also ein wenig von der vorgegeben Linie abzuweichen. Diese Angabe sollte im Normallfall sehr klein ausfallen, damit es zu keinen sichtbaren Effekten kommt. Wichtig ist sie dennoch, da die Angabe einer Toleranz das Berechnen der Form beschleunigt.
Nachdem wir jetzt also wissen, wie man ein sphere_sweep erstellt, schreiten wir gleich zur Tat:

        #version 3.6;
        #include "colors.inc"
        #include "finish.inc"

        camera{ location <0, 6, -8> look_at <0,4,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        sphere_sweep{ linear_spline 6
        <-3,5,8>,1, <1,5,7>,1 ,<1,1,6>,.6, <-2,1,6>,.6,
        <-5,2,12>,1, <5,2,10>,1.5
        tolerance 1.2e-4 pigment{SeaGreen} finish{Dull} }

        sphere_sweep{ linear_spline 5
        <-1.8,1,0>,1, <-1.5,.5,0>,1, <-1,0,0>,1, <-.5,.5,0>,1, <-.2,1,0>,1
        tolerance 1.2e-4 pigment{rgbf<1,0,0,.6>} finish{Shiny}
        scale <1,1,.3> translate<3,6,3> }

        sphere_sweep{ b_spline 7
        <0,-2,0>,.2, <1,0,-.4>,.2, <-.6,1.5,.8>,.2, <.8,3,.2>,.2,
        <0,4.5,-.6>,.2, <-.8,6,0>,.2, <0,7,0>,.2
        tolerance 1.2e-4 pigment{Blue} scale <.16,0.6,.16> translate<2,1.5,3> }
        

Während der erste sphere_sweep ein einfaches Röhrensystem darstellt, zeigen die beiden anderen Formen, was mit dieser Definition noch alles möglich ist. Doch schaut einfach selbst:

Beispiele für sphere_sweep

Blob: Gummiband-Effekt

Im Folgenden wollen wir und nicht um den aus der Werbung bekannten Rahm-Spinat "mit dem Blubb" kümmern, sondern viel mehr einen blob kennen lernen. Dabei geht es um eine Figur, die man am besten mit einem Hauch Physik erklären kann: Stellt euch vor, ihr habt zwei elektrisch geladene Kugeln, wobei die eine positiv und die andere negativ geladen ist. Wie ihr hoffentlich aus dem Physikunterricht wisst, ziehen sich die beiden Kugeln wegen ihrer verschieden Ladung an. Die Anziehungskraft nimmt dabei ab, je weiter sich die beiden Kugeln entfernen. Jetzt stellt ihr euch noch vor, ihr könntet das elektrische Feld sehen, das zu der Anziehung führt. Genau dann wisst ihr wie ein blob entsteht. Für alle die jetzt Schwierigkeiten haben sich das vorzustellen, hier eine kleine Hilfestellung:

Ein blob

Wie man deutlich sieht, ist der Überlapp der Kugeln umso größer, je näher sie zusammen kommen. Genau so sollte es auch sein, da sich die Kugeln immer stärker anziehen, wenn sich sich aneinander annähern. Wichtig für Physiker ist dabei zu beachten, dass hier nicht das komplette elektrische Feld dargestellt wird, sondern lediglich die Komponenten, zur Anziehung der beiden Kugeln beitragen.
Jetzt haben wir uns aber genug mit Physik beschäftigt. Viel interessanter wird für euch sein, wie man so einen blob denn nun erstellt. Nichts leichter als das, schaut her:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 6, -8> look_at <0,4,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        blob{ threshold 0.4
         sphere{ <-2, 2, 0>, 2.5, .6 pigment{Red} }
         sphere{ <2, 2, 0>, 2.5, .6 pigment{Blue} } }

        blob{ threshold 0.4
         sphere{ <-1.5, 3, 6>, 2.5, .6 pigment{Red} }
         sphere{ <1.5, 3, 6>, 2.5, .6 pigment{Blue} } }

        blob{ threshold 0.4
         sphere{ <-1, 5, 12>, 2.5, .6 pigment{Red} }
         sphere{ <1, 5, 12>, 2.5, .6 pigment{Blue} } }
        

Schön am blob ist, dass es nicht viel zu beachten gibt. Als erster Wert muss der threshold angegeben werden. Dieser bestimmt dabei, ab welcher "Feldstärke" etwas vom blob zu sehen ist. Wählt ihr den Wert zu groß, kann es sein, dass lediglich zwei Kugeln dargestellt werden. Zu kleine Werte führen jedoch zu sehr großen Formen, die den hübschen "Gummiband-Effekt" (siehe mittlere Kugeln in obigem Bild) überdecken können.
Anschließend wird die Form definiert, die erstellt werden soll. Dabei sind jedoch lediglich sphere und cylinder möglich. Neu dabei ist, dass nach der Angabe des Radius der Kugel noch ein Wert steht. Dieser gibt einfach die Stärke der Anziehung zwischen den Kugeln an. Hier sind übrigens auch negative Werte möglich, um eine Abstoßung zu simulieren.
Damit ist der blob dann auch schon fertig definiert. Mehr gibt es hier auch nicht, außer dem Hinweis, dass sich mit Hilfe des blob sehr schöne organische Formen erstellen lassen. So sind etwas Birnen oder gar eine Hand denkbar. Im nächsten Abschnitt gibt es dazu ein sehr schönes Beispeil.

Arbeiten mit Definitionen

In diesem Abschnitt soll es um keine neue Form gehen, sondern darum, wie man Formen ohne großen Aufwand immer wieder erzeugen kann:

        #declare Rund = box{ -2,2 };  //Definiert das Objekt
        object{ Rund pigment{Yellow} }  //Zeigt das Objekt
        

Dabei haben wir das Objekt "Rund" definiert, das einem Quader entspreich. Während wir damit Probleme haben könnten, stört sich "POV-Ray(TM)" überhaupt nicht daran. Der Begriff hinter #declare dient lediglich der Identifikation des Gegenstandes und kann daher jeder beliebige Begriff sein, zumindest solange er keine Sonderzeichen oder Umlaute enthält. Zu Beachten gilt allerdings, dass ab Version 3.7 jedes #declare mit einem Strichpunkt (;) beendet wird. Ansonten gibt "POV-Ray(TM)" eine Fehlermeldung aus.
Es muss jedoch beachtet werden, dass der Gegenstand in der ersten Zeile nur definiert wird. Die Platziegung im Bild erfolgt über einen extra Befehl, der in der zweiten Zeite steht. Außerdem geben wir dem Objekt in dieser Zeile eine Farbe. Ebenso kann das Objekt in dieser Zeile beliebig verschoben und deformiert werden.
Das war dann auch schon die ganze Kunst und wir sind damit bereit das neu erlernte Wissen im nächsten Abschnitt anzuwenden. Bevor wir jedoch in die Arbeit mit CSG einsteigen, will ich noch einen passendes Zitat vorweg schicken:

Zitat "You know you have been raytracing too long when...
...Your friends are used to the fact that you will suddenly stop walking in order to look at objects and figure out how to do them as CSGs.
"
- Jeff Lee (Forum auf www.povray.org) -

CSG: Formen verbinden

Es gibt verschiedene Möglichkeiten, Formen und Gegenstände miteinander zu verbinden. Mit union und mege lernen wir in diesem Abschnitt zwei davon kennen. Dazu erstellen wir ein Bild mit verschiedenen Arten des blob und nutzen auch gleich das vorhin kennen gelernte declare aus:

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 8, -10> look_at <0,4,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        #declare Right_Arm = blob {
          threshold 0.25
          sphere { <-1.2,.4,0>,1.4 ,1.4 }
          sphere { <0,0,0>, 1.3, 1.8 }
          sphere { <0,-1.1,0>, 1.2, .8 scale <1.2,2,1.4>}
          sphere { <0,-3.5,.1>, .6, 1.4 }
          cylinder{ <0,-4.0,-.4>, <0,-5.8,-1.4>, 1, 1.8 }
          cylinder{ <0,-6.9,-2.15>, <0,-7.2,-2.7> 1.2, .8 } };

        #declare Left_Arm = blob {
          threshold 0.25
          sphere { <1.2,.4,0>,1.4 ,1.4 }
          sphere { <0,0,0>, 1.3, 1.8 }
          sphere { <0,-1.1,0>, 1.2, .8 scale <1.2,2,1.4>}
          sphere { <0,-3.5,.1>, .6, 1.4 }
          cylinder{ <0,-4.0,-.4>, <0,-5.8,-1.4>, 1, 1.8 }
          cylinder{ <0,-6.9,-2.15>, <0,-7.2,-2.7> 1.2, .8 } };
 
        #declare Body = union {
          superellipsoid { <.4,.4> scale <6,4,4> }
          superellipsoid { <.4,.4> scale <6,4,4> translate -4*y }
          superellipsoid { <.4,.4> scale <6.2,2.4,4.1> translate -8.5*y }
          object{Right_Arm scale 1.8 translate <7.5,1.8,-.5>}
          object{Left_Arm scale 1.8 translate <-7.5,1.8,-.5>} };

        object{Body scale .5 translate<-8,6,20> pigment{Red}}
        object{Body scale .5 translate<8,6,20> pigment{Blue}}
        

Das Bild mag auf den ersten Blick recht aufwenig aussehen, doch eigentlich besteht es lediglich aus zwei Formen von Typ blob sowie aus drei identischen superellipsoiden. Was genau auf dem Bild dargestellt wird, sollte schon aus den Definitionen klar werden. Genau das ist hier nämlich der erste neue Punkt: Wir haben die Funktion #declare verwendet, um die Datei zu strukturieren. Außerdem ist der Body als union ausgeführt. Wer etwas Englisch versteht, wird sicherlich schon ganz richtig vermuten was sich dahinter verbirgt. So lassen sich über diese Konstruktion einzelne Formen zu einem Gegenstand zusammenfassen. In unserem Fall haben wir die drei superellipsoiden sowie den Right_Arm und den Left_Arm zum Body vereint. Am Ende plazieren wir zwei dieser Figuren im Bild:

Ein Beispiel für CSG

Wie erwartet sehen wir auf dem Bild zwei Oberkörper, einen Roten und einen Blauen. Dabei zeigt sich eine Stärke des #declare: Eine so definierte Form lässt sich sehr schnell mehrere Male auf einem Bild platzieren. Kombiniert mit der Möglichkeit verschiedene Formen durch eine union zu verbinden lässt sich das Erstellen von Bildern stark vereinfachen.

Es gibt allerdings noch eine weitere Möglichkeit, um Formen zu einem Gegenstand zu verbinden. Dies ist über merge möglich. Um den Unterschied zwischen den beiden Möglichkeiten kennen zu lernen, erstellen wir folgendes Bild.

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 5, -10> look_at <0,3,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }
        box{ <-20,-1,12>,<20,8.5,14> pigment{brick Gray Red scale .5} }

        union{
        sphere{ -2,2 translate<-4,4,6> pigment{rgbf<1,1,0,.6>} }
        sphere{ -2,2 translate<-2.5,4,6> pigment{rgbf<1,0,1,.6>} } 
        torus{ 1.5,.6 rotate 90*x translate<-4.5,4,3.5>
        pigment{rgbf<0,0,1,.6>} } }

        merge{
        sphere{ -2,2 translate<2.5,4,6> pigment{rgbf<1,1,0,.6>} }
        sphere{ -2,2 translate<4,4,6> pigment{rgbf<1,0,1,.6>} } 
        torus{ 1.5,.6 rotate 90*x translate<1.5,4,3.5>
        pigment{rgbf<0,0,1,.6>} } }
        

Bevor wir uns nun das entstandene Bild anschauen sei noch ein Hinweis vorweg geschickt: Die beiden Figuren sind jeweils transparent, da der Unterschied zwischen union und merge nur bei gläsernen Formen zu sehen ist.

Unterschied zwischen merge und union

Der Unterschied besteht hier im Überlapp der jeweiligen Figuren. So wird bei der linken Form, die wir durch union erzeugt haben, die Überlappung im Innenteil angezeigt, während bei der rechten Form, die aus einem merge entstanden ist, davon nichts zu sehen ist. Wir können uns also aussuchen, ob der Innenteil gezeigt wird oder nicht. Damit lassen sich dann genau die gewünschten Ergebnisse erzielen. Daher haben auch beide Möglichkeiten durchaus ihre Berechtigung, auch wenn union sicherlich häufiger Verwendung finden wird.

CSG: Die virtuelle Schere

Es gibt bei "POV-Ray(TM)" nicht nur die Möglichkeit verschiedene Formen zu verbinden, man kann auch Teile von Formen entfernen. Dafür stehen difference und intersection zur Verfügung. An einem einfachen Beispiel wollen wir und dabei gleich mal anschauen, was man damit Schönes anstellen lässt.

        #version 3.6;
        #include "colors.inc"

        camera{ location <0, 5, -10> look_at <0,3,0> }
        light_source{ <0, 10, -2> color White }
        background{SkyBlue}
        plane{ y,0 pigment{checker Black White} }

        difference{
          sphere{ <-9,2,6>,2 pigment{Red} }
          sphere{ <-8,2,6>,1.5 pigment {Blue} } rotate 40*y }

        intersection{
          sphere{ <-2,2,6>,2 pigment{Red} }
          sphere{ <-1,2,6>,1.5 pigment {Blue} } rotate 40*y }
        

Während difference so ziemlich selbsterklärend sein sollte, da hier einfach die unten stehenden Figuren von der obersten Form abgezogen werden, ist intersection schon schwieriger zu verstehen. Doch schauen wir uns das entstande Bild an.

Beispiele für difference und intersection

Auf der rechten Seite ist die difference zu sehen und wie erwartet findet sich in der ersten Kugel eine "Beule" in der Größe der Überlappung mit der blauen Kugel. Übrigens ist die "Beule" auch aus genau diesem Grund blau gefärbt. Sie übernimmt nämlich die Farbe des Gegenstandes, der sie erzeugt.
Die linke Form scheint etwas seltsam, sie entsteht, wenn einzig der Bereich angezeigt wird, in dem sich beide Kugeln überlappen. Es entsteht eben genau das, was intersection aussagt: Eine Überscheidung mehrerer Gegenstände.
Schon an diesem einfachen Beispiel wird deutlich welche Möglichkeiten sich hier eröffnen. Durch difference und intersection lassen sich völlig neue Figuren erstellen. Natürlich lassen sich diese dann auch durch union sowie merge mit weiteren Formen verbinden. Der Fantasie sind hier keine Grenzen gesetzt.

Beispiel

Um all unser bisheriges Wissen zur Anwendung zu bringen, wollen wir nun ein besonders schönes Motiv erstellen. Dabei handelt es sich um den Zug einer Holzbahn. Dazu benötigen wir nahezu alle Formen und Figuren zum Einsatz, die wir bisher kennengelernt haben. Manche sind dabei allerdings in Details versteckt, die euch erst beim genaueren Hinschauen auffallen werden. Doch machen wir uns einfach mal an die Arbeit:

Beispiel: Die Holzbahn

Eine Warnung sei vorweg geschickt: Bis der Holzzug mit Lok und zwei Wagen fertig ist, bedarf es eine Menge Zeilen Code. Ihr könnt aber gerne klein anfangen und zunächst nur die Lok oder einen Wagen ertellen. Dazu sind die einzelnen Definitionen gut verständlich benannt. Jetzt aber viel Spaß beim rendern!

        #version 3.6;
        #include "colors.inc"
        #include "textures.inc"
        #include "finish.inc"

        //-------------------- Einstellungen & Ambiente --------------------

        background{ color SkyBlue }
        camera{ location <0, 1, -3> look_at <0,1.1,2> }
        light_source{ <10, 20, -50> White }
        light_source{ <10, 20, 50> White }

        //-------------------------- Definitionen --------------------------

        #declare Wheel = sphere { <0,0.4,0>, 1 scale <1.2,1.2,.20>
          pigment {Red} finish{Shiny} };

        #declare Puffer = union {
          cylinder{ <0,0,0>,<-.4,0,0>,.2 }
          cylinder{ <-.4,0,0>,<-.5,0,0>,.5 pigment{Red} } };

        #declare Kupplung = union {
          intersection {
            isosurface { function {.02*pow(z,4)+.02*pow(x,4)+y}
            contained_by {box{-3,3}} threshold -1 max_gradient 18
            max_trace 4 texture{Chrome_Metal} translate 3.7*y }
            cylinder{ <0,0,0>,<0,5,0>,2.6 } }
          difference {
            cylinder{ <0,0,0>,<0,2.2,0>,2.8 }
            cylinder{ <0,-.1,0>,<0,2.3,0>,2.2 }
            pigment{Blue} } scale .25 rotate <0,0,-90> };

        #declare Chassis_Lok = union {
          box { <-5.5,.3,-2.25>, <5.5,.8,2.25> }
          object{ Wheel translate <-3.8,0,2.4> }
          object{ Wheel translate <3.8,0,2.4> }
          object{ Wheel translate <0,0,2.4> }
          object{ Wheel translate <0,0,-2.4> }
          object{ Wheel translate <-3.8,0,-2.4> }
          object{ Wheel translate <3.8,0,-2.4> }
          object{ Puffer translate <-5.5,.5,1.6> }
          object{ Puffer translate <-5.5,.5,-1.6> } };

        #declare Kessel = difference {
          intersection {
            cylinder{ <-5.6,2,0>,<1.5,2,0>,2.3 }
            sphere{ <1.4,2,0>,7 } }
            box{ <-5.6,-1,-2.4>,<1.1,.79,2.4> } };

        #declare Vorderbau = union {
          object{Kessel}
          difference {
            cone{ <-4.2,4,0>,.5, <-4.2,7,0>,.7 }
            cylinder{ <-4.2,4,0>,<-4.2,7.2,0>,.4 } }
            cylinder{ <-1.2,4,0>, <-1.2,4.8,0>,1 }
            sphere{ <-1.2,4.8,0>,1} };

        #declare Roof = difference {
          cylinder{ <0,0,0>, <9,0,0>, 2.4 }
          box{ <-0.1,-0.1,-2.5>, <9.1,-2.5,2.5> } };

        #declare Housing = union {
          difference {
            box{ <1.5,.79,-2.25>,<5,6,2.25> }
            box{ <1.8,1,-2>,<4.8,5.8,2> }
            box{ <2.6,3.8,-2.5>,<4,5.1,2.5> }
            cylinder{ <1.4,4.8,1.3>,<2,4.8,1.3>,.65 }
            cylinder{ <1.4,4.8,-1.3>,<2,4.8,-1.3>,.65 } }
          object{ Roof scale <.48,.6,1> translate <1.1,5.8,0>
            pigment{Blue} finish{Shiny} } };

        #declare Tender = difference {
          prism { linear_sweep linear_spline 0.5, 4.8, 5
            <0,0>, <.5,0>, <1.6,3.2>, <0,3.2>, <0,0> }
          prism { linear_sweep linear_spline 0.7, 4.6, 5
            <0,0>, <.3,0>, <1.4,3.3>, <0,3.3>, <0,0> } rotate <-90,0,0> };

        #declare Dampflok = union {
          object{ Chassis_Lok texture{DMFWood2} }
          object{ Vorderbau pigment{Gray60} finish{Shiny} }
          object{ Housing pigment{rgb<1,.7,.01>} finish{Shiny} }
          object{ Tender pigment{rgb<1,.6,.1>} translate <5,.8,2.65> }
          object{ Kupplung translate <5.6,.8,0> } };

        #declare Long_Side = difference {
          box{ <0,0,0>, <8,3.5,0.5> }
          box{ <3.2,1.5,-.2>, <4.7,3,.6> }
          box{ <1,1.5,-.2>, <2.5,3,.6> }
          box{ <5.4,1.5,-.2>, <6.9,3,.6> } };

        #declare Short_Side = box{ 0, <0.5,3.5,3.5> };
        #declare Roof = difference {
          cylinder{ <0,0,0>, <9,0,0>, 2.4 }
          box{ <-0.1,-0.1,-2.5>, <9.1,-2.5,2.5> } };

        #declare Chassis_Wagen1 = union {
          box { <-4,0.3,-2.25>, <4,0.8,2.25> }
          object{ Wheel translate <-2.6,0,2.4> }
          object{ Wheel translate <2.6,0,2.4> }
          object{ Wheel translate <-2.6,0,-2.4> }
          object{ Wheel translate <2.6,0,-2.4> } };

        #declare Body = union {
          object{Long_Side translate <-4,0.8,-2.25>}
          object{Long_Side translate <-4,0.8,1.75>}
          object{Short_Side translate <-4,0.8,-1.75>}
          object{Short_Side translate <3.5,0.8,-1.75>} };

        #declare Wagen1 = union {
          object{ Chassis_Wagen1 texture{DMFWood2} }
          object{ Body }
          object{ Roof translate <-4.5,4.3,0> pigment{Blue} finish{Shiny} }
          object{ Kupplung rotate <0,180,0> translate <-4.1,.8,0> }
          object{ Kupplung translate <4.1,.8,0> } };

        #declare Holzzug1 = union {
          object{ Dampflok translate <-8,0,15> }
          object{ Wagen1 rotate <0,10,0> translate <3.1,-.02,14.5>
            pigment{rgb<.5,.8,.3>} finish{Shiny} }
          object{ Wagen1 rotate <0,20,0> translate <12.35,-.02,12>}
            pigment{rgb<.5,.9,.2>} finish{Shiny} };

        //----------------------- Platzieren der Objekte ---------------------

        plane { y, -1.5 pigment {checker Black White} }
        box{ <-20,-2,20>,<20,6.25,21> pigment{brick Gray75 Brown scale .15} } 
        object{ Holzzug1 scale .4 rotate <0,-30,0> translate <2,-1.23,8> }
        

Wer sich von den vielen Zeilen zur Erstellung des Bildes nicht abschrecken lässt, der erhält nicht nur ein wirklich hübsches Bild, sondern neben dem Herz-Luftballon aus dem Abschnitt sphere sweep, auch einen zweiten Gegenstand vom Titelbild. Doch genug der Worte, schaut selbst:

Ein Holzzug