美文网首页opengl
OpenGL纹理MipMap和隧道案例

OpenGL纹理MipMap和隧道案例

作者: 过气的程序员DZ | 来源:发表于2020-07-22 13:50 被阅读0次

    开场白

    本文介绍一下纹理的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();
        
    }
    
    1. 定义纹理需要的一些变量,这些的值都会在加载纹理gltReadTGABits函数中进行返回,并且在设定纹理glTexImage2D函数中会使用到,传入即可。

    2. 申请纹理对象,本案例中会用到3个纹理(地面、天花板、墙壁)。

    3. 设置纹理属性,用一个for循环,for内部统一处理3个纹理。

      1. 绑定纹理,使用2D的方式
      2. 加载纹理,获取纹理的相关信息到定义的变量上
      3. 设置放大缩小过滤方式
      4. 设置环绕方式,绕S和T环绕
      5. 设定纹理
      6. 设置mip
    4. 设置地板,使用三角形带GL_TRIANGLE_STRIP的方式设置批次类。将纹理的四个顶点设置到如图的位置上: 地板
    5. 设置天花板,与设置地板类似: 天花板
    1. 设置左右墙壁: 左右墙壁
    渲染函数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();
    }
    
    1. 使用纹理替换GLT_SHADER_TEXTURE_REPLACE着色器
    2. 绘制不同纹理前需要先进行绑定后在绘制

    两个核心函数中纹理设置大概流程如图:


    右击菜单-修改过滤方式

    通过右击可以呼出菜单,修改不同的缩小过滤方式


    GL_NEARSET_MIPMAP_LINEAR 与 GL_LINEAR
    • 左图是:GL_NEARSET_MIPMAP_LINEAR方式
    • 右图是:GL_LINEAR方式
      可以看到远处墙壁模糊度是不同的,也就是缩小状态下过滤方式不同。

    示例代码地址

    相关文章

      网友评论

        本文标题:OpenGL纹理MipMap和隧道案例

        本文链接:https://www.haomeiwen.com/subject/lwwzkktx.html