OpenGl Mip貼圖
阿新 • • 發佈:2019-02-02
Mip貼圖可以用來提高遊戲的效能,主要原理是把一張紋理圖片按照一定側尺寸縮放,生成一些列的圖片,按照圖片離照相機的距離的遠近,再決定使用哪個圖片
比如,離照相機遠的使用小的 。
決定怎樣使用mip貼圖,取決於當前設定的mip貼圖的過濾模式,mip貼圖的過濾模式遵守GL_FILTER_MIPMAP_SELECTOR的形式,FILTER指定了被選擇的Mip層將要使用的紋理過濾器,SELECTOR指定了如何選擇Mip層,列如GL_LINEAR_MIPMAP_LINEAR表示,從最鄰近的Mip層之間執行線性插值得到圖片後,再執行線性過濾。
我們這一節要實現的效果如下圖:
我們可以看到遠處的磚塊逐漸模糊,我們可以按鍵盤的方向鍵來進行行走
下面是程式碼實現
首先是要包含的標頭檔案和全域性變數
#include <GLTools.h> #include <GLShaderManager.h> #include <GLFrustum.h> #include <GLBatch.h> #include <GLFrame.h> #include <GLMatrixStack.h> #include <GLGeometryTransform.h> #define FREEGLUT_STATIC #include <GL/glut.h> 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" };
下面是主函式main
int main(int argc, char *argv[]) { //設定工作路徑 gltSetWorkingDirectory(argv[0]); //初始化glut glutInit(&argc, argv); //申請一個帶有雙緩衝區,顏色緩衝區的視窗 glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); //視窗大小 glutInitWindowSize(800, 600); //視窗名字 glutCreateWindow("Mip"); //視窗大小改變時的回撥函式 glutReshapeFunc(ChangeSize); //鍵盤按鍵響應函式 glutSpecialFunc(SpecialKeys); //渲染的回撥函式 glutDisplayFunc(RenderScene); GLenum err = glewInit(); if (GLEW_OK != err) { fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err)); return 1; } //初始化函式 SetupRC(); //主函式迴圈 glutMainLoop(); //迴圈結束後釋放記憶體 ShutdownRC(); return 0; }
下面是視窗大小改變時的回撥函式ChangeSize
void ChangeSize(int w, int h)
{
GLfloat fAspect;
if(h == 0)
h = 1;
//設定視口大小
glViewport(0, 0, w, h);
fAspect = (GLfloat)w/(GLfloat)h;
//設定視口變換矩陣
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
//設定模型變換矩陣
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
//將檢視變換矩陣 和 模型 變換矩陣統一管理起來
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
下面是響應鍵盤方向鍵的函式SpecialKeys
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
//重新整理視窗
glutPostRedisplay();
}
下面是初始化函式SetupRC
void SetupRC()
{
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
// Black background
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
shaderManager.InitializeStockShaders();
// 生成紋理物件
glGenTextures(TEXTURE_COUNT, textures);
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
//繫結紋理物件
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
//載入紋理資料
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,
&iComponents, &eFormat);
//設定紋理過濾模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//設定紋理環繞模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//生成紋理圖片
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
//生成Mip層
glGenerateMipmap(GL_TEXTURE_2D);
//刪除原紋理資料記憶體
free(pBytes);
}
// 地面資料
GLfloat z;
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
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);
}
floorBatch.End();
//天花板資料
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
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);
}
ceilingBatch.End();
//左牆面資料
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
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);
}
leftWallBatch.End();
//右牆面資料
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
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);
}
rightWallBatch.End();
}
下面是渲染函式RenderScene
void RenderScene(void)
{
// 清除後臺緩衝區
glClear(GL_COLOR_BUFFER_BIT);
//設定檢視變換矩陣
modelViewMatrix.PushMatrix();
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//設定mip貼圖過濾模式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
//繪製地板
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();
modelViewMatrix.PopMatrix();
//交換緩衝區
glutSwapBuffers();
}
下面是程式退出時的清理函式ShutdownRC
void ShutdownRC(void)
{
//刪除紋理物件
glDeleteTextures(TEXTURE_COUNT, textures);
}
各向異性過濾
如果在進行紋理過濾時考慮了觀察者的角度,那麼這種方法就成為各向異性過濾
假如說你在觀察一個三角形,三角形上有一點A,當從正面觀察時,和從側面觀察時,A點的紋理資料的會有所不同,因為在生成A點的紋理資料時,會根據觀察的
方向來採取周圍紋理資料進行運算
在使用各向異性過濾模式之前,我們需要測試硬體支不支援這種過濾模式,我們可以使用函式gltIsExtSupported("GL_EXT_texture_filter_anisotropic")在檢測,返回1標示支援,0表示不支援,
我們使用函式 void GLAPIENTRY glGetFloatv (GLenum pname, GLfloat *params) 來得到最大支援的各向異性過濾的最大數量
我們使用函式 GLAPIENTRY glTexParameterf (GLenum target, GLenum pname, GLfloat param) 來啟用各向異性過濾模式
我們可以把渲染函式RenderScene中的程式碼
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
換成如下的程式碼,來使用各向異性過濾模式
if (gltIsExtSupported("GL_EXT_texture_filter_anisotropic"))
{
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
}
下圖是使用了各向異性過濾後的效果,比之前的清晰多了,但同時也有效能上的消耗