10.1、資料結構
當您想要使用一系列的數字來完美的表達3d環境時,隨著環境複雜度的上公升,這個工作的難度也會隨之上公升。出於這個原因,我們必須將資料歸類,使其具有更多的可操作性風格。在程式清單頭部出現了sector(區段)的定義。每個3d世界基本上可以看作是sector(區段)的集合。乙個sector(區段)可以是乙個房間、乙個立方體、或者任意乙個閉合的區間。
typedef struct tagsector // 建立sector區段結構
sector; // 命名為sector
乙個sector(區段)包含了一系列的多邊形,所以下乙個目標就是********(我們將只用三角形,這樣寫**更容易些)。
typedef struct tag******** // 建立********三角形結構
********; // 命名為 ********
三角形本質上是由一些(兩個以上)頂點組成的多邊形,頂點同時也是我們的最基本的分類單位。頂點包含了opengl真正感興趣的資料。我們用3d空間中的座標值(x, y, z)以及它們的紋理座標(u, v)來定義三角形的每個頂點。
typedef struct tagvertex // 建立vertex頂點結構
vertex; // 命名為vertex
// 先前的定義:char* worldfile = "data//world.txt";
void setupworld() // 設定我們的世界
下乙個挑戰是將每個單獨的文字行讀入變數。這有很多辦法可以做到。乙個問題是檔案中並不是所有的行都包含有意義的資訊。空行和注釋不應該被讀入。我們建立了乙個叫做readstr()的函式。這個函式會從資料檔案中讀入乙個有意義的行至乙個已經初始化過的字串。下面就是**:
void readstr(file *f,char *string) // 讀入乙個字串
while ((string[0] == '/') || (string[0] == '/n'));// 考察是否有必要進行處理
return; // 返回
}下一步我們讀入區段資料。這一課將只處理乙個區段,不過實現乙個多區段引擎也很容易。讓我們將注意力轉回setupworld()。程式必須知道區段內包含了多少個三角形。我們在資料檔案中以下面這種形式定義三角形數量:
numpollies n
接下來是讀取三角形數量的**:
int num********s; // 區段中的三角形數量
char oneline[255]; // 儲存資料的字串
...readstr(filein,oneline); // 讀入一行資料
sscanf(oneline, "numpollies %d/n", &num********s); // 讀入三角形數量
餘下的世界載入過程採用了相似的方法。接著,我們對區段進行初始化,並讀入部分資料:
// 先前的定義:sector sector1;
char oneline[255]; // 儲存資料的字串
int num********s; // 區段的三角形數量
float x, y, z, u, v; // 3d和紋理座標
...sector1.******** = new ********[num********s]; // 為num********s個三角形分配記憶體並設定指標
sector1.num********s = num********s; // 定義區段1中的三角形數量
// 遍歷區段中的每個三角形
for (int triloop = 0; triloop < num********s; triloop++) // 遍歷所有的三角形
}資料檔案中每個三角形都以如下形式宣告:
x1 y1 z1 u1 v1
x2 y2 z2 u2 v2
x3 y3 z3 u3 v3
10.3、顯示世界
現在區段已經載入記憶體,我們下一步要在螢幕上顯示它。到目前為止,我們所作過的都是些簡單的旋轉和平移。但我們的鏡頭始終位於原點(0, 0, 0)處。任何乙個不錯的3d引擎都會允許使用者在這個世界中游走和遍歷,我們的這個也一樣。實現這個功能的一種途徑是直接移動鏡頭並繪製以鏡頭為中心的3d環境。這樣做會很慢並且不易用**實現。我們的解決方法如下:
1)根據使用者的指令旋轉並變換鏡頭位置。
2)圍繞原點,以與鏡頭相反的旋轉方向來旋轉世界。(讓人產生鏡頭旋轉的錯覺)
3)以與鏡頭平移方式相反的方式來平移世界(讓人產生鏡頭移動的錯覺)。
這樣實現起來就很簡單。下面從第一步開始吧(平移並旋轉鏡頭)。
if (keys[vk_right]) // 右方向鍵按下了麼?
if (keys[vk_left]) // 左方向鍵按下了麼?
if (keys[vk_up]) // 向上方向鍵按下了麼?
else // 否則
walkbias = (float)sin(walkbiasangle * piover180)/20.0f; // 使遊戲者產生跳躍感
}if (keys[vk_down]) // 向下方向鍵按下了麼?
else // 否則
walkbias = (float)sin(walkbiasangle * piover180)/20.0f; // 使遊戲者產生跳躍感
}這個實現很簡單。當左右方向鍵按下後,旋轉變數yrot相應增加或減少。當前後方向鍵按下後,我們使用sine和cosine函式重新生成鏡頭位置(您需要些許三角函式學的知識)。piover180是乙個很簡單的折算因子用來折算度和弧度。
接著您可能會問:walkbias是什麼意思?這是nehe的發明的單詞。基本上就是當人行走時頭部產生上下擺動的幅度。我們使用簡單的sine正弦波來調節鏡頭的y軸位置。如果不新增這個而只是前後移動的話,程式看起來就沒這麼棒了。
現在,我們已經有了下面這些變數。可以開始進行步驟2和3了。由於我們的程式還不太複雜,我們無需新建乙個函式,而是直接在顯示迴圈中完成這些步驟。
int drawglscene(glvoid) // 繪製opengl場景
return true; // 返回
}搞定。我們已經完成了自己的第一幀畫面。這絕對算不上什麼quake,但是...,我們絕對也不是carmack或者abrash。執行程式時,您可以按下f、b、pgup與pgdown鍵來看看效果。pgup/pgdown簡單的上下傾斜鏡頭。如果nehe決定保留的話,程式中使用的紋理取自於我的學校id證件上的**,並且做了浮雕效果...。
現在您也許在考慮下一步該做什麼。但還是不要考慮使用這些**來實現完整的3d引擎,寫這個程式的目的也並非如此。您也許希望您的遊戲中不止存在乙個sector,尤其是實現類似入口這樣的部分,您還可能需要使用多邊形(超過3個頂點)。程式現在的**實現允許載入多個sector並剔除了背面(背向鏡頭不用繪製的多邊形)。將來我會寫個這樣的教程,但這需要更多的數學知識基礎。nehe(05/01/2000):我已經為教程中引用的每一行**加上了注釋。希望這對您有意義。在此之前只有少數幾行**作了注釋...。
如果對教程和**有任何問題(這是我的第乙個教程,我的解釋也可能含混不清),請別猶豫寫信給我。
OpenGL教程之漫遊3D世界
jeff molofee nehe 的opengl 教程 漫遊3d 世界 原 文 lesson 10 loading and moving through a 3d world 譯 者 cker 這一課是由lionel brits telgeuse 好了,現在歡迎來到名不見經傳的第十課。到現在為止,...
OpenGL教程之向3D進軍
在上節課的內容上作些擴充套件,我們現在開始生成真正的3d物件,而不是象前兩節課中那樣3d世界中的2d物件。我們給三角形增加乙個左側面,乙個右側面,乙個後側面來生成乙個金字塔 四稜錐 給正方形增加左 右 上 下及背面生成乙個立方體。我們混合金字塔上的顏色,建立乙個平滑著色的物件。給立方體的每一面則來個...
OpenGL教程之向3D進軍
jeff molofee nehe 的opengl 教程 向3d 進軍 原 文 lesson 5 solid objects 譯 者 cker 在上節課的內容上作些擴充套件,我們現在開始生成真正的3d物件,而不是象前兩節課中那樣3d世界中的2d物件。我們給三角形增加乙個左側面,乙個右側面,乙個後側面...