Mip贴图
Mip贴图是一种功能强大的纹理技巧,它不仅可以提高渲染性能,而且可以改善场景的显示质量。
它使用标准纹理贴图处理两个常见的问题,从而实现上述目标。
第一个问题是一种称为闪烁(Scintillation,即锯齿假影)的效果。
当屏幕上被渲染物体的表面与它所应用的纹理图像相比显得非常小时,就会出现这种效果。闪烁可以被看成是某种类型的闪光,当纹理图像的采样区域的移动幅度与它在屏幕上的大小相比显得不成比例时,就会发生这种现象。当照相机或物体处于运动状态时,我们很容易看到闪烁的负面效果。
第二个问题更多地和性能有关,但它的原因和闪烁相同。
也就是说,问题的根源在于它必须加载大量的纹理内存并对它们进行过滤处理,但屏幕上实际显示的只是很少的一部分片段。纹理越大,这个问题所造成的性能影响也就越为明显。
Mip贴图纹理由一系列纹理图像组成,每个图像大小在每个轴的方向上都缩小一半,或者说是原来图像像素总数的四分之一。

如图所示显示了这些场景。Mip贴图层并不一定是正方形的,但每个图像的大小都依次减半,直到最后一个图像的大小是1×1的纹理单元为止。
当其中一个维度的大小到达1时,接下来的减半处理就只发生在其他维度上了。使用一组正方形(即各个维度的大小相等)的Mip贴图所要求的内存比不使用Mip贴图要多出三分之一。
加载
Mip贴图层是通过glTexImage函数加载的。
现在轮到level参数发挥它的作用了,因为它指定了图像数据用于哪个Mip层。
第一层是0,接着是1、2,然后依此类推。如果Mip贴图未被使用,那么就只有第0层会被加载。
在默认情况下,为了使用Mip贴图,所有的Mip层都必须加载。
但是,我们可以用GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAX_LEVEL纹理参数特别设置需要使用的基层和最大层。
例如,如果想指定只加载从第0层至第4层,可以像下面这样调用glTexParameteri函数两次。
cpp
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 4);
贴图过滤
Mip贴图在两个基本的纹理过滤模式GL_NEAREST和GL_LINEAR上添加了
一个新的变化,这是通过向Mip贴图过滤模式提供4种不同变化实现
的。下表列出了这些变化。

仅仅使用glTexImage函数加载Mip层并不能启用Mip贴图功能。
如果纹理过滤设置为GL_LINEAR或GL_NEAREST,那么就只有纹理贴图基层会被使用,其他所有加载的Mip层都将被忽略。
我们必须指定其中一个Mip贴图过滤器,这样才能使用所有已加载的Mip层。
这个常量具有
GL_FILTER_MIPMAP_SELECTOR的形式,其中FILTER指定了被选择的Mip层将要使用的纹理过滤器,SELECTOR则指定了如何选择Mip层。例如,GL_NEAREST选择最接近匹配的Mip层。
如果选择GL_LINEAR,它就会在两个最邻近的Mip层之间执行线性插值,其结果又由被选择的纹理过滤器进行过滤。
生成Mip层
和仅仅加载基本的纹理图像相比,Mip贴图所需要的纹理内存要多出大约三分之一,它还要求所有更小的基本纹理图像都可以进行加载。
有时候,这可能会带来不便,因为程序员或软件的终端用户并不一定需要这些更小的图像。
对纹理的Mip层进行预先计算会得到最好的结果,同时让为我们生成纹理是非常方便的,也是比较普遍的方式。
一旦我们通过 glGenerateMipmap函数加载了第0层,就可以为纹理生成所有的Mip层了。
cpp
void glGenerateMipmap(GLenum target);
target参数可以是:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D、
GL_TEXTURE_ CUBE_MAP、GL_TEXTURE_1D_ARRAY或GL_TEXTURE_2D_ARRAY。
示例
隧道示例程序在启动时加载3个纹理,然后在它们之间进行切换,来对一个隧道进行渲染。
代码如下:
cpp
#include "tunnel.h"
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#ifndef FREEGLUT_STATIC
#define FREEGLUT_STATIC
#endif
#include <GL/glut.h>
#define TEXTURE_BRICK 0
#define TEXTURE_FLOOR 1
#define TEXTURE_CEILING 2
#define TEXTURE_COUNT 3
namespace tunnel {
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
GLGeometryTransform transformPipeline;
GLBatch floorBatch;
GLBatch ceilingBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLfloat viewZ = -65.0f;
GLuint textures[TEXTURE_COUNT];
const char* szTextureFiles[TEXTURE_COUNT] = {
"./src/chapter_5/src/Tunnel/brick.tga"
, "./src/chapter_5/src/Tunnel/floor.tga"
, "./src/chapter_5/src/Tunnel/ceiling.tga" };
void ProcessMenu(int value)
{
GLint iLoop;
for (iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
// // 设置纹理过滤方式(缩小时)
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_NEAREST_MIPMAP_LINEAR);
break;
case 4:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
}
}
glutPostRedisplay();
}
void SetupRC()
{
GLbyte* pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
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_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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);
// 数据传至GPU
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
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();
}
void ShutdownRC()
{
glDeleteTextures(TEXTURE_COUNT, textures);
}
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP)
viewZ += 0.5f;
if (key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
// Refresh the Window
glutPostRedisplay();
}
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.0f, 120.0f);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT);
modelViewMatrix.PushMatrix();
{
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
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();
}
modelViewMatrix.PopMatrix();
glutSwapBuffers();
}
int tunnel_test(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
// Standard initialization stuff
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
// Add menu entries to change filter
glutCreateMenu(ProcessMenu);
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);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
// Startup, loop, shutdown
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
}
效果预览:

各项异性过滤
各向异性纹理过滤(Anisotropic texture filtering)并不是OpenGL核心规范的一部分,但它是一种得到广泛支持的扩展,可以极大地提高纹理过滤操作的质量。
我们在前面内容中讲述了纹理贴图,并学习了两种基本的纹理过滤: 最邻近过滤(GL_NEAREST)和线性过滤(GL_LINEAR)。
当一个纹理贴图被过滤时,OpenGL使用纹理坐标来判断一个特定的几何片段将落在纹理贴图的什么地方。然后,紧邻这个位置的纹理单元使用GL_NEAREST或GL_LINEAR过滤操作进行采样。
当几何图形进行纹理贴图时,如果它的观察方向与观察点恰好垂直,那么这个过程是相当完美的。如图所示。

但是,当我们从一个角度倾斜地观察这个几何图形时,对周围纹理单元进行常规采样将导致一些纹理信息的丢失(看上去显得模糊)。
更为逼真和准确的采样应该是沿着包含纹理的平面方向进行延伸,它的结果如下图所示。

如果我们在进行纹理过滤时考虑了观察角度,那么这种过滤方法就称为各向异性过滤。
我们还可以把各向异性过滤应用到所有的基本纹理过滤或Mip贴图纹理过滤模型中。
应用各向异性过滤需要3个步骤。首先,必须确认这种扩展是得到支持的。
为此,可以查询扩展字符串GL_EXT_texture_filter_anisotropic。
cpp
if(gltIsExtSupported("GL_EXT_texture_filter_anisotropic"))
//设置标志,表示这个扩展受到支持
在确认这个扩展得到支持之后,就可以查询得到支持的各向异性过滤的最大数量。
为此,可以调用glGetFloatv函数,并以GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT为参数。
cpp
GLfloat fLargest;
......
......
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
各向异性过滤所应用的数量越大,沿最大变化方向(沿最强的观察点)所采样的纹理单元就越多。
值1.0表示常规的纹理过滤(称为各向同性过滤)。
最后,我们可以用glTexParameter函数以及GL_TEXTURE_MAX_ANISOTROPY_EXT常量设置想要应用的各向异性过滤的数量。
cpp
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT,
fLargest);
各向异性过滤是以每个纹理对象为基础进行应用的,就像标准过滤参数一样。
示例
在上面的隧道示例中添加各向异性过滤。
代码如下:
cpp
#include "tunnel2.h"
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLFrame.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#ifndef FREEGLUT_STATIC
#define FREEGLUT_STATIC
#endif
#include <GL/glut.h>
#define TEXTURE_BRICK 0
#define TEXTURE_FLOOR 1
#define TEXTURE_CEILING 2
#define TEXTURE_COUNT 3
namespace tunnel2 {
GLShaderManager shaderManager;
GLMatrixStack modelViewMatrix;
GLMatrixStack projectionMatrix;
GLFrustum viewFrustum;
GLGeometryTransform transformPipeline;
GLBatch floorBatch;
GLBatch ceilingBatch;
GLBatch leftWallBatch;
GLBatch rightWallBatch;
GLfloat viewZ = -65.0f;
GLuint textures[TEXTURE_COUNT];
const char* szTextureFiles[TEXTURE_COUNT] = {
"./src/chapter_5/src/Tunnel/brick.tga"
, "./src/chapter_5/src/Tunnel/floor.tga"
, "./src/chapter_5/src/Tunnel/ceiling.tga" };
void ProcessMenu(int value)
{
GLfloat fLargest;
GLint iLoop;
for (iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
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_NEAREST_MIPMAP_LINEAR);
break;
case 4:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
break;
case 7:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
break;
}
}
// Trigger Redraw
glutPostRedisplay();
}
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();
// Load textures
glGenTextures(TEXTURE_COUNT, textures);
for (iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
// Bind to next texture object
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
// Load texture, set filter and wrap modes
pBytes = gltReadTGABits(szTextureFiles[iLoop], &iWidth, &iHeight,
&iComponents, &eFormat);
// Load texture, set filter and wrap modes
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);
glGenerateMipmap(GL_TEXTURE_2D);
// Don't need original texture data any more
free(pBytes);
}
// Build Geometry
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();
}
void ShutdownRC()
{
glDeleteTextures(TEXTURE_COUNT, textures);
}
void SpecialKeys(int key, int x, int y)
{
if (key == GLUT_KEY_UP)
viewZ += 0.5f;
if (key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
// Refresh the Window
glutPostRedisplay();
}
void ChangeSize(int w, int h)
{
GLfloat fAspect;
// Prevent a divide by zero
if (h == 0)
h = 1;
// Set Viewport to window dimensions
glViewport(0, 0, w, h);
fAspect = (GLfloat)w / (GLfloat)h;
// Produce the perspective projection
viewFrustum.SetPerspective(80.0f, fAspect, 1.0, 120.0);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT);
modelViewMatrix.PushMatrix();
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
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();
modelViewMatrix.PopMatrix();
// Buffer swap
glutSwapBuffers();
}
int tunnel2_test(int argc, char* argv[])
{
gltSetWorkingDirectory(argv[0]);
// Standard initialization stuff
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Anisotropic Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
// Add menu entries to change filter
glutCreateMenu(ProcessMenu);
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);
glutAttachMenu(GLUT_RIGHT_BUTTON);
GLenum err = glewInit();
if (GLEW_OK != err) {
fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
return 1;
}
// Startup, loop, shutdown
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}
}
效果预览:
