美文网首页
OpenGL-13-案例4:纹理金字塔

OpenGL-13-案例4:纹理金字塔

作者: 宇宙那么大丶 | 来源:发表于2020-08-04 23:31 被阅读0次

    结合之前文章所涉及到的知识,我们来实现一个简单案例

    效果图

    一、案例分析

    image.png

    金字塔由4个三角形+底部正方形构成,而底部正方形是由X、Y两个三角形构成的。所以说整个模型是有5个顶点:(以物体中心点为原点坐标)
    VBackLeft (-1.0,-1.0,-1.0)
    VBackRight (1.0,-1.0,-1.0)
    vFrontLeft (-1.0,-1.0,1.0)
    VFrontRight(1.0,-1.0,1.0)
    vApex (0,1.0,0)

    结合上一篇文章的纹理坐标知识来看,那么他们相对应的纹理坐标就是:
    VBackLeft ( 0,0)
    VBackRight (1,0)
    vFrontLeft (0,1)
    VFrontRight (1,1)
    vApex (0.5,1)

    二、代码逻辑流程图

    image.png

    三、源码

    #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;
    GLFrame                cameraFrame;
    GLFrame             objectFrame;
    GLFrustum            viewFrustum;
    
    GLBatch             pyramidBatch;
    
    //纹理变量,一般使用无符号整型
    GLuint              textureID;
    
    GLGeometryTransform    transformPipeline;
    M3DMatrix44f        shadowMatrix;
    
    
    
    
    //用来初始化纹理,将TGA文件加载为2D纹理。
    bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode)
    {
    
        //定义一个指针
        GLbyte *pBits;
        //定义文件的 宽、高、组件
        int nWidth,nHeight,nComponents;
        //定义文件格式
        GLenum eFormat;
        
        
        //1、读纹理的像素
        
        /*
         参数1:纹理文件名称
         参数2:文件宽度地址
         参数3:文件高度地址
         参数4:文件组件地址
         参数5:文件格式地址
         返回值:pBits,指向图像数据的指针
         */
        pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
        
        if (pBits == NULL) {
            return false;
        }
    
        
        
        //2、设置纹理参数
        
        //S、T方向的环绕方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
        
    
        //放大、缩小时过滤方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
        
        
        //3、载入纹理
        /*
         参数1:纹理维度
         参数2:mip贴图层次
         参数3:纹理单元存储的颜色成分(从读取像素图是获得)
         参数4:加载纹理宽
         参数5:加载纹理高
         参数6:加载纹理的深度
         参数7:像素数据的数据类型(GL_UNSIGNED_BYTE,每个颜色分量都是一个8位无符号整数)
         参数8:指向纹理图像数据的指针
         */
        glTexImage2D(GL_TEXTURE_2D, 0, nComponents, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
        
        //4、使用完,释放指针
        free(pBits);
        
     
        return true;
    }
    //用来绘制金字塔
    void MakePyramid(GLBatch& pyramidBatch)
    {
        
        //1、顶点坐标
        M3DVector3f vApex = { 0.0f, 1.0f, 0.0f };
        M3DVector3f vFrontLeft = { -1.0f, -1.0f, 1.0f };
        M3DVector3f vFrontRight = { 1.0f, -1.0f, 1.0f };
        M3DVector3f vBackLeft = { -1.0f,  -1.0f, -1.0f };
        M3DVector3f vBackRight = { 1.0f,  -1.0f, -1.0f };
        
        //2、begin 开始设置
        /*
         1、类型
         2、定点数
         3、这个批次中会用到几个纹理,不写就是0
         */
        pyramidBatch.Begin(GL_TRIANGLES, 18, 1);
        
        //======================金字塔底部======================
        //底部的四边形 = 三角形X + 三角形Y
        //三角形X = (vBackLeft,vBackRight,vFrontRight)
        
        //1、vBackLeft
        //导入纹理坐标
        
        /*
         参数1:texture,纹理层次,对于使用存储着色器来进行渲染,设置为0
         参数2:s  对应顶点坐标中的x坐标
         参数3:t  对应顶点坐标中的y坐标
         */
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        //导入顶点坐标
        pyramidBatch.Vertex3fv(vBackLeft);
        
        
        //2、vBackRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        //3、vFrontRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
        
        
        //三角形Y =(vFrontLeft,vBackLeft,vFrontRight)
        //vFrontLeft
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //vBackLeft
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        //vFrontRight
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 1.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
        
        
        //======================金字塔前面======================
        //三角形:(Apex,vFrontLeft,vFrontRight)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
    
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
    
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
        
        
        //======================金字塔左边======================
        //三角形:(vApex, vBackLeft, vFrontLeft)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        
        //======================金字塔右边======================
        //三角形:(vApex, vFrontRight, vBackRight)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vFrontRight);
    
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        
        //======================金字塔后面======================
        //三角形:(vApex, vBackRight, vBackLeft)
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.0f);
        pyramidBatch.Vertex3fv(vApex);
        
        pyramidBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackRight);
        
        pyramidBatch.MultiTexCoord2f(0, 1.0f, 0.0f);
        pyramidBatch.Vertex3fv(vBackLeft);
        
        
        
        //3、end 结束设置
        pyramidBatch.End();
    }
    
    
    //初始化纹理和金字塔
    void SetupRC()
    {
        //1、设置背景色
        glClearColor(0.77, 0.77, 0.77, 1.0);
        //2、初始化着色器
        shaderManager.InitializeStockShaders();
        //3、深度测试
        glEnable(GL_DEPTH_TEST);
        
        //4、分配纹理对象
        glGenTextures(1, &textureID);
        //5、绑定纹理状态
        glBindTexture(GL_TEXTURE_2D, textureID);
        //6、加载纹理数据并且设置纹理数据
        //纹理文件名称、缩小时候的过滤方式、放大时候的过滤方式、环绕方式
        LoadTGATexture("brick.tga", GL_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
        
        //7、初始化金字塔
        MakePyramid(pyramidBatch);
        
        //8、设置观察者位置
        cameraFrame.MoveForward(-10);
        
    }
    
    
    
    
    //清理,删除纹理对象
    void ShutdownRC(void)
    {
        glDeleteTextures(1, &textureID);
    }
    
    
    void RenderScene(void)
    {
        
        //1、清理缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //2、压栈
        modelViewMatrix.PushMatrix();
        
        //观察者矩阵
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        modelViewMatrix.MultMatrix(mCamera);
        
        //物体矩阵
        M3DMatrix44f mObjectFrame;
        objectFrame.GetMatrix(mObjectFrame);
        modelViewMatrix.MultMatrix(mObjectFrame);
        
        //3、以防万一,在这里绑定纹理
        glBindTexture(GL_TEXTURE_2D, textureID);
        
        //4、使用着色器  纹理替换矩阵着色器
        /*
        参数1:GLT_SHADER_TEXTURE_REPLACE(着色器标签)
        参数2:模型视图投影矩阵
        参数3:纹理层
        */
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_REPLACE,transformPipeline.GetModelViewProjectionMatrix(),0);
        
         
        //5、绘制
        pyramidBatch.Draw();
        
        //6、出栈
        modelViewMatrix.PopMatrix();
        
        //7、交换缓冲区
        glutSwapBuffers();
        
    }
    
    void SpecialKeys(int key, int x, int y)
    {
        //这里让物体在世界坐标系中进行旋转
        if(key == GLUT_KEY_UP){
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1, 0, 0);
        }
        
        if(key == GLUT_KEY_DOWN){
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 1, 0, 0);
        }
        
        if(key == GLUT_KEY_LEFT){
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
        }
        
        if(key == GLUT_KEY_RIGHT){
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
        }
        //提交,调用RenderScene重新渲染
        glutPostRedisplay();
        
        
    }
    
    void ChangeSize(int w, int h)
    {
        //1
        glViewport(0, 0, w, h);
        //2、创建投影矩阵
        viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
        //3、将投影矩阵加载到投影矩阵堆栈上
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        //4、设置变换管道来使用MV矩阵堆栈和P矩阵堆栈
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    
    int main(int argc, char* argv[])
    {
        gltSetWorkingDirectory(argv[0]);
        
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        glutInitWindowSize(800, 800);
        glutCreateWindow("纹理金字塔");
    
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        
        glutReshapeFunc(ChangeSize);
        glutSpecialFunc(SpecialKeys);
        glutDisplayFunc(RenderScene);
        
        SetupRC();
        
        glutMainLoop();
        
        ShutdownRC();
        
        return 0;
    }
    
    
    
    

    四、注意事项

    • 避免一个方法太臃肿,提炼出两个封装函数,看起来更清晰
    • 注意LoadTGATexture 方法中,纹理API的使用顺序
    • 注意MakePyramid 方法中,各个顶点在纹理坐标中对应的映射关系,不要搞混了
    • 注意RenderScene 方法中,压栈后为了防止纹理在其他地方的使用,特地又绑定了一次。然后记得出栈操作

    相关文章

      网友评论

          本文标题:OpenGL-13-案例4:纹理金字塔

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