当这个参数被设置为GL_TRUE时,所有调用glTexImage或者glTexSubImage都会自动更新纹理贴图(第0级)和所有更低层的纹理。通过使用硬件加速会比使用glu库中的gluBuildMipmap要快,但这个特性本来只是个扩展,在OpenGL1.4才被纳入OpenGL核心API的。
LOD(多细节层次)偏好当mipmapping被启用时,OpenGL会根据各个mip层的大小和几何图形在屏幕上的面积来决定哪一个mip层被选择。OpenGL会选择最合适的mip贴图层与屏幕上的纹理表示形式进行匹配。我们可以告诉OpenGL向后(选择更大的mip层)或向前(选择更小的mipmap层)来调整选择的标准。使用更小的mip层可以提高性能,选择更大的mip层可以锐化纹理映射的对象。这个偏好设置示例如下:
glTexEnvf(GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, –1.5);
上面会使的细节纹理层倾向于使用更高层的细节(更小的level层参数),从而使得纹理的外观更为锐利,代价是纹理处理的开销大一些。
纹理对象glTexImage, glTexSubImage和gluBuildMipmaps这些函数的调用消耗的时间特别多。这些函数大量的移动内存,有时需要重新调整数据的格式来适应一些内部的表示。在纹理之间切换或者重新加载不同的纹理图片会带来较大的性能开销。
为了减少这些开销,我们可以使用纹理对象。纹理对象允许你一次性加载多个纹理状态(包括纹理图像),然后在它们之间快速切换。纹理状态由当前绑定的纹理对象来维护。纹理的名称由unsigned int类型来标识。使用下面的函数来生成一定数量的纹理对象:
void glGenTextures(GLsizei n, GLuint *textures);
上面的函数调用指定了纹理对象的数量,和存储纹理对象名称的数组。我们可以通过纹理对象名称来操作纹理状态。绑定其中的一个纹理状态的函数调用如下:
void glBindTexture(GLenum target, GLuint texture);
target参数必须是GL_TEXTURE_1D,GL_TEXTURE_2D或者GL_TEXTURE_3D.texture是纹理名称指定要绑定的纹理对象。在此函数之后,纹理图像的加载和纹理参数的设置都只影响当前绑定的纹理对象。最后删除纹理对象的函数如下:
void glDeleteTextures(GLsizei n, GLuint *texture);
参数的意义与glGenTextures相同。不一定需要每次产生纹理对象使用后就删除所有的纹理对象。多次调用glGenTextures的开销较小,但多次调用glDeleteTextures会有导致一些延迟,原因是需要释放大量的能存空间。在不再需要此纹理对象时,要把该纹理对象删除,防止内存泄露。
判断纹理对象名称是否可用可以通过下面的函数调用来判断:
GLboolean glIsTexture(GLuint texture);
返回GL_TRUE代表可用,GL_FALSE代表不可用。
管理多个纹理一般而言,在程序初始化时加载多个纹理对象,然后在渲染期间不断地切换,在不再使用时删除纹理对象。下面是一个通道tunnel的例子,此例在启动时加载三个纹理对象,然后通过切换来绘制通道的砖墙,天花板和地板。此例中还演示了不同的mipmap模式,通过右键菜单来切换,通过上下箭头键来在通道中移动,通过左右键来旋转通道。
完整示例如下:
#include "gltools.h"
#include <stdio.h>
//定义宏常量
#define CEILING 0
#define BRICK 1
#define FLOOR 2
#define TEXTURENUM 3
//纹理图像的路径
const char* texFileName[] = {"..\\ceiling.tga","..\\brick.tga","..\\floor.tga"};
//纹理对象名称
static GLuint textureName[TEXTURENUM];
//旋转与移动
static GLfloat yRot = 0.0f;
static GLfloat zPos = 0.0f;
//切换不同的纹理模式
void ProcessMenu(int value)
{
switch (value)
{
case 0:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case 1:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case 2:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 4:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 5:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
if (gltIsExtSupported("GL_EXT_texture_filter_anisotropic"))
{
//开启各向异性过滤
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
printf("anisotropic:%f\n", fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
}
break;
default:
break;
}
glutPostRedisplay();
}
void SetupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
//开启深度测试,消除隐藏面,避免后画的墙画到前面来
glEnable(GL_DEPTH_TEST);
//纹理图像的信息
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
//设置纹理环境
glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV, GL_REPLACE);
//生成纹理对象
glGenTextures(TEXTURENUM, textureName);
for (int i = 0; i < TEXTURENUM; ++i)
{
void *pImage = gltLoadTGA(texFileName[i], &iWidth, &iHeight, &iComponents, &eFormat);
if (pImage)
{
//绑定纹理对象,生成mipmap
glBindTexture(GL_TEXTURE_2D, textureName[i]);
gluBuild2DMipmaps(GL_TEXTURE_2D, iComponents, iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
free(pImage);
}
glEnable(GL_TEXTURE_2D);
}
void ShutdownRC()
{
//最后删除纹理对象
glDeleteTextures(TEXTURENUM, textureName);
}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glPushMatrix();
//移动和旋转
glTranslatef(0.0f, 0.0f, zPos);
glRotatef(yRot, 0.0f, 1.0f, 0.0f);
for(GLfloat z = -60.0f; z <= 0.0f; z += 10.0f)
{
//绑定地板纹理绘制地板,注意glBeindTexture在glBegin和glEnd中是无效的
glBindTexture(GL_TEXTURE_2D, textureName[FLOOR]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-10.0f, -10.0f, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-10.0f, -10.0f, z + 10.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(10.0f, -10.0f, z + 10.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(10.0f, -10.0f, z);
glEnd();
//绑定天花板纹理
glBindTexture(GL_TEXTURE_2D, textureName[CEILING]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-10.0f, 10.0f, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-10.0f, 10.0f, z + 10.0f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(10.0f, 10.0f, z + 10.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(10.0f, 10.0f, z);
glEnd();
//绑定砖墙的纹理
glBindTexture(GL_TEXTURE_2D, textureName[BRICK]);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-10.0f, -10.0f, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-10.0f, 10.0f, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-10.0f, 10.0f, z + 10.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-10.0f, -10.0f, z + 10.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(10.0f, -10.0f, z);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(10.0f, 10.0f, z);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(10.0f, 10.0f, z + 10.0f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(10.0f, -10.0f, z + 10.0f);
glEnd();
}
//GLclampf prioritize[TEXTURENUM] = {0.0f, 0.0f, 1.0f};
//glPrioritizeTextures(TEXTURENUM, textureName, prioritize);
//GLboolean isResident[TEXTURENUM];
//if (glAreTexturesResident(TEXTURENUM, textureName, isResident))
//{
// printf("all texture is resident\n");
//}
//else
//{
// printf("texture resident is : %d %d %d", isResident[0], isResident[1], isResident[2]);
//}
glPopMatrix();
glutSwapBuffers();
}
void ChangeSize(GLsizei w, GLsizei h)
{
if (h == 1)
h = 0;
glViewport(0, 0, w, h);
GLfloat aspect = (GLfloat)w/(GLfloat)h;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(35.5, aspect, 1.0, 150.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glutPostRedisplay();
}
void SpecialKey(int value, int x, int y)
{
if (value == GLUT_KEY_LEFT)
{
yRot += 0.5f;
}
if (value == GLUT_KEY_RIGHT)
{
yRot -= 0.5f;
}
if (value == GLUT_KEY_UP)
{
zPos += 0.5f;
}
if (value == GLUT_KEY_DOWN)
{
zPos -= 0.5f;
}
if (yRot > 365.5f)
{
yRot = 0.0f;
}
glutPostRedisplay();
}
int main(int arg, char **argv)
{
glutInit(&arg, argv);
glutInitDisplayMode(GL_RGB | GL_DOUBLE | GL_DEPTH);
glutInitWindowSize(800, 600);
glutCreateWindow("tunel");
glutReshapeFunc(ChangeSize);
glutDisplayFunc(RenderScene);
glutSpecialFunc(SpecialKey);
glutCreateMenu(ProcessMenu);
glutAddMenuEntry("GL_NEAREST", 0);
glutAddMenuEntry("GL_LINEAR", 1);
glutAddMenuEntry("GL_NEAREST_MIPMAP_NEAREST", 2);
glutAddMenuEntry("GL_LINEAR_MIPMAP_NEAREST", 3);
glutAddMenuEntry("GL_NEAREST_MIPMAP_LINEAR", 4);
glutAddMenuEntry("GL_LINEAR_MIPMAP_LINEAR", 5);
glutAddMenuEntry("ANISOTROPIC", 6);
glutAttachMenu(GLUT_RIGHT_BUTTON);
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
GL_NEAREST效果图(纹理比较锐利):
GL_LINEAR_MIPMAP_LINEAR效果图(线性插值后的纹理过渡较平滑):