OpenGL纹理案例-隧道
之前我写过一些关于纹理的一些基本API和API的方法参数的解释,现在我们来弄一个隧道案例。
隧道的最终效果如下图所示:
QQ20200721-145258-HD.gif
在介绍项目时,首先了解几个重要的函数:
1、SetupRC:这个函数的主要功能是设置图形坐标,设置纹理参数,读取纹理图片,加载纹理,载入纹理,最后生成纹理。
2、RenderScene:主要用于绘制场景,绑定纹理并绘制批次类容器。
3、ChangeSize:用于加载窗口,显示画面
4、ProcessMenu:用于右键显示菜单选项
5、SpecialKeys:移动键位,作出响应。
下面附上代码:
#include "GLTools.h"
#include "GLShaderManager.h"
#include "GLFrustum.h"
#include "GLBatch.h"
#include "GLFrame.h"
#include "GLMatrixStack.h"
#include "GLGeometryTransform.h"
#ifdef __APPLE__
#include <glut/glut.h>
#else
#define FREEGLUT_STATIC
#include <GL/glut.h>
#endif
GLShaderManager shaderManager; //着色器管理器
GLMatrixStack modelViewMatrix; //模型视图矩阵
GLMatrixStack projectionMatrix; //投影矩阵
GLFrustum viewFrustum; //视景体
GLGeometryTransform transformPipeline; //几何变换管线
//4个批次容器类
GLBatch floorBatch;//地面
GLBatch ceilingBatch;//天花板
GLBatch leftWallBatch;//左墙面
GLBatch rightWallBatch;//右墙面
//深度初始值,-65。
GLfloat viewZ = -65.0f;
// 纹理标识符号
#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" };
在上面的一段代码中,主要是设置着色器,模型矩阵,批次类容器等,其中4个批次类容器用于存储墙面。
下面附上一些常规的函数代码
void ProcessMenu(int value)
{
GLint iLoop;
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
switch(value)
{
case 0:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_NEAREST(最邻近过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
break;
case 1:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_LINEAR(线性过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
break;
case 2:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_NEAREST_MIPMAP_NEAREST(选择最邻近的Mip层,并执行最邻近过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
break;
case 3:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_NEAREST_MIPMAP_LINEAR(在Mip层之间执行线性插补,并执行最邻近过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
break;
case 4:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_NEAREST_MIPMAP_LINEAR(选择最邻近Mip层,并执行线性过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
break;
case 5:
//GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_LINEAR_MIPMAP_LINEAR(在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;
}
}
//触发重画
glutPostRedisplay();
}
//关闭渲染环境
void ShutdownRC(void)
{
//删除纹理
glDeleteTextures(TEXTURE_COUNT, textures);
}
//前后移动视口来对方向键作出响应
void SpecialKeys(int key, int x, int y)
{
if(key == GLUT_KEY_UP)
//移动的是深度值,Z
viewZ += 0.5f;
if(key == GLUT_KEY_DOWN)
viewZ -= 0.5f;
//更新窗口,即可回调到RenderScene函数里
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);
}
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;
}
上面4个函数主要是设置窗口大小,上下键位移动,关闭渲染和右键菜单的选项。
菜单的选项其实也就是设置纹理参数。
下面是两个重要的函数的代码:
SetupRC
//1.黑色的背景
glClearColor(0.0f, 0.0f, 0.0f,1.0f);
//2.初始化shaderManager
shaderManager.InitializeStockShaders();
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
//3.生成纹理标记
glGenTextures(TEXTURE_COUNT, textures);
//4. 循环设置纹理数组的纹理参数
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
pBytes = gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight,
&iComponents, &eFormat);
//加载纹理、设置过滤器和包装模式
//GL_TEXTURE_MAG_FILTER(放大过滤器,GL_NEAREST(最邻近过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
//GL_TEXTURE_MIN_FILTER(缩小过滤器),GL_NEAREST(最邻近过滤)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//GL_TEXTURE_WRAP_S(s轴环绕),GL_CLAMP_TO_EDGE(环绕模式强制对范围之外的纹理坐标沿着合法的纹理单元的最后一行或一列进行采样)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
//GL_TEXTURE_WRAP_T(t轴环绕),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);
//释放原始纹理数据,不在需要纹理原始数据了
free(pBytes);
}
//5. 设置几何图形顶点/纹理坐标(上.下.左.右)
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();
上面的函数设置了纹理的相关设置,还有4面墙的坐标表示,这个坐标以屏幕的横竖代表x轴和y轴,面向屏幕的方向代表z轴。
RenderScene函数代码
void RenderScene(void)
{
//1.用当前清除色,清除窗口
glClear(GL_COLOR_BUFFER_BIT);
//2.模型视图压栈
modelViewMatrix.PushMatrix();
//Z轴平移viewZ 距离
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
//3.纹理替换矩阵着色器
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//4.绑定纹理
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();
//5.pop
modelViewMatrix.PopMatrix();
//6.缓存区交换
glutSwapBuffers();
}
上面的函数主要是渲染场景,绘制墙面的作用,利用纹理着色器去绑定纹理和绘制纹理图像。
在上面的所有代码当中,我们并没有去设置纹理坐标,那系统是如何绘制纹理的呢?
其实是纹理替换矩阵着色器去帮我们去做的,这边并不需要我们自己去设置。
整个流程代码并不多。
下面在讲述一下纹理绘制的步骤:
1、生成纹理标记glGenTextures()
2、绑定纹理glBindTexture()
3、读取纹理图片gltReadTGABits()
4、加载纹理glTexParameteri()
5、载入纹理glTexImage2D()
最后在渲染时,绑定纹理并绘制就完成了。
网友评论