OpenGL纹理MipMap和隧道案例
2020-07-22 本文已影响0人
过气的程序员DZ
开场白
本文介绍一下纹理的mip映射以及用一个隧道案例来展示不同mip过滤模式的效果。
MipMap简介一下
MipMap是一个纹理渲染技术。它能够提高渲染性能并且提升场景的视觉质量。
纹理贴图会有以下两个常见问题:
- 闪烁:当纹理size远大于被渲染物体表面时,会出现闪烁现象。尤其是移动观察者远离物体的时候,负面效果更明显
- 性能影响:当加载大量纹理时,并且对这些纹理进行缩小操作(观察者远离物体)时,会对性能要求比较高。
MipMap可以很好的解决上述的两个问题。使用此技术,OpenGL会生成大小不等的多个纹理并且OpenGL会自动根据不同状态选择不同的纹理进行渲染。多个纹理也就会增加存储空间,也就是用空间换时间的一种方案。
设置方法
调用纹理参数函数,例如:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_LINEAR);
- 参数1:指定纹理模式
- 参数2:告诉OpenGL要设定的什么,这里设定的是纹理缩小过滤方式。
- 参数3:mip之间使用线性过滤,整体使用邻近过滤。
mip的纹理过滤方式
过滤方式如图:
纹理过滤方式
- 前两个GL_NEAREST和GL_LINEAR方式,只会加载基础mip层。
- 后四个是GL_X1_MIPMAP_X2形式。X2指定的是mip之间的过滤方式,X1指定的是整体的过滤方式。可以理解为是纹理的内部(X2)和外部(X1)分别使用什么样的过滤方式。
隧道案例
先看看效果:
案例效果
案例简述
使用纹理渲染地面、天花板和左右两面的墙壁。用键盘控制前进、后退以及观察视角的旋转。
核心代码
核心代码就是纹理的渲染,键盘控制可以参考本人往期的OpenGL的相关文章。
初始化设置SetupRC
void SetupRC() {
glClearColor(0.0f,0.0f,0.0f,1.0f);
shaderManager.InitializeStockShaders();
//1.定义纹理需要的相关变量
GLbyte *pBytes;
GLint iWidth, iHeight, iComponents;
GLenum eFormat;
GLint iLoop;
//2.申请TEXTURE_COUNT个纹理对象
glGenTextures(TEXTURE_COUNT, textures);
//3.设置纹理属性
for (iLoop = 0; iLoop < TEXTURE_COUNT; iLoop++) {
//3.1绑定纹理
glBindTexture(GL_TEXTURE_2D, textures[iLoop]);
//3.2加载纹理
pBytes = gltReadTGABits(szTextureFiles[iLoop], &iWidth, &iHeight, &iComponents, &eFormat);
//3.3放大缩小过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
//3.4环绕方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
//3.5设定纹理
glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBytes);
//3.6设置mipmap
glGenerateMipmap(GL_TEXTURE_2D);
free(pBytes);
}
GLfloat z;
//4.地板批次类设置
floorBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for (z = 60; z >= 0; z-=10) {
floorBatch.MultiTexCoord2f(0, 0, 0);
floorBatch.Vertex3f(-10, -10, z);
floorBatch.MultiTexCoord2f(0, 1, 0);
floorBatch.Vertex3f(10, -10, z);
floorBatch.MultiTexCoord2f(0, 0, 1);
floorBatch.Vertex3f(-10, -10, z-10);
floorBatch.MultiTexCoord2f(0, 1, 1);
floorBatch.Vertex3f(10, -10, z-10);
}
floorBatch.End();
//5.天花板批次类设置
ceilingBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for (z = 60; z >= 0; z-=10) {
ceilingBatch.MultiTexCoord2f(0, 0, 0);
ceilingBatch.Vertex3f(-10, 10, z);
ceilingBatch.MultiTexCoord2f(0, 0, 1);
ceilingBatch.Vertex3f(-10, 10, z - 10);
ceilingBatch.MultiTexCoord2f(0, 1, 0);
ceilingBatch.Vertex3f(10, 10, z);
ceilingBatch.MultiTexCoord2f(0, 1, 1);
ceilingBatch.Vertex3f(10, 10, z - 10);
}
ceilingBatch.End();
//6.左侧墙壁批次类设置
leftWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for (z = 60; z >= 0; z-=10) {
leftWallBatch.MultiTexCoord2f(0, 0, 0);
leftWallBatch.Vertex3f(-10, -10, z);
leftWallBatch.MultiTexCoord2f(0, 0, 1);
leftWallBatch.Vertex3f(-10, 10, z);
leftWallBatch.MultiTexCoord2f(0, 1, 0);
leftWallBatch.Vertex3f(-10, -10, z - 10);
leftWallBatch.MultiTexCoord2f(0, 1, 1);
leftWallBatch.Vertex3f(-10, 10, z - 10);
}
leftWallBatch.End();
//7.右侧墙壁批次类设置
rightWallBatch.Begin(GL_TRIANGLE_STRIP, 28, 1);
for (z = 60; z >= 0; z-=10) {
rightWallBatch.MultiTexCoord2f(0, 0, 0);
rightWallBatch.Vertex3f(10, -10, z);
rightWallBatch.MultiTexCoord2f(0, 0, 1);
rightWallBatch.Vertex3f(10, 10, z);
rightWallBatch.MultiTexCoord2f(0, 1, 0);
rightWallBatch.Vertex3f(10, -10, z - 10);
rightWallBatch.MultiTexCoord2f(0, 1, 1);
rightWallBatch.Vertex3f(10, 10, z - 10);
}
rightWallBatch.End();
}
-
定义纹理需要的一些变量,这些的值都会在加载纹理gltReadTGABits函数中进行返回,并且在设定纹理glTexImage2D函数中会使用到,传入即可。
-
申请纹理对象,本案例中会用到3个纹理(地面、天花板、墙壁)。
-
设置纹理属性,用一个for循环,for内部统一处理3个纹理。
- 绑定纹理,使用2D的方式
- 加载纹理,获取纹理的相关信息到定义的变量上
- 设置放大缩小过滤方式
- 设置环绕方式,绕S和T环绕
- 设定纹理
- 设置mip
- 设置地板,使用三角形带GL_TRIANGLE_STRIP的方式设置批次类。将纹理的四个顶点设置到如图的位置上: 地板
- 设置天花板,与设置地板类似: 天花板
- 设置左右墙壁: 左右墙壁
渲染函数RenderScene
void RenderScene(void) {
//清楚一个或一组特定的缓冲区
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
M3DMatrix44f mCamera;
cameraFrame.GetCameraMatrix(mCamera);
modelViewMatrix.MultMatrix(mCamera);
modelViewMatrix.PushMatrix(mCamera);
modelViewMatrix.Translate(0, 0, 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();
}
- 使用纹理替换GLT_SHADER_TEXTURE_REPLACE着色器
- 绘制不同纹理前需要先进行绑定后在绘制
两个核心函数中纹理设置大概流程如图:
右击菜单-修改过滤方式
通过右击可以呼出菜单,修改不同的缩小过滤方式
GL_NEARSET_MIPMAP_LINEAR 与 GL_LINEAR
- 左图是:GL_NEARSET_MIPMAP_LINEAR方式
- 右图是:GL_LINEAR方式
可以看到远处墙壁模糊度是不同的,也就是缩小状态下过滤方式不同。