本案例主要目的多个纹理如何使用,加深对纹理的使用的理解。
整体的案例效果如图所示
下面接着说说隧道的绘制过程,整体的流程图如下:
其中main、ChangeSize、ShutdownRC就不多作说明,
- SetupRC:初始化背景色、着色器,生成纹理,并设置顶点及纹理坐标
- RenderScene:清理缓存、绑定纹理并绘制隧道
- SpecialKeys:根据上下键位,记录前后移动的深度值,并重新渲染
- ProcessMenu:根据选择的菜单选项,for循环更换所有纹理的过滤方式,并重新渲染
SetupRC函数
主要分为三部分,着重讲下生成纹理和设置隧道数据
- 初始化
- 生成纹理
- 设置隧道数据
函数流程图如下:
生成纹理
生成纹理有两部分
- 生成纹理对象:本案例中使用了3个纹理,需要传入一个纹理数组
//参数1:纹理个数
//参数2:纹理数组
glGenTextures(TEXTURE_COUNT, textures);
- 设置纹理参数
因为使用了多个纹理,每个纹理都需要设置这些参数,所以利用for循环(有几个纹理就循环几次)实现,主要有以下步骤
bind --> readTGA --> texParameter --> texImage --> generateMip --> free
- 绑定纹理对象 --
glBindTexture
- 读取TGA纹理文件 --
gltReadTGABits
- 设置纹理环绕方式和过滤方式 --
glTexParameteri
- 载入纹理 --
glTexImage2D
- 生成Mip贴图(当过滤方式与Mip无关时,可以设置,也可以不设置,如果使用与Mip相关的过滤方式,就必须设置) --
glGenerateMipmap
- 释放指向纹理对象空间的指针
- 绑定纹理对象 --
//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);
//生成mip贴图
glGenerateMipmap(GL_TEXTURE_2D);
free(pBytes);
}
设置隧道数据
其实这部分的重点主要是隧道顶点坐标纹理的映射,只要把这部分理清楚了,就非常简单了,隧道的顶点坐标如图所示:
与4个面相对应的纹理坐标如图所示
4个面均是由多个三角形使用三角形带的图元连接方式组成,所以需要使用for循环,每次设置4个点的顶点及纹理坐标,
- 设置纹理坐标使用批次类的
MultiTexCoord2f
,其中第一个参数是指图层,即纹理的level
,一般默认设置为0,后两个参数即为纹理的坐标s、t
- 设置顶点坐标使用批次类的
Vertex3f
,传入顶点的x、y、z
RenderScene函数
函数的流程图如下
- 清理颜色缓存
- push压栈:目的是为了不影响矩阵的初始状态,方便后续恢复矩阵
- 视图模型矩阵平移:因为初始化是是在原点,需要往-z轴平移一定的矩阵,方便观察,且与矩阵栈顶相乘,将其结果覆盖栈顶矩阵
- 使用纹理替换矩阵着色器:最后一个参数是指纹理的level,默认为0
shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE, transformPipeline.GetModelViewProjectionMatrix(), 0);
- 绑定纹理:由于地面、天花板、左右墙面分别使用不同的纹理,所以需要分别绑定并绘制
- pop出栈:恢复矩阵的初始状态
- 交换缓冲区
SpecialKeys函数
函数功能比较简单,主要就是根据上下键位,记录移动的深度值,并重新渲染
ProcessMenu函数
这个函数是鼠标右键的相应回调函数,根据鼠标右键的菜单选择,对所有纹理进行过滤方式的设置,这也是为什么使用for循环设置的原因,因为有多个纹理,需要设置多次,且在设置前,需要重新绑定纹理,主要流程图如下
其中主要是几种过滤方式的在项目中的显示效果
完整的代码见github - 08_OpenGL_隧道