NeHe的opengl教程delphi版(9)----星星 (轉)
{
歡迎進入第九課。到現在為止,您應該很好的理解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/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- NeHe的opengl教程delphi版(7)----濾波 (轉)
- 漂亮的星星動畫:Jeff Molofee(NeHe) 的 OPENGL 教程-第九課 (轉)動畫
- OpenGL系列教程之一:OpenGL(轉)
- 用Delphi進行OpenGL程式設計學習心得 (轉)程式設計
- OpenGL著色器教程
- 華為CMPP原始碼delphi6版 (轉)原始碼
- Delphi資料庫程式設計教程(九) (轉)資料庫程式設計
- canvas隨機生成星星(轉)Canvas隨機
- DELPHI的萬用字元比較(第五版) (轉)字元
- 遊戲製作詳解自----OpenGL入門教程(九)(轉)遊戲
- 遊戲製作詳解自----OpenGL入門教程(五)(轉)遊戲
- 遊戲製作詳解自----OpenGL入門教程(四)(轉)遊戲
- 《Delphi高手突破》第一章——預覽版 (轉)
- OpenGL版本與OpenGL擴充套件機制(轉)套件
- Delphi學習寶典1.2版的破解(vb版)
- Delphi與Excel (轉)Excel
- (譯)win32asm教程-9 (轉)Win32ASM
- Java Servlet和JSP教程(9)(轉)JavaServletJS
- Emacs 教程中文版(轉)Mac
- Delphi 的 Utf-8 轉換
- Delphi中的類和物件 (轉)物件
- 數星星
- 【GLSL教程】(二)在OpenGL中使用GLSL
- Direct3D9初級教程 (轉)3D
- OpenGL的渲染成紋理技術(轉)
- Linux下的OpenGL程式設計(轉)Linux程式設計
- Delphi 類的靜態屬性 (轉)
- DELPHI的萬用字元比較 (轉)字元
- delphi的物件導向之路1 (轉)物件
- Orace 的標準版&企業版9i&10g的區別[轉]
- opengl 教程(5) shader(2) uniform變數ORM變數
- css活用,半星星的效果CSS
- Delphi 中的 XMLDocument 類詳解(9) - 關於 HasChildNodes 與 IsTextElementXML
- OpenGL 1.3 最新擴充套件 (轉)套件
- Delphi物件模型(Part V) (轉)物件模型
- Delphi物件模型(Part IV) (轉)物件模型
- Delphi物件模型(Part VI) (轉)物件模型
- Delphi物件模型(Part II) (轉)物件模型