OpenGL隧道案例
2020-07-27 本文已影响0人
黑眼豆豆_
首先我们来看效果,通过点击鼠标的前后,我们就像走在一个一个隧道一样。
隧道效果.gif
顶点、纹理设置
我们可以将隧道拆分成3个部分:地面、天花板、墙面(左墙和右墙)。分别用4个三角形批次类来装载。
//4个批次容器类
GLBatch floorBatch;//地面
GLBatch ceilingBatch;//天花板
GLBatch leftWallBatch;//左墙面
GLBatch rightWallBatch;//右墙面
地面
如图所示,地面是由一个一个的三角形拼接而成
image.png-
拼接方式
我们使用GL_TRIANGLE_STRIP的方式来链接每个顶点,即从第三个点开始,每点与前面的两个点组合画一个三角形,即线性连续三角形串:图元类型参数。如果不清楚连接方式,可以看OpenGL渲染架构解析这篇文章。
//参数1:代表点的连接方式,GL_TRIANGLE_STRIP
//参数2:代表点的个数
//参数3:代表这个批次类中会使用1个批次类
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
-
绘制顶点坐标和纹理坐标
这步操作就是讲纹理跟顶点坐标进行一个映射操作
for(z = 60.0f; z >= 0.0f; z -=10.0f)
{
//绘制纹理坐标
//参数1:texture,纹理层次,对于存储着色器来进行渲染,一般为0。
//参数2:s,对应顶点坐标中的x坐标
//参数3:t,对应顶点坐标中的y坐标
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);
}
天花板
天花板的构造方式和地板一样。
image.png
- 拼接方式
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);
}
左墙
image.png- 拼接方式
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);
}
右墙
image.png- 拼接方式
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);
}
加载纹理
在这个项目中由于有3个纹理,所以我们用纹理数组来记录纹理。
#define TEXTURE_COUNT 3 //纹理个数
GLuint textures[TEXTURE_COUNT];//纹理标记数组
1. 生成纹理标记
/** 分配纹理对象 glGenTextures
参数1:纹理对象的数量
参数2:纹理对象标识数组
*/
glGenTextures(TEXTURE_COUNT, textures);
2. 绑定纹理
由于这个项目有多个纹理,所以我们用一个循环来进行绑定纹理以及设置纹理的参数。
// 循环设置纹理数组的纹理参数
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
/**绑定纹理对象 glBindTexture
参数1:纹理模式,GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_3D
参数2:需要绑定的纹理对象
*/
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
/**加载tga文件
参数1:纹理文件名称
参数2:文件宽度变量地址
参数3:文件高度变量地址
参数4:文件组件变量地址
参数5:文件格式变量地址
返回值:pBytes,指向图像数据的指针
*/
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
参数1:纹理维度,GL_TEXTURE_2D
参数2:mip贴图层次
参数3:纹理单元存储的颜色成分(从读取像素图中获得)
参数4:加载纹理宽度
参数5:加载纹理的高度
参数6:加载纹理的深度
参数7:像素数据的数据类型,GL_UNSIGNED_BYTE无符号整型
参数8:指向纹理图像数据的指针
*/
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
/**为纹理对象生成一组完整的mipmap glGenerateMipmap
参数1:纹理维度,GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_2D
*/
glGenerateMipmap(GL_TEXTURE_2D);
//释放原始纹理数据,不在需要纹理原始数据了
free(pBytes);
}
其中的iWidth
,iHeight
等参数我们提前设置。
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
那么这些参数从哪来的呢?就是由
gltReadTGABits(szTextureFiles[iLoop],&iWidth, &iHeight, &iComponents, &eFormat);
这句代码获取,gltReadTGABits
代表的是从.tga文件读取纹理,所以iWidth
,iHeight
等这些参数就是从纹理数据获取而来的,其实就是指针,指向了存放这些参数的地址。
SetupRC
//在这个函数里能够在渲染环境中进行任何需要的初始化,它这里的设置并初始化纹理对象
void 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
参数1:纹理对象的数量
参数2:纹理对象标识数组
*/
glGenTextures(TEXTURE_COUNT, textures);
//4. 循环设置纹理数组的纹理参数
for(iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++)
{
/**绑定纹理对象 glBindTexture
参数1:纹理模式,GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_3D
参数2:需要绑定的纹理对象
*/
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
/**加载tga文件
参数1:纹理文件名称
参数2:文件宽度变量地址
参数3:文件高度变量地址
参数4:文件组件变量地址
参数5:文件格式变量地址
返回值:pBytes,指向图像数据的指针
*/
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
参数1:纹理维度,GL_TEXTURE_2D
参数2:mip贴图层次
参数3:纹理单元存储的颜色成分(从读取像素图中获得)
参数4:加载纹理宽度
参数5:加载纹理的高度
参数6:加载纹理的深度
参数7:像素数据的数据类型,GL_UNSIGNED_BYTE无符号整型
参数8:指向纹理图像数据的指针
*/
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
/**为纹理对象生成一组完整的mipmap glGenerateMipmap
参数1:纹理维度,GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_2D
*/
glGenerateMipmap(GL_TEXTURE_2D);
//释放原始纹理数据,不在需要纹理原始数据了
free(pBytes);
}
//5. 设置几何图形顶点/纹理坐标(上.下.左.右)
GLfloat z;
/*
GLTools库中的容器类,GBatch,
void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
参数1:图元枚举值
参数2:顶点数
参数3:1组或者2组纹理坐标
*/
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
//参考PPT图6-10
//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();
//参考PPT图6-11
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();
//参考PPT图6-12
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();
//参考PPT图6-13
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
接下来进行纹理绘制
- 由于用到了纹理,我么我们用GLT_SHADER_TEXTURE_REPLACE(纹理替换着色器)这个着色器。
// 0代表从第一层绘制纹理,
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();
为什么在Draw()
之前还要进行绑定呢?这是因为我们要将纹理数据和三角形批次类进行绑定。
- 所以RenderScene代码如下:
//调用,绘制场景
void RenderScene(void)
{
//1.用当前清除色,清除窗口
glClear(GL_COLOR_BUFFER_BIT);
//2.模型视图压栈
modelViewMatrix.PushMatrix();
//Z轴平移viewZ 距离
modelViewMatrix.Translate(0.0f, 0.0f, viewZ);
//3.纹理替换矩阵着色器
/*
参数1:GLT_SHADER_TEXTURE_REPLACE(着色器标签)
参数2:模型视图投影矩阵
参数3:纹理层
*/
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
//4.绑定纹理
/*
参数1:纹理模式,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
参数2:需要绑定的纹理
*/
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();
}
移动观察者
//前后移动视口来对方向键作出响应
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();
}
这样我们就可以看到在隧道中前进和后退的效果。