Var
h_RC : HGLRC; // Rendering Context(著色描述表)。
h_DC : HDC; // Device Context(設備描述表)
h_Wnd : HWND; // 窗口句柄
h_Instance : HINST; // 程序Instance(實例)。
keys : Array[0..255] Of Boolean; // 用于鍵盤例程的數(shù)組
{下列這幾行新加的。
twinkle和 tp是布爾變量, 表示它們只能設為 TRUE 或 FALSE。
twinkle用來跟蹤 閃爍 效果是否啟用。
tp用來檢查 'T'鍵有沒有被按下或松開.
(按下時 tp=TRUE, 松開時 tp=FALSE).}
twinkle : Boolean; // 閃爍的星星 (新增)
tp : Boolean; // 'T' 按下了么? (新增)
{現(xiàn)在我們來創(chuàng)建一個結構。
結構這詞聽起來有點可怕,但實際上并非如此。(就是delphi的紀錄類型)
一個結構使用一組簡單類型的數(shù)據(jù) (以及變量等)來表達較大的具有相似性的數(shù)據(jù)組合。
我們知道我們在保持對星星的跟蹤。
您可以看到下面的就是 stars;
每個星星有三個整型的色彩值。一個紅色 (r), 一個綠色 (g), 以及一個藍色 (b).
此外,每個星星離屏幕中心的距離不同,
而且可以是以屏幕中心為原點的任意360度中的一個角度。
dist的浮點數(shù)來保持對距離 的跟蹤.
angle的浮點數(shù)保持對星星角度值的跟蹤。
因此我們使用了一組數(shù)據(jù)來描述屏幕上星星的色彩, 距離, 和角度。
不幸的是我們不止對一個星星進行跟蹤。
但是無需創(chuàng)建 50 個紅色值、 50 個綠色值、 50 個藍色值、 50 個距離值
以及 50 個角度值,而只需創(chuàng)建一個數(shù)組star。}
Type
stars = Record // 為星星創(chuàng)建一個結構,結構命名為stars
r, g, b: integer; // 星星的顏色
dist: GLfloat; // 星星距離中心的距離
angle: GLfloat; // 當前星星所處的角度
End;
Var
star : Array[0..49] Of stars; // 使用 'stars' 結構生成一個包含 50個元素的 'star'數(shù)組
{接下來我們設置幾個跟蹤變量:
星星離觀察者的距離變量(zoom),
我們所見到的星星所處的角度(tilt),
以及使閃爍的星星繞Z軸自轉的變量spin。
loop變量用來繪制50顆星星。
texture[1]用來存放一個黑白紋理。
如果您需要更多的紋理的話,
您應該增加texture數(shù)組的大小至您決定采用的紋理個數(shù)。
}
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; // 創(chuàng)建紋理的存儲空間
Begin
Status := false;
ZeroMemory(@TextureImage, sizeof(TextureImage)); // 將指針設為 NULL
TextureImage[0] := LoadBMP('Star.bmp');
If TextureImage[0] <> Nil Then
Begin
Status := TRUE; // 將 Status 設為 TRUE
glGenTextures(1, texture[0]); // 創(chuàng)建紋理
// 創(chuàng)建 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;
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); // 設置混色函數(shù)取得半透明效果
glEnable(GL_BLEND); // 啟用混色
{以下是新增的代碼。
設置了每顆星星的起始角度、距離、和顏色。
您會注意到修改結構的屬性有多容易。
全部50顆星星都會被循環(huán)設置。
要改變star[1]的角度我們所要做的只是star[1].angle=某個數(shù)值;
就這么簡單!}
For loop := 0 To 49 Do // 創(chuàng)建循環(huán)設置全部星星
Begin
star[loop].angle := 0.0; // 所有星星都從零角度開始
{第loop顆星星離中心的距離是將loop的值除以星星的總顆數(shù),然后乘上5.0。
基本上這樣使得后一顆星星比前一顆星星離中心更遠一點。
這樣當loop為50時(最后一顆星星),loop 除以 num正好是1.0。
之所以要乘以5.0是因為1.0*5.0 就是 5.0。
『CKER:廢話,廢話!這老外怎么跟孔乙己似的!:)』
5.0已經(jīng)很接近屏幕邊緣。我不想星星飛出屏幕,5.0是最好的選擇了。
當然如果如果您將場景設置的更深入屏幕里面的話,
也許可以使用大于5.0的數(shù)值,但星星看起來就更小一些(都是透視的緣故)。
您還會注意到每顆星星的顏色都是從0~255之間的一個隨機數(shù)。
也許您會奇怪為何這里的顏色得取值范圍不是OpenGL通常的0.0~1.0之間。
這里我們使用的顏色設置函數(shù)是glColor4ub,而不是以前的glColor4f。
ub意味著參數(shù)是Unsigned Byte型的。
一個byte的取值范圍是0~255。
這里使用byte值取隨機整數(shù)似乎要比取一個浮點的隨機數(shù)更容易一些。
}
star[loop].dist := (Trunc(loop) / 50) * 5.0; // 計算星星離中心的距離
star[loop].r := random(256); // 為star[loop]設置隨機紅色分量
star[loop].g := random(256); // 為star[loop]設置隨機紅色分量
star[loop].b := random(256); // 為star[loop]設置隨機紅色分量
End;
End;
{
現(xiàn)在我們轉入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 // 循環(huán)設置所有的星星
Begin
glLoadIdentity(); // 繪制每顆星星之前,重置模型觀察矩陣
glTranslatef(0.0, 0.0, zoom); // 深入屏幕里面 (使用 'zoom'的值)
glRotatef(tilt, 1.0, 0.0, 0.0); // 傾斜視角(使用 'tilt'的值)
{
現(xiàn)在我們來移動星星。
星星開始時位于屏幕的中心。
我們要做的第一件事是把場景沿Y軸旋轉。
如果我們旋轉90度的話,X軸不再是自左至右的了,他將由里向外穿出屏幕。
為了讓大家更清楚些,舉個例子。假想您站在房子中間。
再設想您左側的墻上寫著-x,前面的墻上寫著-z,
右面墻上就是+x咯,您身后的墻上則是+z。
加入整個房子向右轉90度,但您沒有動,那么前面的墻上將是-x而不再是-z了。
所有其他的墻也都跟著移動。-z出現(xiàn)在右側,+z出現(xiàn)在左側,+x出現(xiàn)在您背后。
神經(jīng)錯亂了吧?通過旋轉場景,我們改變了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軸正向移動
{
接著的代碼帶點小技巧。
星星實際上是一個平面的紋理。
現(xiàn)在您在屏幕中心畫了個平面的四邊形然后貼上紋理,這看起來很不錯。
一切都如您所想的那樣。但是當您當您沿著y軸轉上個90度的話,
紋理在屏幕上就只剩右側和左側的兩條邊朝著您。 看起來就是一條細線。
這不是我們所想要的。我們希望星星永遠正面朝著我們,而不管屏幕如何旋轉或傾斜。
我們通過在繪制星星之前,抵消對星星所作的任何旋轉來實現(xiàn)這個愿望。
您可以采用逆序來抵消旋轉。當我們傾斜屏幕時,我們實際上以當前角度旋轉了星星。
通過逆序,我們又以當前角度"反旋轉"星星。也就是以當前角度的負值來旋轉星星。
就是說,
如果我們將星星旋轉了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,我們在屏幕上先畫一次不旋轉的星星:
將星星總數(shù)(num) 減去當前的星星數(shù)(loop)再減去1,
來提取每顆星星的不同顏色(這么做是因為循環(huán)范圍從0到num-1)。
舉例來說,結果為10的時候,我們就使用10號星星的顏色。
這樣相鄰星星的顏色總是不同的。這不是個好法子,但很有效。
最后一個值是alpha通道分量。這個值越小,這顆星星就越暗。
由于啟用了twinkle,每顆星星最后會被繪制兩遍。
程序運行起來會慢一些,這要看您的機器性能如何了。
但兩遍繪制的星星顏色相互融合,會產生很棒的效果。
同時由于第一遍的星星沒有旋轉,啟用twinkle后的星星看起來有一種動畫效果。
(如果您這里看不懂得話,就自己去看程序的運行效果吧。)
值得注意的是給紋理上色是件很容易的事。
盡管紋理本身是黑白的,紋理將變成我們在繪制它之前選定的任意顏色。
此外,同樣值得注意的是我們在這里使用的顏色值是byte型的,
而不是通常的浮點數(shù)。甚至alpha通道分量也是如此。}
If (twinkle) Then // 啟用閃爍效果
Begin
// 使用byte型數(shù)值指定一個顏色
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;
{
現(xiàn)在繪制第二遍的星星。
唯一和前面的代碼不同的是這一遍的星星肯定會被繪制,并且這次的星星繞著z軸旋轉。
}
glRotatef(spin, 0.0, 0.0, 1.0); // 繞z軸旋轉星星
// 使用byte型數(shù)值指定一個顏色
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; // 改變星星離中心的距離
{接著幾行檢查星星是否已經(jīng)碰到了屏幕中心。
當星星碰到屏幕中心時,我們?yōu)樗x一個新顏色,然后往外移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;
{
現(xiàn)在我們添加監(jiān)視鍵盤的代碼。
下移到WinMain()。找到SwapBuffers(hDC)一行。
我們就在這一行后面增加鍵盤監(jiān)視代碼。
代碼將檢查T鍵是否已按下。
如果T鍵按下過,并且又放開了,if塊內的代碼將被執(zhí)行。
如果twinkle為FALSE,他將變?yōu)門RUE。
反之亦然。只要T鍵按下, tp就變?yōu)門RUE。
這樣處理可以防止如果您一直按著T鍵的話,塊內的代碼被反復執(zhí)行。
}
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鍵時什么也不會發(fā)生。所以這行代碼很重要。
}
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場景中移動。
我已經(jīng)向您展示了如何創(chuàng)建漂亮的顏色與動畫效果。
實現(xiàn)原理是在原始位圖上再重疊一份位圖拷貝。
到現(xiàn)在為止,只要您很好的理解了我所教您的一切,
您應該已經(jīng)能夠毫無問題的制作您自己的3D Demo了。
所有的基礎知識都已包括在內!}
//========myling :
//1-9課已經(jīng)翻譯完了,就象NEHE說的,基本的知識已經(jīng)基本說完了
//我看了下后面的教程,好像是出自其他人之手,如果有好的例子,我會選擇性的繼
//續(xù)貼的,好累,睡一會:) ,下次見
新聞熱點
疑難解答
圖片精選