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点的纹理数据时,会根据观察的
方向来采取周围纹理数据进行运算
返回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);
}
下图是使用了各向异性过滤后的效果,比之前的清晰多了,但同时也有性能上的消耗