NeHe的opengl教程delphi版(9)----星星 (轉)

gugu99發表於2007-08-17
NeHe的opengl教程delphi版(9)----星星 (轉)[@more@]

{
歡迎進入第九課。到現在為止,您應該很好的理解OpenGL了。
『CKER:如果沒有的話,一定是我翻譯的罪過......』。
( myling補充道:我的罪過更大,呵呵)
您已經學會了設定一個OpenGL視窗的每個細節。
學會在旋轉的物體上貼圖並打上光線以及混色(透明)處理。
這一課應該算是一課中級教程。
您將學到如下的知識:在3D場景中移動點陣圖,並去除點陣圖上的黑色象素(使用混色)。
接著為黑白紋理上色,最後您將學會建立豐富的色彩,
並把上過不同色彩的紋理相互混合,得到簡單的動畫效果。
我們在第一課的程式碼基礎上進行修改。先在原始碼的開始處增加幾個變數。
出於清晰起見,我重寫了整段程式碼。
}

Var
  h_RC  : HGLRC;  // Rendering Context(著色描述表)。
  h_DC  : HDC;  // Device Context(裝置描述表)
  h_Wnd  : HWND;  // 視窗控制程式碼
  h_Instance  : HINST;  // 程式Instance(例項)。
  keys  : Array[0..255] Of Boolean; // 用於鍵盤例程的陣列

  {下列這幾行新加的。
  twinkle和 tp是布林變數, 表示它們只能設為 TRUE 或 FALSE。
  twinkle用來跟蹤 閃爍 效果是否啟用。
  tp用來檢查 'T'鍵有沒有被按下或鬆開.
  (按下時 tp=TRUE, 鬆開時 tp=FALSE).}
  twinkle  : Boolean;  // 閃爍的星星  (新增)
  tp  : Boolean;  // 'T' 按下了麼?  (新增)
  {現在我們來建立一個結構。
  結構這詞聽起來有點可怕,但實際上並非如此。(就是的紀錄型別)
  一個結構使用一組簡單型別的資料 (以及變數等)來表達較大的具有相似性的資料組合。
  我們知道我們在保持對星星的跟蹤。
  您可以看到下面的就是 stars;
  每個星星有三個整型的色彩值。一個紅色 (r), 一個綠色 (g), 以及一個藍色 (b).
  此外,每個星星離螢幕中心的距離不同,
  而且可以是以螢幕中心為原點的任意360度中的一個角度。
  dist的浮點數來保持對距離 的跟蹤.
  angle的浮點數保持對星星角度值的跟蹤。
  因此我們使用了一組資料來描述螢幕上星星的色彩, 距離, 和角度。
  不幸的是我們不止對一個星星進行跟蹤。
  但是無需建立 50 個紅色值、 50 個綠色值、 50 個藍色值、 50 個距離值
  以及 50 個角度值,而只需建立一個陣列star。}
Type
  stars = Record  // 為星星建立一個結構,結構命名為stars
  r, g, b: integer;  // 星星的顏色
  dist: GLfloat;  // 星星距離中心的距離
  angle: GLfloat;  // 當前星星所處的角度
  End;
Var
  star  : Array[0..49] Of stars;  // 使用 'stars' 結構生成一個包含 50個元素的 'star'陣列

  {接下來我們設定幾個跟蹤變數:
  星星離觀察者的距離變數(zoom),
  我們所見到的星星所處的角度(tilt),
  以及使閃爍的星星繞Z軸自轉的變數spin。
  l變數用來繪製50顆星星。
  texture[1]用來存放一個黑白紋理。
  如果您需要更多的紋理的話,
  您應該增加texture陣列的大小至您決定採用的紋理個數。
  }

  zoom  : GLfloat = -15.0;  // 星星離觀察者的距離
  tilt  : GLfloat = 90.0;  // 星星的傾角
  spin  : GLfloat;  // 閃爍星星的自轉
  loop  : GLuint;  // 全域性l Loop 變數
  texture  : Array[0..1] Of GLuint; // 存放一個紋理

Procedure glGenTextures(n: GLsizei; Var textures: GLuint); stdcall; external
  opengl32;

Procedure glBindTexture(target: GLenum; texture: GLuint); stdcall; external
  opengl32;

{
緊接著上面的程式碼就是我們用來載入紋理的程式碼。
我不打算再詳細的解釋這段程式碼。
這跟我們在第六、七、八課中所用的程式碼是一模一樣的。
這一次載入的點陣圖叫做star.bmp。
這裡我們使用glGenTextures(1, &texture[0]),
來生成一個紋理。紋理採用線性濾波方式。
}

Function LoadTexture: boolean;  // 載入點陣圖並轉換成紋理
Var
  Status  : boolean;  // Status 指示器
  TextureImage  : Array[0..1] Of PTAUX_RGBImageRec; // 建立紋理的空間
Begin
  Status := false;
  ZeroMemory(@TextureImage, sizeof(TextureImage)); // 將指標設為 NULL
  TextureImage[0] := LoaMP('Star.bmp');
  If TextureImage[0] <> Nil Then
  Begin
  Status := TRUE;  // 將 Status 設為 TRUE
  glGenTextures(1, texture[0]);  // 建立紋理
  // 建立 Nearest 濾波貼圖
  glBindTexture(GL_TEXTURE_2D, texture[0]);
  // 生成紋理
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);  // ( 新增 )
  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);  // ( 新增 )

  glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0].sizeX,
  TextureImage[0].sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE,
  TextureImage[0].data);
  End;
  If assigned(TextureImage[0]) Then  // 紋理是否存在
  If assigned(TextureImage[0].data) Then // 紋理影像是否存在
  TextureImage[0].data := Nil;  // 釋放紋理影像佔用的
  TextureImage[0] := Nil;  // 釋放影像結構
  result := Status;  // 返回 Status
End;

{
在glInit()中設定OpenGL的渲染方式。這裡不打算使用深度測試,
如果您使用第一課的程式碼的話,
請確認是否已經去掉了 glDepthFunc(GL_LEQUAL)和 glEnable(GL_DEPTH_TEST)。
否則,您所見到的效果將會一團糟。
這裡我們使用了紋理對映,
因此請您確認您已經加上了這些第一課中所沒有的程式碼。
您會注意到我們透過混色來啟用了紋理對映。
}

Procedure glInit();
Begin
  If (Not LoadTexture) Then  // 紋理載入子例程( 新增 )
  exit;  // 如果未能載入,退出( 新增 )

  glEnable(GL_TEXTURE_2D);  // 啟用紋理對映
  glShadeModel(GL_SMOOTH);  // 啟用陰影平滑
  glClearColor(0.0, 0.0, 0.0, 0.5);  // 黑色背景
  glClearDepth(1.0);  // 設定深度快取
  glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // 真正精細的透視修正
  glBlendFunc(GL_SRC_ALPHA, GL_ONE);  // 設定混色取得半透明效果
  glEnable(GL_BLEND);  // 啟用混色

  {以下是新增的程式碼。
  設定了每顆星星的起始角度、距離、和顏色。
  您會注意到修改結構的屬性有多容易。
  全部50顆星星都會被迴圈設定。
  要改變star[1]的角度我們所要做的只是star[1].angle=某個數值;
  就這麼簡單!}
  For loop := 0 To 49 Do  // 建立迴圈設定全部星星
  Begin
  star[loop].angle := 0.0;  // 所有星星都從零角度開始
  {第loop顆星星離中心的距離是將loop的值除以星星的總顆數,然後乘上5.0。
  基本上這樣使得後一顆星星比前一顆星星離中心更遠一點。
  這樣當loop為50時(最後一顆星星),loop 除以 num正好是1.0。
  之所以要乘以5.0是因為1.0*5.0 就是 5.0。
  『CKER:廢話,廢話!這老外怎麼跟孔乙己似的!:)』
  5.0已經很接近螢幕邊緣。我不想星星飛出螢幕,5.0是最好的選擇了。
  當然如果如果您將場景設定的更深入螢幕裡面的話,
  也許可以使用大於5.0的數值,但星星看起來就更小一些(都是透視的緣故)。
  您還會注意到每顆星星的顏色都是從0~255之間的一個隨機數。
  也許您會奇怪為何這裡的顏色得取值範圍不是OpenGL通常的0.0~1.0之間。
  這裡我們使用的顏色設定函式是glColor4ub,而不是以前的glColor4f。
  ub意味著引數是Unsigned Byte型的。
  一個byte的取值範圍是0~255。
  這裡使用byte值取隨機整數似乎要比取一個浮點的隨機數更容易一些。
  }
  star[loop].dist := (Trunc(loop) / 50) * 5.0; // 計算星星離中心的距離
  star[loop].r := ran(256);  // 為star[loop]設定隨機紅色分量
  star[loop].g := random(256);  // 為star[loop]設定隨機紅色分量
  star[loop].b := random(256);  // 為star[loop]設定隨機紅色分量
  End;

End;

{
現在我們轉入glDraw()繪圖程式碼。
如果您使用第一課的程式碼,刪除舊的DrawGLScene程式碼,只需將下面的程式碼複製過去就行了。
實際上,第一課的程式碼只有兩行,所以沒太多東西要刪掉的。
}

Procedure glDraw();
Begin
  glClear(GL_COLOR_BUFFER_BIT Or GL_DEPTH_BUFFER_BIT); // 清除螢幕和深度快取

  glBindTexture(GL_TEXTURE_2D, texture[0]); // 選擇紋理
  For loop := 0 To 49 Do  // 迴圈設定所有的星星
  Begin
  glLoadntity();  // 繪製每顆星星之前,重置模型觀察矩陣
  glTranslatef(0.0, 0.0, zoom);  // 深入螢幕裡面 (使用 'zoom'的值)
  glRotatef(tilt, 1.0, 0.0, 0.0); // 傾斜視角(使用 'tilt'的值)
  {
  現在我們來移動星星。
  星星開始時位於螢幕的中心。
  我們要做的第一件事是把場景沿Y軸旋轉。
  如果我們旋轉90度的話,X軸不再是自左至右的了,他將由裡向外穿出螢幕。
  為了讓大家更清楚些,舉個例子。假想您站在房子中間。
  再設想您左側的牆上寫著-x,前面的牆上寫著-z,
  右面牆上就是+x咯,您身後的牆上則是+z。
  加入整個房子向右轉90度,但您沒有動,那麼前面的牆上將是-x而不再是-z了。
  所有其他的牆也都跟著移動。-z出現在右側,+z出現在左側,+x出現在您背後。
  神經錯亂了吧?透過旋轉場景,我們改變了x和z平面的方向。
  第二行程式碼沿x軸移動一個正值。
  通常x軸上的正值代表移向了螢幕的右側(也就是通常的x軸的正向),
  但這裡由於我們繞y軸旋轉了座標系,x軸的正向可以是任意方向。
  如果我們轉180度的話,螢幕的左右側就映象反向了。
  因此,當我們沿 x軸正向移動時,可能向左,向右,向前或向後。
  }
  glRotatef(star[loop].angle, 0.0, 1.0, 0.0); //旋轉至當前所畫星星的角度
  glTranslatef(star[loop].dist, 0.0, 0.0); // 沿X軸正向移動
  {
  接著的程式碼帶點小技巧。
  星星實際上是一個平面的紋理。
  現在您在螢幕中心畫了個平面的四邊形然後貼上紋理,這看起來很不錯。
  一切都如您所想的那樣。但是當您當您沿著y軸轉上個90度的話,
  紋理在螢幕上就只剩右側和左側的兩條邊朝著您。 看起來就是一條細線。
  這不是我們所想要的。我們希望星星永遠正面朝著我們,而不管螢幕如何旋轉或傾斜。
  我們透過在繪製星星之前,抵消對星星所作的任何旋轉來實現這個願望。
  您可以採用逆序來抵消旋轉。當我們傾斜螢幕時,我們實際上以當前角度旋轉了星星。
  透過逆序,我們又以當前角度"反旋轉"星星。也就是以當前角度的負值來旋轉星星。
  就是說,
  如果我們將星星旋轉了10度的話,又將其旋轉-10度來使星星在那個軸上重新面對螢幕。
  下面的第一行抵消了沿y軸的旋轉。然後,我們還需要抵消掉沿x軸的螢幕傾斜。
  要做到這一點,我們只需要將螢幕再旋轉-tilt傾角。
  在抵消掉x和y軸的旋轉後,星星又完全面對著我們了。
  }
  glRotatef(-star[loop].angle, 0.0, 1.0, 0.0); // 取消當前星星的角度
  glRotatef(-tilt, 1.0, 0.0, 0.0); // 取消螢幕傾斜
  {如果 twinkle 為  TRUE,我們在螢幕上先畫一次不旋轉的星星:
  將星星總數(num) 減去當前的星星數(loop)再減去1,
  來提取每顆星星的不同顏色(這麼做是因為迴圈範圍從0到num-1)。
  舉例來說,結果為10的時候,我們就使用10號星星的顏色。
  這樣相鄰星星的顏色總是不同的。這不是個好法子,但很有效。
  最後一個值是alpha通道分量。這個值越小,這顆星星就越暗。
  由於啟用了twinkle,每顆星星最後會被繪製兩遍。
  程式執行起來會慢一些,這要看您的機器如何了。
  但兩遍繪製的星星顏色相互融合,會產生很棒的效果。
  同時由於第一遍的星星沒有旋轉,啟用twinkle後的星星看起來有一種動畫效果。
  (如果您這裡看不懂得話,就自己去看程式的執行效果吧。)
  值得注意的是給紋理上色是件很容易的事。
  儘管紋理本身是黑白的,紋理將變成我們在繪製它之前選定的任意顏色。
  此外,同樣值得注意的是我們在這裡使用的顏色值是byte型的,
  而不是通常的浮點數。甚至alpha通道分量也是如此。}

  If (twinkle) Then  // 啟用閃爍效果
  Begin
  // 使用byte型數值指定一個顏色
  glColor4ub(star[(50 - loop) - 1].r, star[(50 - loop) - 1].g,
  star[(50 - loop) - 1].b, 255);
  glBegin(GL_QUADS);  // 開始繪製紋理對映過的四邊形
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-1.0, -1.0, 0.0);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(1.0, -1.0, 0.0);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(1.0, 1.0, 0.0);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(-1.0, 1.0, 0.0);
  glEnd();  // 四邊形繪製結束
  End;

  {
  現在繪製第二遍的星星。
  唯一和前面的程式碼不同的是這一遍的星星肯定會被繪製,並且這次的星星繞著z軸旋轉。
  }
  glRotatef(spin, 0.0, 0.0, 1.0); // 繞z軸旋轉星星
  //  使用byte型數值指定一個顏色
  glColor4ub(star[loop].r, star[loop].g, star[loop].b, 255);
  glBegin(GL_QUADS);  // 開始繪製紋理對映過的四邊形
  glTexCoord2f(0.0, 0.0);
  glVertex3f(-1.0, -1.0, 0.0);
  glTexCoord2f(1.0, 0.0);
  glVertex3f(1.0, -1.0, 0.0);
  glTexCoord2f(1.0, 1.0);
  glVertex3f(1.0, 1.0, 0.0);
  glTexCoord2f(0.0, 1.0);
  glVertex3f(-1.0, 1.0, 0.0);
  glEnd();  // 四邊形繪製結束

  {以下的程式碼代表星星的運動。
  我們增加spin的值來旋轉所有的星星(公轉)。
  然後,將每顆星星的自轉角度增加loop/num。
  這使離中心更遠的星星轉的更快。最後減少每顆星星離螢幕中心的距離。
  這樣看起來,星星們好像被不斷地吸入螢幕的中心。}
  spin := spin + 0.01;  // 星星的公轉
  star[loop].angle := star[loop].angle + Trunc(loop) / 50;  // 改變星星的自轉角度
  star[loop].dist := star[loop].dist - 0.01; // 改變星星離中心的距離

  {接著幾行檢查星星是否已經碰到了螢幕中心。
  當星星碰到螢幕中心時,我們為它賦一個新顏色,然後往外移5個單位,
  這顆星星將踏上它迴歸螢幕中心的旅程。}

  If (star[loop].dist < 0.0) Then // 星星到達中心了麼
  Begin
  star[loop].dist := star[loop].dist + 5.0; // 往外移5個單位
  star[loop].r := random(256); // 賦一個新紅色分量
  star[loop].g := random(256); // 賦一個新綠色分量
  star[loop].b := random(256); // 賦一個新藍色分量
  End;
  End;
End;

{
 現在我們新增監視鍵盤的程式碼。
 下移到WinMain()。找到SBuffers(hDC)一行。
 我們就在這一行後面增加鍵盤監視程式碼。
 程式碼將檢查T鍵是否已按下。
 如果T鍵按下過,並且又放開了,if塊內的程式碼將被。
 如果twinkle為FALSE,他將變為TRUE。
 反之亦然。只要T鍵按下, tp就變為TRUE。
 這樣處理可以防止如果您一直按著T鍵的話,塊內的程式碼被反覆執行。
 }
 If (keys[ord('T')] And Not tp) Then  // 是否T 鍵已按下並且 tp值為 FALSE
  Begin
  tp := TRUE;  // 若是,將tp設為TRUE
  twinkle := Not twinkle; // 翻轉 twinkle的值
  End;
 {
 下面的程式碼檢查是否鬆開了T鍵。
 若是,使 tp=FALSE。
 除非tp的值為FALSE,
 否則按著T鍵時什麼也不會發生。所以這行程式碼很重要。
 }
 If (Not keys[Ord('T')]) Then //  T 鍵已鬆開了麼?
  Begin
  tp := FALSE;  // 若是 ,tp為 FALSE
  End;
 {餘下的程式碼檢查上、下方向鍵,向上翻頁鍵或向下翻頁鍵是否按下。}
 If (keys[VK_UP]) Then  // 上方向鍵按下了麼?
  tilt := tilt - 0.5;  // 螢幕向上傾斜
 If (keys[VK_DOWN]) Then  // 下方向鍵按下了麼?
  tilt := tilt + 0.5;  // 螢幕向下傾斜
 If (keys[VK_PRIOR]) Then // 向上翻頁鍵按下了麼
  zoom := zoom - 0.2;  // 縮小
 If (keys[VK_NEXT]) Then  // 向下翻頁鍵按下了麼?
  zoom := zoom + 0.2;  // 放大
 {
 這一課我盡我所能來解釋如何載入一個灰階點陣圖紋理,
 (使用混色)去掉它的背景色後,再給它上色,最後讓它在3D場景中移動。
 我已經向您展示瞭如何建立漂亮的顏色與動畫效果。
 實現原理是在原始點陣圖上再重疊一份點陣圖複製。
 到現在為止,只要您很好的理解了我所教您的一切,
 您應該已經能夠毫無問題的製作您自己的3D Demo了。
 所有的基礎知識都已包括在內!}

//========myling :

//1-9課已經翻譯完了,就象NEHE說的,基本的知識已經基本說完了

//我看了下後面的教程,好像是出自其他人之手,如果有好的例子,我會選擇性的繼

//續貼的,好累,睡一會:)  ,下次見


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10748419/viewspace-962924/,如需轉載,請註明出處,否則將追究法律責任。

相關文章