por Carlos Calzada Grau Sobre el Author: Soy licenciado en inform�tica y desde que me regalaron un Spectrum me han gustado los gr�ficos por ordenador. Soy muy aficionado a Linux, a su filosof�a de desarrollo y a todo lo que no tiene que ver con micro$oft. Los bons�is y los acuarios son mis aficiones no relacionadas con los aparatitos, estos con pantalla y teclado. Contenidos: |
Resumen:
En este tercer art�culo de la serie sobre Renderman (III) trataremos uno de los aspectos m�s importantes: la posibilidad de modelar y animar una escena utilizando "C" o "C++".
Como hemos podido ver en los dos art�culos anteriores, crear una escena o una animaci�n escribiendo directamente en un fichero de texto es una tarea tediosa. �Te imaginas hacer un fichero ".rib" con el movimiento de una pelota botando?. Para hacernos la vida un poco m�s f�cil tenemos la posibilidad de escribir en "C" o "C++" el modelo de la escena o animaci�n con una serie de funciones que sacan por la salida est�ndar los comandos Renderman que queramos. Esta salida la podemos pasar a trav�s de un tubo (pipe) a otro proceso (como rendrib, rendribv o rgl) o redireccionarla directamente hacia un fichero .rib.
Cuando instalamos Blue Moon Rendering Tools, nos aparecen dos directorios llamados lib e include. En �stos encontramos cuatro ficheros de los cuales dos son los que nos interesan: ri.h y libribout.a que son las declaraciones y la librer�a de funciones respectivamente. Debemos copiar ri.h a /usr/local/include y libribout.a a /usr/local/lib (tambi�n se pueden poner en otro sitio pero este es un lugar adecuado) y ya est�, estamos listos para nuestro primer programa.
En este primer programa veremos lo b�sico de la programaci�n con Renderman. Como en cualquier programa C, debemos incluir el correspondiente .h antes de utilizar una librer�a, en nuestro caso ser� ri.h. Adem�s deberemos enlazar (link) el programa con la librer�a, para ello deberemos hacer lo siguiente:
gcc miprograma.c -o miprograma -lribout -lm
Nosotros utilizaremos un Makefile para ahorrarnos algo de trabajo a la hora de teclear:
LIBS = -lm -lribout PROGNAME = primero all: $(PROGNAME) $(PROGNAME).o: $(PROGNAME).c gcc -c $(PROGNAME).c $(PROGNAME): $(PROGNAME).o gcc -o $(PROGNAME) $(PROGNAME).o $(LIBS) |
El primer programa que haremos consistir� en unos ejes y una pelota en el centro, lo llamaremos primero.c (�que original!) y es el siguiente:
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("ambientlight","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 }; |
En las tres primeras lineas tenemos una serie de #includes entre los que est� ri.h que es el que tiene los prototipos de las funciones. Cada llamada de Renderman tiene su funci�n equivalente en ri.h, as� pues, TransformBegin se corresponde con la funci�n RiTransformBegin(), etc. Haciendo make generamos el fichero ejecutable primero. Si lo ejecutamos tendremos por la salida est�ndar una serie de comandos Renderman, �stos los podemos redireccionar a un fichero (primero > primero.rib) o redireccionarlo a otro proceso (primero | rendrib). Si optamos por la �ltima forma, rendrib generar� el fichero renderizado primero.tif:
Antes de poder utilizar cualquiera de las llamadas en un programa debemos hacer una llamada a RiBegin(RI_NULL) y al acabar un llamada a RiEnd(). El par�metro que le pasamos a RiBegin es t�picamente RI_NULL. Para evitar el env�o de RIB's hacia la salida est�ndar podemos poner en este argumento el nombre de un fichero ("mifichero.rib") e incluso el nombre de un proceso (como rendrib) y de esta forma pas�rselo a un programa sin necesidad de crear un fichero RIB intermedio.
En el listado podemos ver, adem�s de las cosas t�picas de C, algunos tipos y funciones propios del interface de Renderman: tenemos el tipo RtColor que es un vector de tres reales indicando la cantidad de rojo, verde y azul (con valores entre 0.0 y 1.0), vemos tambi�n el tipo RtPoint que indica posiciones en el espacio y RtFloat y RtInt que son el tipo real y entero respectivamente.
En la l�nea 29 tenemos la llamada RiBegin(RI_NULL) que como hemos dicho antes es la primera que debemos usar. A partir de aqu� tenemos m�s o menos lo que escribir�amos en un fichero RIB. Esto lo podemos comprobar ejecutando el programa y redireccion�ndolo a un fichero (./primero > primero.rib), obtendremos lo siguiente:
##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 "ambientlight" 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 |
Viendo esto podemos pensar que generar escenas de esta forma es m�s costoso ya que tenemos que hacer exactamente lo mismo, definir todas las cosas y posicionarlas en la escena, pero la verdadera potencia de la librer�a estriba en las animaciones. Este ejemplo consta s�lo de un frame as� que a continuaci�n haremos que la pelota se mueva.
En este segundo programa usaremos exactamente la misma escena pero haremos que la pelota se desplace desde la posici�n (30,0,10) a la posici�n (0,20,10) que es m�s o menos desde la derecha de la pantalla hasta la izquierda. Estas dos posiciones las definiremos como RtPoint (lineas 18 y 19). El n�mero de frames o im�genes de las que constar� nuestra animaci�n lo definimos con la variable nf. A partir de este n�mero y de las posiciones inicial y final obtenemos el paso por frame en los tres ejes (slopex, slopey y slopez). Con todo esto, lo �nico que nos falta es variar la posici�n de la pelota en funci�n del n�mero de frame en que nos encontremos. Esto lo hacemos en las lineas 75 a 78, donde un TransformBegin/TransformEnd se encarga de definir la posici�n de la bola. La posici�n la calculamos de una forma muy simple como podemos ver en la l�nea 76.
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("ambientlight","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 }; |
Podemos pasar ahora a probar nuestro programa, para ello procederemos de la misma forma que en el programa anterior: lo compilamos y lo ejecutamos redireccion�ndolo, por ejemplo, a rendribv. Podemos de esta forma ver como queda nuestra animaci�n de forma r�pida y a una velocidad aceptable. Para ver el fichero rib que se genera no tienes m�s que redireccionar la salida hacia un fichero. Podr�s comprobar que el fichero es bastante grande (segundo.rib ocupa 70kb) ya que tenemos repetido la definici�n de la escena 100 veces (una por cada frame).
En las siguientes figuras podemos ver una serie de im�genes de la animaci�n:
Por supuesto podemos animar cualquier cosa que se nos ocurra: la posici�n de los objetos, su tama�o, la intensidad de la luz, la c�mara, hacer que aparezcan o desaparezcan cosas, etc.
En este �ltimo ejemplo veremos como hacer que la pelota bote. Para ello definimos una funci�n rebote() a la cual le pasamos tres par�metros: el n�mero de frame actual, el n�mero total de frames por rebote y la m�xima altura a la que queremos rebotar. A continuaci�n podemos ver la implementaci�n:
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); } |
Con unos sencillos c�lculos transformamos la t�pica funci�n de par�bola (y=x^2) para que se ajuste al n�mero de frames y a la altura que deseamos. En las siguientes figuras tenemos un parte de las im�genes que se generan para cada rebote en el programa tercero.c:
Para ver las animaciones hemos creado unos GIF's animados de las escenas aunque van un poco lentos (por lo menos con el Netscape) aunque con xanim funcionan a una velocidad aceptable:
Movimiento rectil�neo: segundo_anim.gif
Movimiento parab�lico: tercero_anim.gif
Con esto acabamos con los aspectos b�sicos del interface de Renderman y su programaci�n. La parte m�s avanzada y tambi�n la m�s espectacular es la programaci�n de shaders. Con esto conseguimos un control total sobre el proceso de rendering y sobre el aspecto final de la escena ya que podemos controlar texturas, iluminaci�n, etc.
Contactar con el equipo de LinuFocus
© Carlos Calzada Grau LinuxFocus 1999 |
1999-07-02, generated by lfparser version 0.6