美文网首页OpenGL初探
第十八节—纹理金字塔

第十八节—纹理金字塔

作者: L_Ares | 来源:发表于2020-09-03 12:05 被阅读0次

    本文为L_Ares个人写作,包括图片皆为个人亲自操作,以任何形式转载请表明原文出处。

    首先介绍几个函数,下面要用到的。

    1. 根据三个点寻找法线:

    void m3dFindNormal(M3DVector3f result, const M3DVector3f point1, const M3DVector3f point2, const M3DVector3f point3);

    参数:

    (1).result:找到的法线结果。
    (2).(3).(4).point:用来找法线的三个点。这三个点是要有顺序的,正面是逆时针排列,背面是顺时针排列。

    2. 设置法线:

    void Normal3fv(M3DVector3f vNormal);

    参数:

    vNormal:法线坐标数组

    3. 设置纹理坐标:

    void MultiTexCoord2f(GLuint texture, GLclampf s, GLclampf t);

    参数:

    (1).texture:纹理的mimap涂层数,也就是你要设置哪个纹理的纹理坐标,填入纹理的顺序数,我们这节只有一个纹理,所以这个纹理就是第1个,所以写0。

    (2).s: 纹理的横坐标对应的是多少。

    (2).t:纹理的纵坐标对应的是多少。

    4. 设置顶点坐标:

    void Vertex3fv(M3DVector3f vVertex);

    参数:

    vVertex:顶点坐标数组

    代码如下:

    
    //
    //  main.cpp
    //  09纹理金字塔
    //
    //  Created by EasonLi on 2020/9/2.
    //  Copyright © 2020 EasonLi. All rights reserved.
    //
    
    #include <stdio.h>
    
    #pragma mark - 引用类
    
    //工具类GLTools,GLTool中大多数类似于C语言的工具类都在其中
    #include "GLTools.h"
    //着色器管理器
    #include "GLShaderManager.h"
    //参考帧
    #include "GLFrame.h"
    //矩阵投影工具类
    #include "GLFrustum.h"
    //容器类。三角形批次类
    #include "GLBatch.h"
    //矩阵堆栈类。
    #include "GLMatrixStack.h"
    //管道类。管理mvp矩阵的
    #include "GLGeometryTransform.h"
    //3D数学类
    #include "math3d.h"
    
    //根据系统引入GLUT
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    #pragma mark - 公共变量
    
    //着色器管理器
    GLShaderManager shaderManager;
    //三角形批次类
    GLBatch pyramidBatch;
    //观察者参考帧
    GLFrame cameraFrame;
    //物体参考帧
    GLFrame objFrame;
    //模型视图矩阵堆栈
    GLMatrixStack mvMatrixStack;
    //投影矩阵堆栈
    GLMatrixStack projMatrixStack;
    //矩阵投影工具对象
    GLFrustum viewFrustum;
    //变换管道
    GLGeometryTransform transformPipeline;
    //纹理变量,一般用无符号整型
    GLuint textureID;
    
    
    #pragma mark - 函数
    //初建或重塑窗口
    void ChangeSize (int w , int h)
    {
        
        //设置视口大小
        if (h == 0) {
            h = 1;
        }
        glViewport(0, 0, w, h);
        
        //设置投影方式并创建投影矩阵
        viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 500.f);
        
        //将投影视图矩阵放入投影矩阵堆栈
        projMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //设置变换管道
        transformPipeline.SetMatrixStacks(mvMatrixStack, projMatrixStack);
        
    }
    
    /**
     将TGA文件加载成2D纹理
     里面包括了读取、加载、设置纹理
     参数
     (1)fileName:要加载的TGA文件的文件名
     (2)minFilter:缩小过滤模式
     (3)magFilter:方大过滤模式
     (4)wrapMode:环绕方式
     */
    bool LoadTGATexture(const char *fileName , GLenum minFilter , GLenum magFilter , GLenum wrapMode)
    {
        
        //先定义一下要用到的变量,用来存储读取纹理之后拿到的数据信息
        //因为读取纹理tga文件的函数返回的是字节类型
        GLbyte *mByte;
        //定义获取到的宽、高、组成和颜色成分变量
        int nWidth,nHeight,nComponent;
        GLenum nFormat;
        
        //1.读取纹理。将TGA文件读取成像素
        //参数
        //(1) 要读取的tga的文件名,必要时可以添加路径进去
        //注:从这里开始,后面的参数都是要的指针,所以给的是地址
        //(2) tga文件的宽度
        //(3) tga文件的高度
        //(4) tga文件的颜色成分组成
        //(5) tga文件的格式
        mByte = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponent, &nFormat);
        if (mByte == NULL) {
            return false;
        }
        
        //2.设置纹理
        //设置纹理的环绕方式
        //横向s轴的环绕方式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
        //纵向t轴的环绕方式
        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)允许为纹理添加一个边框,就是给一个边界的限度,一般设置成0就行了
        //(7)载入纹理要用的像素模式
        //(8)像素数据的数据类型
        //(9)指向纹理图像数据的指针。
        glTexImage2D(GL_TEXTURE_2D, 0, nComponent, nWidth, nHeight, 0, nFormat, GL_UNSIGNED_BYTE, mByte);
        
        //使用完成之后要释放
        free(mByte);
        
        //4.加载Mip纹理所生成的所有Mip层
        //参数选择:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
        glGenerateMipmap(GL_TEXTURE_2D);
        
        return true;
        
    }
    
    //设置金字塔顶点及纹理数据等
    void MakePyramid(GLBatch &pyramidBatch)
    {
        //通过三角形批次类设置金字塔基本绘制信息
        //参数:
        //(1)绘制方式
        //(2)顶点数量,金字塔是6个三角形组成的,4个侧面每个都是1个三角形,底面矩形是2个三角形,所以是6个三角形18个顶点
        //(3)要用几个纹理。如果这个参数不设置的话,默认是0的,也就是不用纹理。
        pyramidBatch.Begin(GL_TRIANGLES, 18 , 1);
        
        //设置顶点坐标
        //金字塔顶点
        //Front是靠近观察者的顶点,Back是远离观察者的顶点
        M3DVector3f vApex = {0.f,1.f,0.f};
        M3DVector3f vFrontLeft = {-1.f,-1.f,1.f};
        M3DVector3f vFrontRight = {1.f,-1.f,1.f};
        M3DVector3f vBackLeft = {-1.f,-1.f,-1.f};
        M3DVector3f vBackRight = {1.f,-1.f,-1.f};
        
        //临时的法线变量
        M3DVector3f vNormal;
        
        
        
        //金字塔底部是两个三角形构成,分别命名三角形A和三角形B
    /********************************************设置三角形A******************************************/
        //这里注意,法线在获取的时候,顶点的顺序是有方向性的,正面是逆时针顺序,背面是顺时针顺序,不然会出现黑色,没有纹理
        //顶点:vFrontLeft,vBackLeft,vBackRight
        //根据三个点,找到三角形A的法线,也可以自己算法线,无所谓的
        //参数:
        //(1)result:找到的法线。存储到刚才定义的临时法线变量里面
        //(2)(3)(4):用来找法线的三个点
        m3dFindNormal(vNormal, vFrontLeft, vBackLeft, vBackRight);
        
        //利用找到的三角形A的法线,立即设置三角形A的三个顶点的纹理
        //vBackLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //vBackRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackLeft);
        
        //vFrontRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        //参数:
        //(1)texture:第几个纹理!!!而不是把纹理放上去,是纹理的叠加的时候,这个纹理是第几个。要用哪个
        //(2)s:纹理的横坐标
        //(3)t:纹理的总坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackRight);
        
        
        
    /********************************************设置三角形B******************************************/
        //三角形B的三个点:vBackRight,vFrontRight,vFrontLeft
        //找到三角形B的法线
        m3dFindNormal(vNormal, vBackRight, vFrontRight, vFrontLeft);
        
        //设置三角形B的三个顶点纹理
        //vFrontLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackRight);
        
        //vBackLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontRight);
        
        //vFrontRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        
        
    /********************************************设置正面三角形****************************************/
        //顶点:vApex,vFrontLeft,vFrontRight
        //找到正面三角形的法线
        m3dFindNormal(vNormal, vApex, vFrontLeft, vFrontRight);
       
        //设置正面三角形的顶点纹理
        //vApex
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vApex);
        
        //vFrontLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //vFrontRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontRight);
    
    
        
    /********************************************设置左面三角形****************************************/
        //顶点:vApex,vFrontLeft,vBackLeft
        //找到左面三角形的法线
        m3dFindNormal(vNormal, vApex, vBackLeft, vFrontLeft);
        
        //设置左面三角形顶点纹理
        //vApex
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vApex);
        
        //vFrontLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontLeft);
        
        //vBackLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点
        pyramidBatch.Vertex3fv(vBackLeft);
        
        
        
    /********************************************设置右面三角形****************************************/
        //顶点:vApex,vFrontRight,vBackRight
        //找到右面三角形的法线
        m3dFindNormal(vNormal, vApex, vFrontRight, vBackRight);
        
        //设置右面三角形顶点纹理
        //vApex
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vApex);
        
        //vFrontRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vFrontRight);
        
        //vBackRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackRight);
        
        
        
    /********************************************设置后面三角形****************************************/
        //顶点:vApex,vBackLeft,vBackRight
        //找到后面三角形的法线
        m3dFindNormal(vNormal, vApex, vBackRight, vBackLeft);
        //设置后面三角形顶点纹理
        //vApex
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.5f, 1.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vApex);
    
        //vBackLeft
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 1.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackLeft);
    
        //vBackRight
        //设置法线
        pyramidBatch.Normal3fv(vNormal);
        //设置纹理坐标
        pyramidBatch.MultiTexCoord2f(0, 0.f, 0.f);
        //设置顶点坐标
        pyramidBatch.Vertex3fv(vBackRight);
        
    /***********************************************************************************************/
        
        //结束三角形批次类的设置
        pyramidBatch.End();
        
        
    }
    
    //设置渲染环境
    void SetUpRC ()
    {
        
        //设置清屏颜色
        glClearColor(0.3f, 0.3f, 0.3f, 1.f);
        
        //初始化着色器管理器
        shaderManager.InitializeStockShaders();
        
        //开启深度测试
        glEnable(GL_DEPTH_TEST);
        
        //观察者参考帧偏移一定的位置,不要和原点重合
        cameraFrame.MoveForward(-10.f);
        
        //分配纹理对象
        //参数:(1)要使用几个纹理 (2)纹理对象的指针
        glGenTextures(1, &textureID);
        
        //绑定纹理状态
        //参数:(1)纹理的状态 (2)纹理对象
        glBindTexture(GL_TEXTURE_2D, textureID);
        
        //读取、加载、设置纹理参数
        //这是一个自己写的函数,主要是把这三步合成一步
        //就是将TGA文件加载成2D纹理
        LoadTGATexture("stone.tga", GL_LINEAR_MIPMAP_NEAREST, GL_LINEAR, GL_CLAMP_TO_EDGE);
        
        //设置金字塔顶点及纹理数据等
        MakePyramid(pyramidBatch);
        
    }
    
    //清理
    void ShutDownRC ()
    {
        
        //
        glDeleteTextures(1, &textureID);
        
    }
    
    //渲染
    void RenderScene ()
    {
        
        //清理缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        
        //光源位置
        static GLfloat vLightPos[] = {1.f,1.f,0.f};
        
        //颜色
        static GLfloat vColor[] = {1.f,1.f,1.f,1.f};
        
        //压栈
        mvMatrixStack.PushMatrix();
        
        //获取观察者矩阵
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        mvMatrixStack.MultMatrix(mCamera);
        
        //获取物体矩阵
        M3DMatrix44f mObj;
        objFrame.GetMatrix(mObj);
        mvMatrixStack.MultMatrix(mObj);
        
        //绑定纹理。因为这里我们只有一个纹理,是可以不用再绑定的,但是多个纹理的时候,绑定一下保证纹理不错
        glBindTexture(GL_TEXTURE_2D, textureID);
        
        //设置着色器
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vColor,0);
        
        //绘制
        pyramidBatch.Draw();
        
        //出栈
        mvMatrixStack.PopMatrix();
        
        //交换缓冲区
        glutSwapBuffers();
        
        
    }
    
    //特殊键位
    void SpecialKeys (int key , int x ,int y)
    {
        switch (key) {
                
            case GLUT_KEY_UP:
                objFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
                break;
                
            case GLUT_KEY_DOWN:
                objFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
                break;
                
            case GLUT_KEY_RIGHT:
                objFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
                break;
                
            case GLUT_KEY_LEFT:
                objFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
                break;
                
        }
        glutPostRedisplay();
        
    }
    
    #pragma mark - main
    
    int main (int argc , char *argv[])
    {
        
        //设置工作目录和项目目录一致在/Resource下,glut已经设置,手动安全
        gltSetWorkingDirectory(argv[0]);
        
        //初始化glut
        glutInit(&argc, argv);
        
        //初始化显示模式
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        
        //设置窗口大小
        glutInitWindowSize(600, 600);
        
        //创建窗口并命名
        glutCreateWindow("纹理金字塔");
        
        //注册重塑函数
        glutReshapeFunc(ChangeSize);
        
        //注册渲染函数
        glutDisplayFunc(RenderScene);
        
        //注册特殊键位
        glutSpecialFunc(SpecialKeys);
        
        //初始化glew库
        GLenum status = glewInit();
        if (status != GLEW_OK) {
            printf("glew init error : %s \n",glewGetErrorString(status));
            return 1;
        }
        
        //设置渲染环境
        SetUpRC();
        
        //设置本地消息循环机制
        glutMainLoop();
        
        ShutDownRC();
        
        return 0;
        
    }
    
    
    

    执行结果如图1.1:

    1.1.png

    相关文章

      网友评论

        本文标题:第十八节—纹理金字塔

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