這一課我會教您如何使用三種不同的紋理濾波方式。教您如何使用鍵盤來移動場景中的對象,還會教您在
OpenGL場
景中應用簡單的光照。這一課包含了很多內容,如果您對前面的課程有疑問的話,先回頭復習一下。進入后面的代碼之前,很好的理解基礎知識十分重要。我們還是
在第一課的代碼上加以修改。跟以前不一樣的是,只要有任何大的改動,我都會寫出整段代碼。程序開始,我們先加上幾個新的變量。
#include <windows.h> // Windows的頭文件
#include <stdio.h> // 標準輸入/輸出庫的頭文件 (新增)
#include <gl\\gl.h> // OpenGL32庫的頭文件
#include <gl\\glu.h> // GLu32庫的頭文件
#include <gl\\glaux.h> // GLaux庫的頭文件
HGLRC hRC=NULL; // 永久著色描述表
HDC hDC=NULL; // 私有GDI設備描述表
HWND hWnd=NULL; // 保存我們的窗口句柄
HINSTANCE hInstance; // 保存程序的實例
bool keys[256]; // 用于鍵盤例程的數組
bool active=TRUE; // 窗口的活動標志,缺省為TRUE
bool fullscreen=TRUE; // 全屏標志缺省設定成全屏模式 下面幾行是新的。我們增加三個布爾變量。
light變量跟蹤光照是否打開。變量
lp和
fp用來存儲‘L’和‘F’鍵是否按下的狀態。后面我會解釋這些變量的重要性。現在,先放在一邊吧。
BOOL light; ? ?
// 光源的開/關
BOOL lp; // L鍵按下了么?
BOOL fp; // F鍵按下了么?
現在設置5個變量來控制繞x軸和y軸旋轉角度的步長,以及繞x軸和y軸的旋轉速度。另外還創建了一個z變量來控制進入屏幕深處的距離。
GLfloat xrot; // X 旋轉
GLfloat yrot; // Y 旋轉
GLfloat xspeed; // X 旋轉速度
GLfloat yspeed; // Y 旋轉速度
GLfloat z=-5.0f; // 深入屏幕的距離
接著設置用來創建光源的數組。我們將使用兩種不同的光。第一種稱為環境光。環境光來自于四面八方。所有場景中的對象都處于環境光的照射中。第二種類型的光
源叫做漫射光。漫射光由特定的光源產生,并在您的場景中的對象表面上產生反射。處于漫射光直接照射下的任何對象表面都變得很亮,而幾乎未被照射到的區域就
顯得要暗一些。這樣在我們所創建的木板箱的棱邊上就會產生的很不錯的陰影效果。
創建光源的過程和顏色的創建完全一致。前三個參數分別是RGB三色分量,最后一個是alpha通道參數。
因此,下面的代碼我們得到的是半亮(0.5f)的白色環境光。如果沒有環境光,未被漫射光照到的地方會變得十分黑暗。
GLfloat LightAmbient[]= { 0.5f, 0.5f, 0.5f, 1.0f }; // 環境光參數 (新增)
下一行代碼我們生成最亮的漫射光。所有的參數值都取成最大值1.0f。它將照在我們木板箱的前面,看起來挺好。
GLfloat LightDiffuse[]= { 1.0f, 1.0f, 1.0f, 1.0f }; // 漫射光參數 (新增)
最后我們保存光源的位置。前三個參數和glTranslate中的一樣。依次分別是XYZ軸上的位移。由于我們想要光線直接照射在木箱的正面,所以XY軸
上的位移都是0.0f。第三個值是Z軸上的位移。為了保證光線總在木箱的前面,所以我們將光源的位置朝著觀察者(就是您哪。)挪出屏幕。我們通常將屏幕也
就是顯示器的屏幕玻璃所處的位置稱作Z軸的0.0f點。所以Z軸上的位移最后定為2.0f。假如您能夠看見光源的話,它就浮在您顯示器的前方。當然,如果
木箱不在顯示器的屏幕玻璃后面的話,您也無法看見箱子。最后一個參數取為1.0f。這將告訴OpenGL這里指定的坐標就是光源的位置,以后的
教程中我會多加解釋。
GLfloat LightPosition[]= { 0.0f, 0.0f, 2.0f, 1.0f }; // 光源位置 ( 新增 )
filter變
量跟蹤顯示時所采用的紋理類型。第一種紋理(texture 0)使用gl_nearest(不光滑)濾波方式構建。第二種紋理(texture
1)使用gl_linear(線性濾波)方式,離屏幕越近的圖像看起來就越光滑。第三種紋理 (texture
2)使用mipmapped濾波方式,這將創建一個外觀十分優秀的紋理。根據我們的使用類型,
filter變量的值分別等于0,1或2。下面我們從第一種紋理開始。
GLuint texture[3]為三種不同紋理分配儲存空間。它們分別位于在texture[0]、texture[1]、texture[2]中。
GLuint filter; // 濾波類型
GLuint texture[3]; // 3種紋理的儲存空間
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); // WndProc定義
現在載入一個位圖,并用它創建三種不同的紋理。這一課使用glaux輔助庫來載入位圖,因此在編譯時您應該確認是否包含了glaux庫。我知道Delphi和VC++都包含了glaux庫,但別的語言不能保證都有。(
譯者:
glaux是OpenGL輔助庫,根據OpenGL的跨平臺特性,所有平臺上的代碼都應通用。但輔助庫不是正式的OpenGL標準庫,沒有出現在所有的平臺上。但正好在Win32平臺上可用。呵呵,BCB當然也沒問題了。)這里我只對新增的代碼做注解。如果您對某行代碼有疑問的話,請查看教程六。那一課很詳細的解釋了載入、創建紋理的內容。
在上一段代碼后面及ReSizeGLScene()之前的位置,我們增加了下面的代碼。這和第六課中載入位圖的代碼幾乎相同。
AUX_RGBImageRec *LoadBMP(char *Filename) // 載入位圖
{
FILE *File=NULL; // 文件句柄
if (!Filename) // 確認文件名已初始化
{
return NULL; // 沒有返回 NULL
}
File=fopen(Filename,"r"); // 檢查文件是否存在
if (File) // 存在么?
{
fclose(File); // 關閉文件句柄
return auxDIBImageLoad(Filename); // 載入位圖并返回一個指針
}
return NULL; // 載入失敗返回 NULL
}
這段代碼調用前面的代碼載入位圖,并將其轉換成3個紋理。
Status變量跟蹤紋理是否已載入并被創建了。
int LoadGLTextures() // 載入位圖并轉換成紋理
{
int Status=FALSE; // Status 指示器
AUX_RGBImageRec *TextureImage[1]; // 創建紋理的存儲空間
memset(TextureImage,0,sizeof(void *)*1); // 將指針設為 NULL
現在載入位圖并轉換成紋理。TextureImage[0]=LoadBMP("Data/Crate.bmp")調用我們的LoadBMP()函數。
Data目錄下的“Crate.bmp”將被載入。如果一切正常,圖像數據將存放在TextureImage[0]。Status變量被設為TRUE,我
們將開始創建紋理。
// 載入位圖,檢查有錯,或位圖不存在的話退出。
if (TextureImage[0]=LoadBMP("Data/Crate.bmp"))
{
Status=TRUE; // Status 設為 TRUE
現在我們已經將圖像數據載入TextureImage[0]。我們將用它來創建3個紋理。下面的行告訴OpenGL我們要創建三個紋理,它們將存放在texture[0]、texture[1]、texture[2] 中。
glGenTextures(3, &texture[0]); // 創建紋理
第六課中我們使用了線性濾波的紋理貼圖。這需要機器有相當高的處理能力,但它們看起來很不錯。這一課中,我們接著要創建的第一種紋理使用
GL_NEAREST方式。從原理上講,這種方式沒有真正進行濾波。它只占用很小的處理能力,看起來也很差。唯一的好處是這樣我們的工程在很快和很慢的機
器上都可以正常運行。
您會注意到我們在MIN和MAG時都采用了GL_NEAREST,你可以混合使用GL_NEAREST和GL_LINEAR。紋理看起來效果會好些,但我
們更關心速度,所以全采用低質量貼圖。MIN_FILTER在圖像繪制時小于貼圖的原始尺寸時采用。MAG_FILTER在圖像繪制時大于貼圖的原始尺寸
時采用。
// 創建 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);
下個紋理與第六課的相同,線性濾波。唯一的不同是這次放在了texture[1]中。因為這是第二個紋理。如果放在texture[0]中的話,他將覆蓋前面創建的GL_NEAREST紋理。
// 創建線性濾波紋理
glBindTexture(GL_TEXTURE_2D, texture[1]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3,
TextureImage[0]->sizeX, TextureImage[0]->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
下面是創建紋理的新方法:Mip-mapping(紋理細化)。您可能會注意到當圖像在屏幕上變得很小的時候,很多細節將會丟失。剛才還很不錯的圖案變得
很難看。當您告訴OpenGL創建一個mipmapped的紋理后,OpenGL將嘗試創建不同尺寸的高質量紋理。當您向屏幕繪制一個mipmapped
紋理的時候,OpenGL將選擇它已經創建的外觀最佳的紋理(帶有更多細節)來繪制,而不僅僅是縮放原先的圖像(這將導致細節丟失)。
我曾經說過有辦法可以繞過OpenGL對紋理寬度和高度所加的限制 — 64、128、256,等等。辦法就是
gluBuild2DMipmaps。據我的發現,您可以使用任意的位圖來創建紋理。OpenGL將自動將它縮放到正常的大小。因為是第三個紋理,我們將
它存到texture[2]。這樣本課中的三個紋理全都創建好了。
// 創建 MipMapped 紋理
glBindTexture(GL_TEXTURE_2D, texture[2]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);// (新增)
下面一行生成mipmapped紋理。我們使用三種顏色(紅,綠,藍)來生成一個2D紋理。TextureImage[0]->sizeX
是位圖寬度,extureImage[0]->sizeY是位圖高度,GL_RGB意味著我們依次使用RGB色彩。
GL_UNSIGNED_BYTE意味著紋理數據的單位是字節。TextureImage[0]->data指向我們創建紋理所用的位圖。
gluBuild2DMipmaps(GL_TEXTURE_2D, 3,
TextureImage[0]->sizeX, TextureImage[0]->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); // ( 新增 )
}
現在釋放用來存放位圖數據的內存。我們先查看位圖數據是否存放在TextureImage[0]中,如果有,刪掉。然后釋放位圖結構以確保內存被釋放。
if (TextureImage[0]) // 紋理是否存在
{
if (TextureImage[0]->data) // 紋理圖像是否存在
{
free(TextureImage[0]->data); // 釋放紋理圖像占用的內存
}
free(TextureImage[0]); // 釋放圖像結構
}
最后我們返回
status變量。如果一切OK,
status變量的值為TRUE。否則為FALSE。
return Status; // 返回 Status 變量
}
接著應該載入紋理并初始化OpenGL設置了。InitGL函數的第一行使用上面的代碼載入紋理。創建紋理之后,我們調用glEnable(GL_TEXTURE_2D)啟用2D紋理映射。陰影模式設為平滑陰影(
smooth shading)。背景色設為黑色,我們啟用深度測試,然后我們啟用優化透視計算。
int InitGL(GLvoid) // 此處開始對OpenGL進行所有設置
{
if (!LoadGLTextures()) // 跳轉到紋理載入例程
{
return FALSE; // 如果不能載入紋理返回 FALSE
}
glEnable(GL_TEXTURE_2D); // 啟用紋理映射
glShadeModel(GL_SMOOTH); // 啟用陰影平滑
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // 黑色背景
glClearDepth(1.0f); // 深度緩存設置
glEnable(GL_DEPTH_TEST); // 啟用深度測試
glDepthFunc(GL_LEQUAL); // 所作的深度測試類型
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// 高度優化的透視投影計算
現在開始設置光源。下面下面一行設置環境光的發光量,光源light1開始發光。這一課的開始處我們我們將環境光的發光量存放在LightAmbient數組中。現在我們就使用此數組(半亮度環境光)。
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient); // 設置環境光
接下來我們設置漫射光的發光量。它存放在LightDiffuse數組中(全亮度白光)。
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse); // 設置漫射光
然后設置光源的位置。位置存放在 LightPosition 數組中(正好位于木箱前面的中心,X-0.0f,Y-0.0f,Z方向移向觀察者2個單位[位于屏幕外面])。
glLightfv(GL_LIGHT1, GL_POSITION,LightPosition); // 光源位置
最后,我們啟用一號光源。我們還沒有啟用GL_LIGHTING,所以您看不見任何光線。記住:只對光源進行設置、定位、甚至啟用,光源都不會工作。除非我們啟用GL_LIGHTING。
glEnable(GL_LIGHT1); // 啟用一號光源
return TRUE; // 初始化 OK
}
下一段代碼繪制貼圖立方體。我只對新增的代碼進行注解。如果您對沒有注解的代碼有疑問,回頭看看第六課。
int DrawGLScene(GLvoid) // 從這里開始進行所有的繪制
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// 清除屏幕和深度緩存
glLoadIdentity(); // 重置當前的模型觀察矩陣 下三行代碼放置并旋轉貼圖立方體。glTranslatef(0.0f,0.0f,z)將立方體沿著
Z軸移動Z單位。glRotatef(xrot,1.0f,0.0f,0.0f)將立方體繞X軸旋轉
xrot。glRotatef(yrot,0.0f,1.0f,0.0f)將立方體繞Y軸旋轉
yrot。
glTranslatef(0.0f,0.0f,z); // 移入/移出屏幕 z 個單位 glRotatef(xrot,1.0f,0.0f,0.0f); // 繞X軸旋轉
glRotatef(yrot,0.0f,1.0f,0.0f); // 繞Y軸旋轉
下一行與我們在第六課中的類似。有所不同的是,這次我們綁定的紋理是texture[filter],而不是上一課中的texture[0]。任何時候,
我們按下F鍵,filter的值就會增加。如果這個數值大于2,變量filter
將被重置為0。程序初始時,變量filter的值也將設為0。使用變量filter我們就可以選擇三種紋理中的任意一種。
glBindTexture(GL_TEXTURE_2D, texture[filter]); // 選擇由filter決定的紋理
glBegin(GL_QUADS); // 開始繪制四邊形
glNormal3f是這一課的新東西。Normal就是法線的意思,所謂法線是指經過面(多邊形)上的一點且垂直于這個面(多邊形)的直線。使用光源的
時候必須指定一條法線。法線告訴OpenGL這個多邊形的朝向,并指明多邊形的正面和背面。如果沒有指定法線,什么怪事情都可能發生:不該照亮的面被照亮
了,多邊形的背面也被照亮....。對了,法線應該指向多邊形的外側。
看著木箱的前面您會注意到法線與Z軸正向同向。這意味著法線正指向觀察者-您自己。這正是我們所希望的。對于木箱的背面,也正如我們所要的,法線背對著觀
察者。如果立方體沿著X或Y軸轉個180度的話,前側面的法線仍然朝著觀察者,背面的法線也還是背對著觀察者。換句話說,不管是哪個面,只要它朝著觀察者
這個面的法線就指向觀察者。由于光源緊鄰觀察者,任何時候法線對著觀察者時,這個面就會被照亮。并且法線越朝著光源,就顯得越亮一些。如果您把觀察點放到
立方體內部,你就會法線里面一片漆黑。因為法線是向外指的。如果立方體內部沒有光源的話,當然是一片漆黑。
// 前側面
glNormal3f( 0.0f, 0.0f, 1.0f); // 法線指向觀察者
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);// Point 1 (Front)
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);// Point 2 (Front)
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Point 3 (Front)
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Point 4 (Front)
// 后側面
glNormal3f( 0.0f, 0.0f,-1.0f); // 法線背向觀察者
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Point 1 (Back)
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Point 2 (Back)
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Point 3 (Back)
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Point 4 (Back)
// 頂面
glNormal3f( 0.0f, 1.0f, 0.0f); // 法線向上
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);// Point 1 (Top)
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Point 2 (Top)
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Point 3 (Top)
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);// Point 4 (Top)
// 底面
glNormal3f( 0.0f,-1.0f, 0.0f); // 法線朝下
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Point 1 (Bottom)
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Point 2 (Bottom)
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Point 3 (Bottom)
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Point 4 (Bottom)
// 右側面
glNormal3f( 1.0f, 0.0f, 0.0f); // 法線朝右
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);// Point 1 (Right)
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // Point 2 (Right)
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Point 3 (Right)
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Point 4 (Right)
// 左側面
glNormal3f(-1.0f, 0.0f, 0.0f); // 法線朝左
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);// Point 1 (Left)
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Point 2 (Left)
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Point 3 (Left)
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // Point 4 (Left)
glEnd(); ? ?
// 四邊形繪制結束
下兩行代碼將
xot和
yrot的旋轉值分別增加
xspeed和
yspeed個單位。
xspeed和
yspeed的值越大,立方體轉得就越快。
xrot+=xspeed; // xrot 增加 xspeed 單位
yrot+=yspeed; // yrot 增加 yspeed 單位
return TRUE; }
現在轉入WinMain()主函數。我們將在這里增加開關光源、旋轉木箱、切換過濾方式以及將木箱移近移遠的控制代碼。在接近WinMain()函數結束
的地方你會看到SwapBuffers(hDC)這行代碼。然后就在這一行后面添加如下的代碼。代碼將檢查L鍵是否按下過。如果L鍵已按下,但
lp的值不是false的話,意味著L鍵還沒有松開,這時什么都不會發生。
SwapBuffers(hDC); // 交換緩存 (雙緩存) if (keys[\’L\’] && !lp)// L 鍵已按下并且松開了?
{ 如果
lp的值是false的話,意味著L鍵還沒按下,或者已經松開了,接著
lp將被設為TRUE。同時檢查這兩個條件的原因是為了防止L鍵被按住后,這段代碼被反復執行,并導致窗體不停閃爍。
lp設為true之后,計算機就知道L鍵按過了,我們則據此可以切換光源的開/關:布爾變量
light控制光源的開關。
lp=TRUE; // lp 設為 TRUE
light=!light;// 切換光源的 TRUE/FALSE
下面幾行來檢查光源是否應該打開,并根據light變量的值。
if (!light) // 如果沒有光源
{
glDisable(GL_LIGHTING);//禁用光源
}
else // Otherwise
{
glEnable(GL_LIGHTING);//啟用光源
}
}
下面的代碼查看是否松開了“L”鍵。如果松開,變量
lp將設為false。這意味著“L”鍵沒有按下。如果不作此檢查,光源第一次打開之后,就無法再關掉了。計算機會以為“L”鍵一直按著呢。
if (!keys[\’L\’]) // L鍵松開了么?
{
lp=FALSE; // 若是,則將lp設為FALSE
}
然后對“F”鍵作相似的檢查。如果有按下“F”?⑶搖癋”鍵沒有處于按著的狀態或者它就從沒有按下過,將變量fp設為true。這意味著這個鍵正被按著
呢。接著將filter變量加一。如果filter變量大于2(因為這里我們的使用的數組是texture[3],大于2的紋理不存在),我們重置
filter變量為0。
if (keys[\’F\’] && !fp)// F鍵按下了么?
{
fp=TRUE; // fp 設為 TRUE
filter+=1; // filter的值加一
if (filter>2)// 大于2了么?
{
filter=0; // 若是重置為0
}
}
if (!keys[\’F\’]) // F鍵放開了么?
{
fp=FALSE; // 若是fp設為FALSE
}
這四行檢查是否按下了PageUp鍵。若是的話,減少
z變量的值。這樣DrawGLScene函數中包含的glTranslatef(0.0f,0.0f,z)調用將使木箱離觀察者更遠一點。
if (keys[VK_PRIOR]) // PageUp按下了?
{
z-=0.02f; // 若按下,將木箱移向屏幕內部。
}
接著四行檢查PageDown鍵是否按下,若是的話,增加
z變量的值。這樣DrawGLScene函數中包含的glTranslatef(0.0f,0.0f,z)調用將使木箱向著觀察者移近一點。
if (keys[VK_NEXT]) // PageDown按下了么?
{
z+=0.02f; //若按下的話,將木箱移向觀察者。
}
現在檢查方向鍵。按下左右方向鍵
xspeed相應減少或增加。按下上下方向鍵
yspeed相應減少或增加。記住在以后的教程中如果
xspeed、
yspeed的值增加的話,立方體就轉的更快。如果一直按著某個方向鍵,立方體會在那個方向上轉的越快。
if (keys[VK_UP]) // Up方向鍵按下了么?
{
xspeed-=0.01f;// 若是,減少xspeed
}
if (keys[VK_DOWN]) // Down方向鍵按下了么?
{
xspeed+=0.01f;// 若是,增加xspeed
}
if (keys[VK_RIGHT]) // Right方向鍵按下了么?
{
yspeed+=0.01f;// 若是,增加yspeed
}
if (keys[VK_LEFT]) // Left方向鍵按下了么?
{
yspeed-=0.01f;// 若是, 減少yspeed
}
像前幾課一樣,我們最后還需要更正窗體的標題。
if (keys[VK_F1]) // F1鍵按下了么?
{
keys[VK_F1]=FALSE;
KillGLWindow(); // 銷毀當前的窗口
fullscreen=!fullscreen;// 切換 全屏/窗口 模式
// 重建 OpenGL 窗口(修改)
if (!CreateGLWindow("NeHe\’s Textures, Lighting & Keyboard Tutorial",640,480,16,fullscreen))
{
return 0;// 如果窗口未能創建,程序退出
}
} } }
} // 關閉
KillGLWindow(); // 銷毀窗口
return (msg.wParam); // 退出程序
}