OpenGL可以把紋理映射到指定的圖形的表面上。簡單一點的,就是給平面映射紋理,比如一個四邊形,一個長方體的6個面,都可以指定位圖作為紋理映射到各個面上。
關(guān)于將一個位圖作為紋理映射到某個或者多個面上,可以學(xué)習(xí)Jeff Molofee的OpenGL系列教程。
對于指定的多個紋理,要根據(jù)自己的需要映射到不同的面上,需要對位圖創(chuàng)建一個數(shù)組,用來存儲位圖的名稱,然后在初始化OpenGL的時候,可以讀取這些位圖,然后生成多個紋理存儲到一個紋理數(shù)組中,接著就可以指定繪制的某個面,對該指定的面進(jìn)行紋理映射。
下面,在的Jeff Molofee教程的第六課的基礎(chǔ)上,實現(xiàn)對6個面分別進(jìn)行不同的紋理映射。
準(zhǔn)備工作就是制作6幅不同的位圖,如圖所示:

關(guān)鍵代碼及其說明如下。
創(chuàng)建全局紋理數(shù)組
GLuint texture[6]; // 創(chuàng)建一個全局的紋理數(shù)組,用來存儲將位圖轉(zhuǎn)換之后得到的紋理,對應(yīng)于立方體的6個面
加載位圖文件
加載位圖,也就是把位圖讀取到內(nèi)存空間,實現(xiàn)紋理的創(chuàng)建,加載位圖的函數(shù)說明一下:
AUX_RGBImageRec *LoadBMP(char *Filename) // 根據(jù)位圖文件的名稱進(jìn)行加載
{
FILE *File=NULL; // 文件指針
if (!Filename) // 如果沒有指定位圖文件名稱就返回NULL
{
return NULL;
}
File=fopen(Filename,"r"); // 根據(jù)指定的位圖文件名稱,打開該位圖文件
if (File) // 如果位圖文件存在
{
fclose(File); // 因為只是需要判斷問題是否存在,而不需要對位圖文件進(jìn)行寫操作,所以關(guān)閉位圖文件
return auxDIBImageLoad(Filename); // 其實,只需要一個真正存在的位圖文件的名稱,實現(xiàn)加載位圖文件,并返回一個指針
}
return NULL; // 位圖文件加載失敗就返回NULL
}
上面實現(xiàn)加載位圖的函數(shù)中,AUX_RGBImageRec是glaux.h中定義的類型,該類型的定義如下所示:
/*
** RGB Image Structure
*/
typedef struct _AUX_RGBImageRec {
GLint sizeX, sizeY;
unsigned char *data;
} AUX_RGBImageRec;
首先,AUX_RGBImageRec類型是一個RGB圖像結(jié)構(gòu)類型。該結(jié)構(gòu)定義了三個成員:
sizeX —— 圖像的寬度;
sizeY —— 圖像的高度;
data; —— 圖形所包含的數(shù)據(jù),其實也就是該圖形在內(nèi)存中的像素數(shù)據(jù)的一個指針。
AUX_RGBImageRec類型的變量描述了一幅圖像的特征。
上述函數(shù)中,調(diào)用了glaux.h庫文件中的auxDIBImageLoad函數(shù),其實它是一個宏,函數(shù)原型為auxRGBImageLoadW(LPCWSTR)或者auxRGBImageLoadA(LPCSTR),可以在該庫文件中找到它的定義,如下所示:
/* AUX_RGBImageRec * APIENTRY auxRGBImageLoad(LPCTSTR); */
#ifdef UNICODE
#define auxRGBImageLoad auxRGBImageLoadW
#else
#define auxRGBImageLoad auxRGBImageLoadA
#endif
AUX_RGBImageRec * APIENTRY auxRGBImageLoadA(LPCSTR);
AUX_RGBImageRec * APIENTRY auxRGBImageLoadW(LPCWSTR);
#ifdef UNICODE
#define auxDIBImageLoad auxDIBImageLoadW
#else
#define auxDIBImageLoad auxDIBImageLoadA
#endif
AUX_RGBImageRec * APIENTRY auxDIBImageLoadA(LPCSTR);
AUX_RGBImageRec * APIENTRY auxDIBImageLoadW(LPCWSTR);
宏auxDIBImageLoad實現(xiàn)的功能就是:根據(jù)指定的位圖名稱,將該位圖的信息加載到內(nèi)存中,以便用來創(chuàng)建成為紋理。
創(chuàng)建紋理并加載紋理
用于創(chuàng)建并加載紋理的函數(shù)為LoadGLTextures,實現(xiàn)如下所示:
int LoadGLTextures() // 根據(jù)加載的位圖創(chuàng)建紋理
{
int Status=FALSE; // 指示紋理創(chuàng)建是否成功的標(biāo)志
AUX_RGBImageRec *TextureImage[6]; // 創(chuàng)建一個紋理圖像數(shù)組,這里指定數(shù)組大小為6
memset(TextureImage,0,sizeof(void *)*6); // 初始化紋理圖像數(shù)組,為其分配內(nèi)存
char *pictures[] = {// 創(chuàng)建一個位圖名稱數(shù)組,對應(yīng)6幅位圖
"Data/No1.bmp",
"Data/No2.bmp",
"Data/No3.bmp",
"Data/No4.bmp",
"Data/No5.bmp",
"Data/No6.bmp"
};
for(int i=0; i<6; i++)// 遍歷位圖名稱數(shù)組,根據(jù)位圖名稱分別生成
{
if (TextureImage[i]=LoadBMP(pictures[i]))// 加載位圖i成功,修改狀態(tài)標(biāo)志變量Status為TRUE
{
Status=TRUE;
glGenTextures(1, &texture[i]); // 為第i個位圖創(chuàng)建紋理
glBindTexture(GL_TEXTURE_2D, texture[i]);// 將生成的紋理的名稱綁定到指定的紋理上
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[i]->sizeX, TextureImage[i]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[i]->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
}
if (TextureImage[i]) // 釋放位圖數(shù)組占用的內(nèi)存空間
{
if (TextureImage[i]->data)
{
free(TextureImage[i]->data);
}
free(TextureImage[i]);
}
}
return Status; // 創(chuàng)建紋理并加載,返回成功或者失敗的標(biāo)志Status
}
上述函數(shù)是創(chuàng)建和加載紋理的核心實現(xiàn)。
1、glGenTextures函數(shù)
其中,調(diào)用了glGenTextures函數(shù),查看MSDN可以看到,聲明如下所示:
void glGenTextures(
GLsizein,
GLuint *textures
);
函數(shù)參數(shù)的含義:
n—— 生成的紋理的名稱的個數(shù);
textures—— 生成的紋理名稱所存儲位置的指針,也就是一個紋理數(shù)組的內(nèi)存地址,或者說是數(shù)組首元素的內(nèi)存地址。
函數(shù)被調(diào)用,會生成一系列紋理的名字,并存儲到指定的數(shù)組中。
2、glBindTexture函數(shù)
glBindTexture函數(shù)實現(xiàn)了將調(diào)用glGenTextures函數(shù)生成的紋理的名字綁定到對應(yīng)的目標(biāo)紋理上。該函數(shù)的聲明如下所示:
void glBindTexture(
GLenumtarget,
GLuinttexture
);
函數(shù)參數(shù)的含義:
target—— 紋理被綁定的目標(biāo),它只能取值GL_TEXTURE_1D或者GL_TEXTURE_2D;
texture—— 紋理的名稱,并且,該紋理的名稱在當(dāng)前的應(yīng)用中不能被再次使用。
3、glTexImage2D函數(shù)
調(diào)用glTexImage2D函數(shù),用來指定二維紋理圖像。該函數(shù)的聲明如下所示:
void glTexImage2D(
GLenumtarget,
GLintlevel,
GLintcomponents,
GLsizeiwidth,
GLsizeiheight,
GLintborder,
GLenumformat,
GLenumtype,
const GLvoid*pixels
);
函數(shù)參數(shù)的含義:
target—— 指定目標(biāo)紋理,必須為GL_TEXTURE_2D;
level—— 指定圖像級別的編號,0表示基本圖像,其它可以參考MSDN;
components—— 紋理中顏色組件的編號,可是是1或2或3或4;
width—— 紋理圖像的寬度;
height—— 紋理圖像的高度;
border—— 紋理圖像的邊框?qū)挾龋仨毷?或1;
format—— 指定像素數(shù)據(jù)的格式,一共有9個取值:GL_COLOR_INDEX、GL_RED、GL_GREEN、GL_BLUE、GL_ALPHA、GL_RGB、GL_RGBA、GL_BGR_EXT、GL_BGRA_EXT、GL_LUMINANCE、GL_LUMINANCE_ALPHA ,具體含義可以參考MSDN;
type—— 像素數(shù)據(jù)的數(shù)據(jù)類型,取值可以為GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, and GL_FLOAT;
pixels—— 內(nèi)存中像素數(shù)據(jù)的指針。
4、glTexParameteri函數(shù)
glTexParameteri函數(shù)或者glTexParameterf函數(shù)用來設(shè)置紋理參數(shù),聲明如下所示:
void glTexParameterf(
GLenumtarget,
GLenumpname,
GLfloatparam
);
void glTexParameteri(
GLenumtarget,
GLenumpname,
GLintparam
);
函數(shù)參數(shù)的含義:
target—— 目標(biāo)紋理,必須為GL_TEXTURE_1D或GL_TEXTURE_2D;
pname—— 用來設(shè)置紋理映射過程中像素映射的問題等,取值可以為:GL_TEXTURE_MIN_FILTER、GL_TEXTURE_MAG_FILTER、GL_TEXTURE_WRAP_S、GL_TEXTURE_WRAP_T,詳細(xì)含義可以查看MSDN;
param—— 實際上就是pname的值,可以參考MSDN。
另外,該類函數(shù)還有兩個:
void glTexParameterfv(
GLenumtarget,
GLenumpname,
const GLfloat*params
);
void glTexParameteriv(
GLenumtarget,
GLenumpname,
const GLint*params
);
上述程序中調(diào)用如下:
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
功能就是實現(xiàn)線形濾波的功能,當(dāng)紋理映射到圖形表面以后,如果因為其它條件的設(shè)置導(dǎo)致紋理不能更好地顯示的時候,進(jìn)行過濾,按照指定的方式進(jìn)行顯示,可能會過濾掉顯示不正常的紋理像素。
紋理映射過程
紋理映射的過程是在DrawGLScene函數(shù)中實現(xiàn)的,也就是在繪制圖形的過程中,直接進(jìn)行我呢里映射,或者稱為,為指定的平面貼紋理,DrawGLScene函數(shù)實現(xiàn)如下所示:
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f,0.0f,-5.0f);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
glRotatef(zrot,0.0f,0.0f,1.0f);
// Front Face
glBindTexture(GL_TEXTURE_2D, texture[0]);// 選擇第一個紋理texture[0],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glEnd();
// Back Face
glBindTexture(GL_TEXTURE_2D, texture[1]);// 選擇第二個紋理texture[1],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glEnd();
// Top Face
glBindTexture(GL_TEXTURE_2D, texture[2]);// 選擇第三個紋理texture[2],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glEnd();
// Bottom Face
glBindTexture(GL_TEXTURE_2D, texture[3]);// 選擇第四個紋理texture[3],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glEnd();
// Right face
glBindTexture(GL_TEXTURE_2D, texture[4]);// 選擇第五個紋理texture[4],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glEnd();
// Left Face
glBindTexture(GL_TEXTURE_2D, texture[5]);// 選擇第六個紋理texture[5],進(jìn)行貼紋理
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
xrot+=0.3f;
yrot+=0.2f;
zrot+=0.4f;
return TRUE;
}
因為,通過前面的過程,已經(jīng)將位圖加載并創(chuàng)建和加載紋理成功,紋理數(shù)組已經(jīng)存在于內(nèi)存之中,調(diào)用上述函數(shù)實現(xiàn)紋理映射,即,從內(nèi)存中取出指定的紋理,將其映射到立方體的指定的面上。
上述函數(shù)中調(diào)用了glTexCoord2f函數(shù),設(shè)置紋理坐標(biāo),該函數(shù)的聲明如下所示:
void glTexCoord2f(
GLfloats,
GLfloatt
);
glTexCoord2f 的第一個參數(shù)是X坐標(biāo),當(dāng)s=0.0f 時是紋理的左側(cè),s=0.5f 時是紋理的中點,s=1.0f 時是紋理的右側(cè)。 glTexCoord2f 的第二個參數(shù)是Y坐標(biāo),t=0.0f 是紋理的底部,t=0.5f 是紋理的中點, t=1.0f 是紋理的頂部。
上述函數(shù)在為前面那個面映射紋理的時候調(diào)用如下:
// Front Face
glBindTexture(GL_TEXTURE_2D, texture[0]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glEnd();
其中:
glTexCoord2f(0.0f, 0.0f);表示將紋理texture[0]的左下角坐標(biāo)(0.0f, 0.0f)映射到立方體前面那個面的頂點(-1.0f, -1.0f, 1.0)上;
glTexCoord2f(1.0f, 0.0f);表示將紋理texture[0]的右下角坐標(biāo)(1.0f, 0.0f)映射到立方體前面那個面的頂點(1.0f, -1.0f, 1.0f)上;
glTexCoord2f(1.0f, 1.0f);表示將紋理texture[0]的右上角坐標(biāo)(1.0f, 1.0f)映射到立方體前面那個面的頂點(1.0f, 1.0f, 1.0f)上;
glTexCoord2f(0.0f, 1.0f);表示將紋理texture[0]的左上角坐標(biāo)(0.0f, 1.0f)映射到立方體前面那個面的頂點(-1.0f, 1.0f, 1.0f)上。
這樣,紋理texture[0]就被映射到了立方體前面那個面上。
紋理映射結(jié)果
為了使立方體能夠運動起來,對立方體進(jìn)行累的旋轉(zhuǎn)變換,而且,定義了三個全局變量:
GLfloat xrot; // 沿著x旋轉(zhuǎn)的變量
GLfloat yrot; // 沿著y旋轉(zhuǎn)的變量
GLfloat zrot; // 沿著z旋轉(zhuǎn)的變量
初始化都為0,然后,在每次調(diào)用DrawGLScene函數(shù)的時候,改變x、y、z的分量值,在DrawGLScene函數(shù)中如下所示:
xrot+=0.3f;
yrot+=0.2f;
zrot+=0.4f;
在DrawGLScene函數(shù)中還要執(zhí)行旋轉(zhuǎn)變換:
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
glRotatef(zrot,0.0f,0.0f,1.0f);
每次重繪都在改變旋轉(zhuǎn)軸,所以我們繪制的立方體能夠動起來,看到各個進(jìn)行紋理映射的面的效果,如圖所示@}`N%JBO(8V493@3]~L.jpg)
