1. MIP貼圖
MIP貼圖的產(chǎn)生主要是為了解決渲染時的閃爍和性能耗費(fèi)的問題沐兰,簡單來說就是當(dāng)需要屏幕上只需要處理一小部分片段時赏半,但是這時候確加載了大的紋理圖像贺归,而大的紋理圖像需要更多的變換操作這就帶來了性能的消耗,而圖片過大與屏幕顯示區(qū)域不成正比断箫,這樣各種變換從采樣到紋理應(yīng)用的過程中就容易帶來閃爍的效果拂酣。
MIP貼圖可以解決掉上述問題,當(dāng)載入某個紋理時仲义,會為加載的這個紋理圖像生成多個不同大小的紋理圖像婶熬,最大的為紋理圖像本身大小依次減半,直到最小的為紋理單元的大小埃撵,這樣當(dāng)處理小片段時可以選用小的紋理赵颅,大的片段可以選用大的紋理,這樣就避免了閃爍和性能的耗費(fèi)的問題了暂刘。但是使用MIP貼圖之后加載的紋理圖像需要的內(nèi)存會多三分之一左右饺谬。
加載MIP貼圖我們可以使用glTexImage函數(shù)而其中的level會指定使用那層的MIP層紋理,如果MIP貼圖沒有被使用那么久加載第零層谣拣,使用的話會加載所有MIP層的募寨。我們也可以通過glTexParameteri限制MIP加載范圍和使用范圍。
2.MIP貼圖過濾
沒有使用MIP時芝发,我們使用最鄰近過濾或者線性過濾绪商,都會給第零層的紋理貼圖進(jìn)行過濾,而使用了MIP貼圖辅鲸,我們也只會為第零層也就是基層紋理進(jìn)行過濾格郁,其它的MIP層會被忽略,但是我們可以通過下面過濾參數(shù)來指定那層需要過濾独悴。3. 生成MIP層
上面說到了可以在加載已經(jīng)構(gòu)建好的MIP層例书,但有時我們是不需要處理小片段紋理貼圖的,這時候加載所有的MIP層就有些不合適刻炒,這時候我們我們可以在使用某個紋理時在生成MIP層决采,我們可以使用
void glGaneraterMapMip(GLenum target)
需要注意的是在運(yùn)行過程中生成MIP層要比記載已經(jīng)創(chuàng)建好的MIP層慢很多,所以我們也要根據(jù)使用場景來確定使用哪種MIP層加載方式坟奥。
4. 各向?yàn)V性
對于紋理過濾我們可以使用基本的最鄰近過濾和線性過濾來處理紋理圖像顯示時的變形树瞭,但有時候由于觀察角度的改變物體各個方向上的排布也會發(fā)生改變拇厢,變的不那么規(guī)整,這時候如果紋理過濾的方式不做調(diào)整的話晒喷,就會失去一些真實(shí)性孝偎,各向?yàn)V性的紋理過濾方式會解決這個問題,它會根據(jù)物體各個方向上的不同的排布凉敲,而進(jìn)行不同的采樣衣盾,進(jìn)而達(dá)到更準(zhǔn)確的過濾。
5. 壓縮紋理
我們可以通過glTexImage來加載紋理并對其壓縮爷抓,其中internalFormat會指定壓縮的格式势决,需要注意的是不同的硬件支持的壓縮格式不同的,所以我們要在紋理壓縮之前對齊做出兼容判斷蓝撇,glTexImage是當(dāng)使用紋理時對其壓縮加載果复,我們也可以預(yù)先加載紋理,然后保存本地在使用時直接加載預(yù)先壓縮好的紋理渤昌,這時我們可以通過
void glCompressTexImage1d(GLenum target, GLint level, GLint internalformat , GLseizei width, GLint border , GLenum format, GLenum type, void *data)
void glCompressTexImage2d(GLenum target, GLint level, GLint internalformat , GLseizei width, GLseizei height, GLint border , GLenum format, GLenum type, void *data)
void glCompressTexImage3d(GLenum target, GLint level, GLint internalformat , GLseizei width, GLseizei height, GLseizei depth, GLint border , GLenum format, GLenum type, void *data)
這個函數(shù)和glTexImage函數(shù)參數(shù)一樣据悔,僅有的區(qū)別是internalformat參數(shù)必須指定一種本地支持的壓縮格式。
需要注意的是經(jīng)過壓縮的紋理會占用更小的內(nèi)存耘沼,對紋理的讀寫效率更高,但是弊端就是可能不能保證紋理的質(zhì)量朱盐,這要依據(jù)使用場景選擇是否要對紋理進(jìn)行壓縮群嗤。
6. 下面是一個紋理貼圖的例子
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
GLGeometryTransform transformPipeline;
GLBatch floorBatch;
GLBatch ceilingBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLfloat viewZ = -65.0f;
#define TEXTURE_BRICK 0
#define TEXTURE_FLOOR 1
#define TEXTURE_CEILING 2
#define TEXTURE_COUNT 3
GLuint textures[TEXTURE_COUNT];
const char *szTextureFiles[TEXTURE_COUNT] = { "brick.tga", "floor.tga", "ceiling.tga" };
void ProcessMenu(int value)
{
GLfloat fLargest;
GLint iLoop;
//便利紋理對象
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
//根據(jù)紋理對象句柄得到紋理對象
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
switch(value)
{
case 0:
//紋理縮小過濾,最鄰近濾波紋理坐標(biāo)會與最近紋理單元進(jìn)行映射完成一一對應(yīng)的關(guān)系兵琳,達(dá)到過濾的作用狂秘。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case 1:
//紋理縮小過濾,線性過濾躯肌,紋理坐標(biāo)會落在最近的紋理單元中心并對周圍紋理單元進(jìn)行加權(quán)平均運(yùn)算得到一個插值紋理單元與紋理坐標(biāo)映射完成一一對應(yīng)達(dá)到過濾作用者春。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case 2:
//MIP貼圖縮小過濾,最鄰近的MIP貼圖最鄰近過濾清女,對當(dāng)前應(yīng)用的MIP貼圖的紋理圖像的最鄰近的紋理圖像進(jìn)行最鄰近過濾钱烟。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
//MIP貼圖縮小過濾,最鄰近的MIP貼圖線性過濾嫡丙,對當(dāng)前應(yīng)用的MIP貼圖的紋理圖像的最鄰近的紋理圖像進(jìn)行線性過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 4:
//MIP貼圖縮小過濾拴袭,線性的MIP貼圖對最鄰近過濾,對當(dāng)前應(yīng)用的MIP貼圖的紋理圖像的周圍紋理圖像進(jìn)行加權(quán)平均運(yùn)算得到插值的紋理圖像然后對齊進(jìn)行最鄰近過濾曙博。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
//MIP貼圖縮小過濾拥刻,線性的MIP貼圖對最鄰近過濾,對當(dāng)前應(yīng)用的MIP貼圖的紋理圖像的周圍紋理圖像進(jìn)行加權(quán)平均運(yùn)算得到插值的紋理圖像然后對齊進(jìn)行線性過濾父泳。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
//得到本地硬件支持的最大各向異性濾波數(shù)量
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
//紋理縮小過濾般哼,各向異性過濾吴汪,因紋理在各個方向可能存在不同的顏色和空間排布,所以針對方向上不同的紋理狀態(tài)采取不同的采樣方式蒸眠,進(jìn)而達(dá)到更準(zhǔn)確的過濾
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
break;
case 7:
//關(guān)閉各向?yàn)V性過濾
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
break;
}
}
// 重新渲染
glutPostRedisplay();
}
void SetupRC()
{
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
//設(shè)置背景顏色
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
//初始化存儲著色器
shaderManager.InitializeStockShaders();
//設(shè)置紋理對象個數(shù)漾橙,與紋理對象句柄集合
glGenTextures(TEXTURE_COUNT, textures);
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
//根據(jù)紋理句柄得到紋理對象
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
//讀取圖片像素
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,
&iComponents, &eFormat);
//設(shè)置紋理參數(shù)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//紋理放大過濾,最鄰近過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);//紋理縮小過濾黔宛,最鄰近過濾
//紋理環(huán)繞近刘,這里是沿著S,T坐標(biāo)進(jìn)行紋理剪裁環(huán)繞,并對其坐標(biāo)內(nèi)的紋理單元采用上一個紋理單元的最后一行或者一列進(jìn)行采樣然后拼接臀晃,這樣避免了紋理剪裁環(huán)繞模式因?qū)R紋理圖像進(jìn)行切割成小方塊后而應(yīng)用到幾何體上出現(xiàn)縫隙的問題觉渴。
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//對像素解包,這里用的tag圖本身是沒有緊密包裝的徽惋,緊密包裝是按照本地硬件尋址大小而對像素地址進(jìn)行字節(jié)對齊的一種方式案淋,方便一次取更多的數(shù)據(jù)提高讀寫效率
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
//加載紋理到內(nèi)存,紋理單元為2D圖像模式险绘,設(shè)置使用基層紋理踢京,設(shè)置壓縮方式為對RGB進(jìn)行壓縮方式,也可設(shè)置為原圖的壓縮方式通過iComponents指針找到宦棺,設(shè)置紋理的寬和高瓣距,設(shè)置紋理數(shù)據(jù)的類型為無符號字節(jié),像素數(shù)據(jù)的指針代咸,因無法將基本顏色值寫入到內(nèi)存蹈丸,這里通過像素的指針來指向基本顏色值
glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
//為所有紋理生成2D紋理單元的MIP貼圖層
glGenerateMipmap(GL_TEXTURE_2D);
// 釋放圖片像素
free(pBytes);
}
GLfloat z;
//批量管理頂點(diǎn)坐標(biāo),管理地板的頂點(diǎn)和紋理坐標(biāo)呐芥,指定頂點(diǎn)坐標(biāo)為三角帶鏈接逻杖,指定頂點(diǎn)坐標(biāo)和紋理對象數(shù)
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
//設(shè)置地板的頂點(diǎn)的坐標(biāo)和紋理坐標(biāo)與頂點(diǎn)坐標(biāo)的映射關(guān)系,并且存儲到floorBatch容器中進(jìn)行批量管理
floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z);
floorBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
floorBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
floorBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
floorBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
}
//批量管理結(jié)束標(biāo)識
floorBatch.End();
//批量管理頂點(diǎn)坐標(biāo)思瘟,管理天花板的頂點(diǎn)和紋理坐標(biāo)荸百,指定頂點(diǎn)坐標(biāo)為三角帶鏈接,指定頂點(diǎn)坐標(biāo)和紋理對象數(shù)
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
//設(shè)置天花板的頂點(diǎn)的坐標(biāo)和紋理坐標(biāo)與頂點(diǎn)坐標(biāo)的映射關(guān)系滨攻,并且存儲到ceilingBatch容器中進(jìn)行批量管理
ceilingBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
ceilingBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
ceilingBatch.Vertex3f(-10.0f, 10.0f, z);
ceilingBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
ceilingBatch.Vertex3f(10.0f, 10.0f, z);
}
//批量管理結(jié)束標(biāo)識
ceilingBatch.End();
//批量管理頂點(diǎn)坐標(biāo)够话,管理左邊的墻的頂點(diǎn)和紋理坐標(biāo),指定頂點(diǎn)坐標(biāo)為三角帶鏈接铡买,指定頂點(diǎn)坐標(biāo)和紋理對象數(shù)
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
//設(shè)置地板的頂點(diǎn)的坐標(biāo)和紋理坐標(biāo)與頂點(diǎn)坐標(biāo)的映射關(guān)系更鲁,并且存儲到leftWallBatch容器中進(jìn)行批量管理
leftWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
leftWallBatch.Vertex3f(-10.0f, -10.0f, z - 10.0f);
leftWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
leftWallBatch.Vertex3f(-10.0f, 10.0f, z - 10.0f);
}
//批量管理結(jié)束標(biāo)識
leftWallBatch.End();
//批量管理頂點(diǎn)坐標(biāo),管理右邊的墻的頂點(diǎn)和紋理坐標(biāo)奇钞,指定頂點(diǎn)坐標(biāo)為三角帶鏈接澡为,指定頂點(diǎn)坐標(biāo)和紋理對象數(shù)
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
//設(shè)置地板的頂點(diǎn)的坐標(biāo)和紋理坐標(biāo)與頂點(diǎn)坐標(biāo)的映射關(guān)系,并且存儲到rightWallBatch容器中進(jìn)行批量管理
rightWallBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
rightWallBatch.Vertex3f(10.0f, -10.0f, z - 10.0f);
rightWallBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
rightWallBatch.Vertex3f(10.0f, 10.0f, z - 10.0f);
}
//批量管理結(jié)束標(biāo)識
rightWallBatch.End();
}
void ShutdownRC(void)
{
//刪除紋理對象
glDeleteTextures(TEXTURE_COUNT, textures);
}
void SpecialKeys(int key, int x, int y)
{
// 通過前后建控制模型視圖的z坐標(biāo)
if(key == GLUT_KEY_UP)
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
//重新渲染
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
GLfloat fAspect;
if(h == 0)
h = 1;
//設(shè)置視口大小
glViewport(0, 0, w, h);
fAspect = (GLfloat)w/(GLfloat)h;
//進(jìn)行透視投影
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
//將投影矩陣載入投影堆棧中
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//設(shè)置管線管理模型視圖矩陣堆棧和投影矩陣堆棧
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void RenderScene(void)
{
//清理顏色緩沖區(qū)
glClear(GL_COLOR_BUFFER_BIT);
//推入一個單位矩陣到模型視圖矩陣堆棧當(dāng)中
modelViewMatrix.PushMatrix();
//模型視圖矩陣變換景埃,沿z軸移動變換
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
//使用交換存儲著色器可以將紋理坐標(biāo)應(yīng)用到幾何體上媒至,這里transformPipeline.GetModelViewProjectionMatrix()將投影矩陣轉(zhuǎn)換成單元的模型視圖投影矩陣顶别,設(shè)置一重紋理貼圖
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//得到地板紋理對象
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_FLOOR]);
//渲染地板
floorBatch.Draw();
//得到天花板紋理對象
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_CEILING]);
//渲染天花板
ceilingBatch.Draw();
//得到墻紋理對象
glBindTexture(GL_TEXTURE_2D, textures[TEXTURE_BRICK]);
//渲染左邊的墻
leftWallBatch.Draw();
//渲染右邊的墻
rightWallBatch.Draw();
//彈出模型視圖矩陣,每次重新渲染要把結(jié)果給到ChangeSize中的管線進(jìn)行管理
modelViewMatrix.PopMatrix();
// 雙緩沖渲染時交換前后臺渲染數(shù)據(jù)
glutSwapBuffers();
}
int main(int argc, char *argv[])
{
gltSetWorkingDirectory(argv[0]);
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Anisotropic Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
//創(chuàng)建菜單管理
glutCreateMenu(ProcessMenu);
//創(chuàng)建各個菜單管理的操作
glutAddMenuEntry("GL_NEAREST",0);
glutAddMenuEntry("GL_LINEAR",1);
glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST",2);
glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 3);
glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 4);
glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
glutAddMenuEntry("Anisotropic Filter", 6);
glutAddMenuEntry("Anisotropic Off", 7);
//菜單管理的事件觸發(fā)方式
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
SetupRC();
glutMainLoop();
//刪除紋理對象
ShutdownRC();
return 0;
}