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