主頁 地圖 索引 搜尋 新聞 前期雜誌 相關聯結 關於 LinuxFocus
[Top Bar]
[Bottom Bar]
[Photo of the Author]
Carlos Calzada Grau

作者簡介:

計算機科學研究生,計算機圖形迷。十分熱心於 Linux 和它的發展哲學及一切 與微軟無關的任何事物。與計算機無關的其他愛好是盆景和養魚。

目錄:

  1. 引言:
  2. 簡單場景:
  3. 矩形直線運動:
  4. 拋物線運動:
  5. 參考文獻:

著色人(Renderman)III

[Ilustration]

摘要:Abstract

這是 Renderman 系列的第三篇文章 ( Renderman I Renderman II ), 現在我們將討論最主要的問題:使用“C”或“C++”語言 建立模型和產生一個場景的可能性。



 

引言

從前兩篇文章中可很清晰地看出,直接通過鍵入一個文字檔案 描述一個場景是可能的,但這畢竟太單調乏味。 比如,考慮寫一個“.rib”文件來描述一個跳躍的球的運動! 更容易一些,我們可以書寫建立場景模型和動畫的“C語言”或“C++語言” 應用程式,並利用傳送“.rib”到標準輸出的用戶自定義函式。 UNIX 的管道允許我們直接傳送生成的 Renderman 命令到其他程序 (如rendribrendribvrgl), 或者重定向到“.rib”文件。

我們以前安裝的藍月亮著色工具(Blue Moon Rendering Tools) 建立兩個名為 libinclude 的新目錄, 包含四個文件,我們只涉及到其中的兩個:ri.h 表頭檔 (Include file) 和 libribout.a 函式庫 (Library) 。表頭檔應該拷貝到 /use/local/include 目錄, libribout.a 拷貝到 /use/local/lib 目錄(有經驗的讀者可以 選擇安裝到其他位置)。安裝完後,我們開始準備第一個例子程式。

 

簡單場景:

我們的第一個例子演示十分基本的 Renderman 編程。像任何 C 代碼一樣, 我們使用函式庫之前必須包含相應的表頭檔,這裡就是ri.h。 此外,我們必須將程式和函式庫這樣連接:

gcc myprogram.c -o myprogram -lribout -lm
      

下面是一個將保存在控制台上鍵入的時間的例子 Makefile
LIBS = -lm -lribout
PROGNAME = primero
	 
all: $(PROGNAME)
	
$(PROGNAME).o: $(PROGNAME).c
	gcc -c $(PROGNAME).c
	
$(PROGNAME): $(PROGNAME).o 
	gcc -o $(PROGNAME) $(PROGNAME).o $(LIBS)

 

我們的第一個例子將在中央建立一些坐標軸,一個球體,文件名為 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("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 };

 

前三行是基本的表頭檔,其中ri.h是Renderman函式庫的頭定義。 每個 Renderman 調用在 ri.h 中有其相對應的 C 語言調用,例如 TransformBegin符合於函式RiTransformBegin()等等。 用 make 建立可執行的primero。我們的例子 執行時可以通過重定向(primero > primero.rib)或 直接傳送輸出到其他程序(primero | rendrib)來建立輸入文件。 在後一種情況中,rendrib負責生成一個著色後的文件primero.tif

從函式庫裡調用的函式必須處於RiBegin(RI_NULL)RiEnd()調用之間。 傳遞給RiBegin的參數通常是RI_NULL。 為了防止RIB輸出到標準輸出,我們可以傳遞輸出文件名 * ("myfile.rib"), 甚至一個程序名(如rendrib),它的執行將傳送Renderman命令給著色者 而無須建立一個中間RIB文件。

我們第一個例子的源代碼包含典型的C指令,加上Renderman接口固有的類型和函式﹔ 類型RtColor是含三個實數部分的向量,分別代表紅色、綠色和藍色 (范圍由0.0 到1.0 ),類型RtPoint保存空間位置,RtFloatRtInt分別是實數和整數類型。

第29行包含一個到RiBegin(RI_NULL)的調用,正像我們先前描述的 是Renderman接口的初始化調用。從這裡開始在通常的命令函式後, 應該輸入一個典型的RIB文件。試著運行代碼,并將輸出重定向到一個文件 (./primero > primero.rib),輸出結果應該這樣:
##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

我們的第一個例子并不十分有用。為了生成另一個場景,我們必須寫一個 新的程式,完成類似的操作。函式庫的性能真正體現在動畫的生成上。 第一個例子中只有一幅畫面生成,接下來,我們要使球體移動。

 

矩形線性運動:

第二個例子中,我們的場景仍是由三個坐標軸和一個球體組成, 但球體將由坐標(20,0,10)移動到(0,20,10),即 從計算機屏幕的右端移動到左端。位置使用RtPoint結構定義(18、19行)。 動畫中畫面或圖像的數量定義在變量nf中。 使用這個數、初始和終止位置,可以計算出每個畫面三個方向上的步長 (slopex,slopeyslopez)。這些就是用畫面 數的函式來修改球體位置所需的所有信息。在第75到78行之間, TransformBegin/TransformEnd負責定義球體的位置。 每一步新的位置將在第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 };

接下來,讓我們像以前一樣試驗第二個例子:編譯并執行,作為例子 重定向輸出到rendribv。這是以我們可接受的速率快速 預覽我們的動畫的一個簡易方法。為檢查rib輸出文件,傳送標準輸出到一個新文件。 如這可以發現生成的文件是十分龐大的 (segundo.rib占70kb) ,這是因為同一個場景定義了100次(每個畫面一次)

下面的圖例給出了一些動畫的中間畫面:

當然,可以使我們希望的任何事物動起來:物體的位置, 大小,光強度,攝像機,使物體忽隱忽顯……

 

拋物線運動:

在最後一個例子中,讓我們來看看如何使球體從地上反彈起來。 我們首先定義函式rebote()(反彈的意思),有三個參數: 當前畫面的數量,每次反彈的畫面數,球體能達到的最大高度。 以下是其實現:

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);
}

利用一些簡單的計算,可以映射典型的拋物曲線(y=x^2)畫面數和希望的最大高度。下面的圖例給出一些由程式生成的 每次反彈的中間圖像。 tercero.c:

我提供一些動畫GIF文件來形象顯示動畫過程,雖然他們運行緩慢 (至少在Netscape下是這樣),但通過xanim你應該能夠 正常地看到。

矩形線性運動:segundo_anim.gif

拋物線運動: tercero_anim.gif

這樣我們關於Renderman的C語言接口基礎的論述及編程告一段落。 最高級、最壯觀的編程主題是陰影(shaders)。 它提供了場景最終著色的根本控制,因為允許我們控制紋理,照明等等。

 

參考文獻



翻譯:Miguel A Sepulveda


網頁由LinuxFocus編輯組維護
© Carlos Calzada Grau
LinuxFocus 1999