1. 程式人生 > >OpenGl Mip貼圖

OpenGl Mip貼圖

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);
}

下圖是使用了各向異性過濾後的效果,比之前的清晰多了,但同時也有效能上的消耗