效果图如下:
案例分析
设置纹理坐标使用批次类的MultiTexCoord2f,其中第一个参数是指图层,即纹理的level,一般默认设置为0,后两个参数即为纹理的坐标s、t;设置顶点坐标使用批次类的Vertex3f,传入顶点的x、y、z;
四个面均是由多个三角形使用三角形带的图元连接方式组成,所以只需要使用for循环,每次设置四个点的顶点及纹理坐标即可完成绘制
//Z表示深度,隧道的深度
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
//如果想隧道越深,把z的值改大就可以了
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();
可以从上图看到,这是一个梯形的长方形,由很多个三角形组成的,从上面代码可以看出GL_TRIANGLE_STRIP 三角形扇,它会连成一个三角形,所以两个三角形只需要四个坐标就可以了,
分析坐标,z代表着深度,越靠近里面的三角形就越小,就不断地(z-10)一路延伸过去,通过for循环不断绘制小长方形形成一个阶梯的地面。
案例代码
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLFrame.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#ifdef __APPLE__
#include
#else
#define FREEGLUT_STATIC
#include
#endif
GLShaderManager shaderManager; //着色器管理器
GLMatrixStack modelViewMatrix; //模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrustum viewFrustum; //视景体
GLGeometryTransform transformPipeline; //几何变换管线
// 批次容器类
GLBatch floorBatch; // 地面
GLBatch ceilingBatch; // 天花板
GLBatch leftWallBatch; // 左墙面
GLBatch rightWallBatch; // 右墙面
// 深度初始值
GLfloat viewZ = -65.f;
// 纹理标识符号
#define TEXTURE_BRICK 0 // 墙面
#define TEXTURE_FLOOR 1 // 地板
#define TEXTURE_CEILING 2 // 天花板
#define TEXTURE_COUNT 3 // 纹理个数
GLuint textures[TEXTURE_COUNT]; // 纹理标记数组
// 文件tag名称数组
const char *szTextureFiles[TEXTURE_COUNT] = {"brick.tga", "floor.tga", "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:
// 缩小过滤器,选择最邻近的Mip层,并执行最邻近过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
// 缩小过滤器,在Mip层之间执行线性插补,并执行最邻近过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 4:
// 缩小过滤器,选择最邻近Mip层,并执行线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
// 缩小过滤器,在Mip层之间执行线性插补,并执行线性过滤,又称为三线性过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
break;
case 6:
// 设置各向异性过滤
GLfloat fLargest;
// 获取各向异性过滤的最大数量
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
// 设置纹理参数(各向异性采样)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
break;
case 7:
// 设置各向同性过滤,数量为1.0表示(各向同性采样)
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);
break;
default:
break;
}
}
// 重绘消息
glutPostRedisplay();
}
// 在这个函数里能够在渲染环境中进行任何需要的初始化,它这里的设置并初始化纹理对象
void SetupRC() {
// 设置背景
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
// 初始化shaderManager
shaderManager.InitializeStockShaders();
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
// 生成纹理标记
glGenTextures(TEXTURE_COUNT, textures);
// 设置纹理参数
for (iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++) {
// 绑定纹理对象
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
// 加载tga文件
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);
// 为纹理对象生成一组完整的mipmap glGenerateMipmap
glGenerateMipmap(GL_TEXTURE_2D);
// 释放纹理数据
free(pBytes);
}
// 设置几何图形顶点和纹理坐标
GLfloat z;
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
//Z表示深度,隧道的深度
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 (GLUT_KEY_UP == key) {
viewZ += 0.5f;
}
if (GLUT_KEY_DOWN == key) {
viewZ -= 0.5f;
}
// 更新窗口
glutPostRedisplay();
}
// 改变视景体和视口,在改变窗口大小或初始化窗口调用
void ChangeSize(int w, int h) {
//1.防止对0进行除法操作
if(h == 0)
h = 1;
//2.将视口设置大小
glViewport(0, 0, w, h);
GLfloat fAspect = (GLfloat)w/(GLfloat)h;
//3.生成透视投影
viewFrustum.SetPerspective(80.0f,fAspect,1.0,120.0);
projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
}
// 调用,绘制场景
void RenderScene(void) {
// 清除缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// 压栈
modelViewMatrix.PushMatrix();
// Z轴平移
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 main(int argc, char *argv[]) {
gltSetWorkingDirectory(argv[0]);
// 标准初始化
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutInitWindowSize(800, 600);
glutCreateWindow("Tunnel");
glutReshapeFunc(ChangeSize);
glutSpecialFunc(SpecialKeys);
glutDisplayFunc(RenderScene);
// 添加菜单入口,改变过滤器
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;
}
// 启动循环,关闭纹理
SetupRC();
glutMainLoop();
ShutdownRC();
return 0;
}