HomeMapIndexSearchNewsAchivesLinksabout LF
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Carlos Calzada Grau 

Über den Autor
Ich habe einen  Abschluß in Informatik. Computergrafik fasziniert mich seit meinem ersten Spektrum Rechner. Ich bin wirklich begeistert von Linux, der Philosopie, die seiner Entwicklung zugrunde liegt und prinzipiell allem, was nichts mit Micro$oft zu tun hat. Daneben zählen noch Bonsais und Aquarien zu den Dingen, mit denen ich mich in meiner Freizeit beschäftige.

M@ail an den Autor

Inhalt:

  1. Einführung
  2. Eine einfache Szene
  3. Geradlinige Bewegung
  4. Parabelförmige Bewegung
  5. Literaturhinweise

Renderman III

[Ilustration]

Zusammenfassung:
Dies ist der dritte Artikel der Serie über Renderman (I , II). In diesem Teil wird eines der wichtigsten Themen beleuchtet: die Möglichkeit, Szenen mittels den Programmiersprachen C oder C++ zu modellieren und zu rendern.


Einführung

Die vorhergehenden Artikel machten deutlich, daß es zweifellos möglich ist, Szenen durch manuell erstellte Textdateien zu kreieren, es aber recht aufwendig ist. Man stelle sich zum Beispiel mal vor, es soll die Bewegung eines prellenden Balles in einer ".rib" Datei per Hand beschrieben werden!  Durch die Möglichkeit, Programme für die Modellierung und Animierung von Szenen in C und C++ zu schreiben, wird das Leben um einiges einfacher gemacht. Die vom Benutzer definierten Funktionen produzieren ein ".rib" Skript, das auf die Standardausgabe ausgegeben wird. Durch die Unix Pipes können die so erzeugten Renderman Befehle direkt an andere Prozesse (wie rendrib, rendribv, rgl) oder auch in eine ".rib" Datei umgeleitet werden.

Durch die frühere Installation von Blue Moon Rendering Werkzeugen sind zwei neue Verzeichnisse erzeugt worden: lib und inlcude. Diese beinhalten vier Dateien, von denen momentan aber nur zwei relevant sind: ri.h, eine Headerdatei,  und libribout.a , die die Funktionsbibliothek darstellt. Die Headerdatei sollte in das Verzeichnis /usr/local/include und ribout.a nach /usr/local/lib (der etwas erfahrene Leser kann sie auch in andere Verzeichnisse installieren) kopiert werden. Nach der Installation der Bibliothek wird es nun Zeit für ein erstes Beispielprogramm.

Eine einfache Szene

Das erste Beispiel demonstriert grundlegende Techniken der Programmierung von Renderman. Wie in jeder C Quelldatei müssen die entsprechenden Headerdateien eingebunden werden, bevor irgendeine Bibliothek benutzt werden kann. In diesem Fall ist es die Datei ri.h. Desweiteren muß das Programm mit der Bibliothek gelinkt werden und zwar mittels:
gcc myprogram.c -o myprogram -lribout -lm
Hier ist ein  Makefile , durch welches man sich Tipparbeit unter der Konsole ersparen kann:
LIBS = -lm -lribout
PROGNAME = primero
         
all: $(PROGNAME)
        
$(PROGNAME).o: $(PROGNAME).c
        gcc -c $(PROGNAME).c
        
$(PROGNAME): $(PROGNAME).o 
        gcc -o $(PROGNAME) $(PROGNAME).o $(LIBS)

 

Im ersten Beispiel werden einige Koordinatenachsen, sowie ein Ball in ihrem Schnittpunkt erstellt. Hier ist der Quelltext der Datei primero.c:
 1 #include <stdio.h>
 2 #include <math.h>
 3 #include <ri.h>
 4
 5 void main(void)
 6 {
 7  int i;
 8  int x,y,z;
 9  int nf;
10  float slopex,slopey,slopez;
11
12  RtColor Rojo={1,0,0};
13  RtColor Verde={0,1,0};
14  RtColor Azul={0,0,1};
15  RtColor Blanco={1,1,1};
16
17  RtPoint p1={30,0,10}; /* Posicicion inicial de la pelota */
18  RtPoint p2={0,20,10}; /*   Posicion final de la pelota   */
19              
20  RtPoint from={0,100,100}; /*   Direccion de la luz       */
21  RtPoint to={0,0,0};
22
23  char name[]="primero.tif";
24  RtFloat fov=45;
25  RtFloat intensity1=0.1;
26  RtFloat intensity2=1.5;
27  RtInt init=0,end=1;
28              
29  RiBegin(RI_NULL);
30    RiFormat(320,240,1);
31    RiPixelSamples(2,2);      
32    RiShutter(0,1);
33    RiFrameBegin(1);
34     RiDisplay(name,"file","rgb",RI_NULL);
35     name[7]++;
36     RiProjection("perspective","fov",&fov,RI_NULL);
37     RiTranslate(0,-5,60);
38     RiRotate(-120,1,0,0);
39     RiRotate(25,0,0,1);
40     RiWorldBegin();
41       RiLightSource(ämbientlight","intensity",&intensity1,RI_NULL);
42       RiLightSource("distantlight","intensity",&intensity2,"from",from,"to",to,RI_NULL);
43       RiColor(Azul);
44       RiTransformBegin();
45         RiCylinder(1,0,20,360,RI_NULL);
46         RiTranslate(0,0,20);
47         RiCone(2,2,360,RI_NULL);
48       RiTransformEnd();
49       RiColor(Verde);
50       RiTransformBegin();
51         RiRotate(-90,1,0,0);
52         RiCylinder(1,0,20,360,RI_NULL);
53         RiTranslate(0,0,20);
54         RiCone(2,2,360,RI_NULL);
55       RiTransformEnd();
56       RiColor(Rojo);
57       RiTransformBegin();
58         RiRotate(90,0,1,0);
59         RiCylinder(1,0,20,360,RI_NULL);
60         RiTranslate(0,0,20);
61         RiCone(2,2,360,RI_NULL);
62       RiTransformEnd();
63       RiColor(Blanco);
64       RiSphere(5,-5,5,360,RI_NULL);
65     RiWorldEnd();
66    RiFrameEnd();
67  RiEnd();
68 };

Die ersten drei Zeilen binden wichtige Headerdatei ein, unter denen sich auch ri.h, die Headerdatei für die Prototypen der Renderman Bibliothek, befindet. Jedes Kommando von Renderman besitzt eine äquivalente C Funktion in der Datei ri.h, so hat etwa TransformBegin die Funktion RiTransformBegin() als entsprechendes Gegenstück, usw. Das Programm primero wird durch einen Durchlauf von make generiert. Der Aufruf des Beispielprogrammes erzeugt durch Umleiten der Standardausgabe (primero > primero.rib) eine Szenen-Datei. Wird dagegen die Ausgabe mittels einer Pipe zur Eingabe von rendrib (primero | rendrib), so rendert  dieses Programm ein entsprechendes Bild (primero.tif).

Die Aufrufe der Bibliotheksfunktionen müssen zwischen einem RiBegin(RI_NULL) und einem RiEnd() Aufruf stehen. Der Parameter, der an RiBegin übergeben wird, ist im allgemeinen RI_NULL. Damit keine RIB Ausgabe an die Standardausgabe geht, könnte man den Namen einer Ausgabedatei  ("myfile.rib") oder sogar den Namen eines Prozesses (zum Beispiel renderib) übergeben. Das Programm wird dann die Renderman Befehle an den Renderer weiterleiten, ohne daß eine RIB Datei für das Zwischenspeichern erzeugt wird.

Die Quelldatei des ersten Beispielprogrammes verwendet sowohl typische C Anweisungen, als auch Typen und Funktionen des Renderman Interfaces: Der Typ Rtcolor ist ein Vektor mit drei Komponenten vom Type Real, für die Farbwerte für Rot, Grün und Blau (zwischen 0.0 und 1.0). RtPoint beschreibt eine Position im Raum, RtFloat und RtInt sind die entsprechenden Typen.

In Zeile 29 wird RiBegin(RI_NULL) aufgerufen. Dadurch wird das Renderman Interface initialisiert. Von hier an folgen die Funktionen der Kommandos, die in einer typischen RIB Datei zu finden sind. Ein Beispielaufruf des Programmes und die Umleitung dessen Ausgabe in eine Datei (./primero > primero.rib) sollte wie folgt aussehen:
 
##RenderMan RIB-Structure 1.0
version 3.03
Format 320 240 1
PixelSamples 2 2
Shutter 0 1
FrameBegin 1
Display "camara.tif" "file" "rgb"
Projection "perspective" "fov" [45 ]
Translate 0 -5 60 
Rotate -120 1 0 0 
Rotate 25 0 0 1 
WorldBegin
LightSource ämbientlight" 1 "intensity" [0.1 ]
LightSource "distantlight" 2 "intensity" [1.5 ] "from" [0 100 100] "to" [0 0 0]
Color [0 0 1]
TransformBegin
Cylinder 1 0 20 360
Translate 0 0 20 
Cone 2 2 360
TransformEnd
Color [0 1 0]
TransformBegin
Rotate -90 1 0 0 
Cylinder 1 0 20 360
Translate 0 0 20 
Cone 2 2 360
TransformEnd
Color [1 0 0]
TransformBegin
Rotate 90 0 1 0 
Cylinder 1 0 20 360
Translate 0 0 20 
Cone 2 2 360
TransformEnd
Color [1 1 1]
Sphere 5 -5 5 360
WorldEnd
FrameEnd

Dieses erste Beispiel war nicht sehr brauchbar. Das nächste Programm ähnelt dem ersten Beispiel. Es demonstriert die Mächtigkeit der Bibliothek in  Sachen Animation. Wurde im ersten Beispiel nur ein einzelnes Bild erzeugt, so wird das nächste den Ball in Bewegung versetzen.

Geradlinige Bewegung:

In diesem Beispiel besteht die Szene wieder aus den drei Koordinatenachsen und einem Ball, allerdings wird sich diesmal der Ball bewegen, und zwar von dem Punkt (30,0,10) zu den Koordinaten (0,20,10), also von rechts nach links über den Schirm. Beide Koordinaten werden durch RtPoint Strukturen definiert (Zeile 18 und 19). Die Anzahl von Bildern (Frames) der Animation wird in der Variable nf definiert. Mit diesem Wert und der Start- und der Endposition kann die Schrittweite der Ballbewegung pro Frame in den drei Dimensionen (slopex, slopey und slopez) berechnet werden. Dies ist alles, was an Informationen für die Veränderung der Ballposition als Funktion der Framezahl nf benötigt wird. Zwischen den Zeilen 75 und 78 beschreibt ein TransformBegin/TransformEnd Abschnitt die Position des Balles. Die neue Position wird in jedem Schritt ganz einfach in Zeile 76 berechnet.
 
 1 #include <stdio.h>
 2 #include <math.h>
 3 #include <ri.h>
 4 #include "filename.h"
 5
 6 void main(void)
 7 {
 8  int i;
 9  int x,y,z;
10  int nf;
11  float slopex,slopey,slopez;  
12
13  RtColor Rojo={1,0,0};
14  RtColor Verde={0,1,0};
15  RtColor Azul={0,0,1};
16  RtColor Blanco={1,1,1};
17
18  RtPoint p1={30,0,10}; /* Posicicion inicial de la pelota */
19  RtPoint p2={0,20,10}; /*   Posicion final de la pelota   */
20      
21  RtPoint from={0,100,100}; /*      Direccion de la luz        */
22  RtPoint to={0,0,0};
23
24  char base[]="camara_";
25  char ext[]="tif";
26  char name[50];
27  RtFloat fov=45;
28  RtFloat intensity1=0.1;
29  RtFloat intensity2=1.5;
30  RtInt init=0,end=1;
31      
32  nf=100; /*        Numero de frames         */
33  slopex=(p2[0]-p1[0])/nf;
34  slopey=(p2[1]-p1[1])/nf;
35  slopez=(p2[2]-p1[2])/nf;
36
37  RiBegin(RI_NULL);
38    RiFormat(320,240,1);
39    RiPixelSamples(2,2);      
40    RiShutter(0,1);
41    for (i=1;i <= nf;i++)
42      {
43      RiFrameBegin(i);
44        filename(base,ext,sizeof(base)+4,i-1,name);
45        RiDisplay(name,"file","rgb",RI_NULL);
46        name[7]++;
47        RiProjection("perspective","fov",&fov,RI_NULL);
48        RiTranslate(0,-5,60);
49        RiRotate(-120,1,0,0);
50        RiRotate(25,0,0,1);
51        RiWorldBegin();
52          RiLightSource(ämbientlight","intensity",&intensity1,RI_NULL);
53          RiLightSource("distantlight","intensity",&intensity2,"from",from,"to",to,RI_NULL);
54          RiColor(Azul);
55          RiTransformBegin();
56              RiCylinder(1,0,20,360,RI_NULL);
57              RiTranslate(0,0,20);
58              RiCone(2,2,360,RI_NULL);
59          RiTransformEnd();
60          RiColor(Verde);
61          RiTransformBegin();
62              RiRotate(-90,1,0,0);
63              RiCylinder(1,0,20,360,RI_NULL);
64              RiTranslate(0,0,20);
65              RiCone(2,2,360,RI_NULL);
66          RiTransformEnd();
67          RiColor(Rojo);
68          RiTransformBegin();
69              RiRotate(90,0,1,0);
70              RiCylinder(1,0,20,360,RI_NULL);
71              RiTranslate(0,0,20);
72              RiCone(2,2,360,RI_NULL);
73          RiTransformEnd();
74          RiColor(Blanco);
75          RiTransformBegin();
76              RiTranslate(p1[0]+slopex*(i-1),p1[1]+slopey*(i-1),p1[2]+slopez*(i-1));
77              RiSphere(5,-5,5,360,RI_NULL);
78          RiTransformEnd();
79        RiWorldEnd();
80      RiFrameEnd();
81      }
82  RiEnd();
83 };

Als nächstes sollte dieses zweite Beispiel wie zuvor kompiliert und ausgeführt werden, wobei seine Ausgabe zum Beispiel an rendribv weitergeleitet werden kann. Dies ist ein einfacher Weg, eine Vorschau auf die Animation schnell und bei einer akzeptablen Framerate zu bekommen. Um sich die RIB Ausgabedatei anzuschauen, muß man nur die Standardausgabe in eine neue Datei  umleiten. Der Leser kann sich davon überzeugen, daß die erzeugte Datei recht groß ist  (segundo.rib belegt 70 KB), da die gleiche Szene hundertmal definiert ist (einmal für jeden Frame).

Folgende Abbildung zeigt einige Zwischenbilder der Animation:

Natürlich kann jedes Objekt animiert, seine Position und Größe verändert werden. Ebenso kann man die Intensität des Lichtes und die Kamera variieren,  Gegenstände auftauchen und verschwinden lassen, uvm.

Parabelförmige Bewegung:

Im letzten Beispiel wird der Ball auf dem Boden prellen. Zuerst wird die Funktion rebote() (bedeutet soviel, wie "hüpfen") definiert, die drei Paramter erwartet: die Nummer des aktuellen Frames, die Anzahl der Frames pro Prellbewegung und die maximale Höhe, die der Ball erreichen kann. Hier ist der Quelltext:
 
float rebote (int i, int nframes, int max)
{
  float min, z;

  while (i > nframes) i-=nframes;

  min=sqrt(max);

  z=i-((float)nframes/2.0);
  z=(z*min)/((float)nframes/2.0);
  z=(float)max - (z*z);
  return(z);
}

Durch ein paar einfache Berechnungen wird die typische Parabelkurve (y=x^2) auf die Zahl der Frames und die maximale Höhe angewendet. Folgende Abbildung zeigt wieder einige Zwischenbilder. Diese werden durch das Programm tercero.c pro Prellbewegung erzeugt:

Hier sind noch ein paar animierte GIF Dateien zu finden, die die Animationen veranschaulichen sollen.Auch wenn sie (zumindest unter Netscape) langsam sind, sollten sie unter xanim mit brauchbarer Geschwindigkeit laufen.

Geradlinige  Bewegung: segundo_anim.gif

Parabelförmige Bewegung: tercero_anim.gif

Mit diesem letzten Beispiel endet die Vorstellung der grundlegenden Nutzung der C Schnittstelle zu Renderman und seiner Programmierung. Die Programmierung von Shadern stellt hierbei die größte Herausforderung und die spektakulärste Anwendung dar. Durch diese erhät der Benutzer die totale Kontrolle über den letztendlichen Rendervorgang der Szene, da durch sie Texturen, Beleuchtung, usw. gesteuert werden können. 

Literaturhinweise



Übersetzung: Harald Radke
Páginas web mantenidas por Miguel Ángel Sepúlveda
© Carlos Calzada Grau 1998
LinuxFocus 1998