美文网首页OpenGL初探
第二十一节—纹理填充卫星图

第二十一节—纹理填充卫星图

作者: L_Ares | 来源:发表于2020-09-07 19:16 被阅读0次

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

    本节是对第十五节的卫星图代码进行了纹理的填充,加上了镜面效果,算是对之前所学习的重点知识的综合复习。

    
    //
    //  main.cpp
    //  11球体
    //
    //  Created by EasonLi on 2020/9/6.
    //  Copyright © 2020 EasonLi. All rights reserved.
    //
    
    #include <stdio.h>
    
    #pragma mark - 引用类
    
    #include "GLTools.h"
    #include "GLShaderManager.h"
    #include "GLFrame.h"
    #include "GLFrustum.h"
    #include "GLBatch.h"
    #include "GLMatrixStack.h"
    #include "GLGeometryTransform.h"
    #include "StopWatch.h"
    #include "math3d.h"
    
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    #pragma mark - 常用变量
    //着色器管理器
    GLShaderManager shaderManager;
    //视景体
    GLFrustum viewFrustum;
    //观察者(视点/摄像机)参考帧
    GLFrame cameraFrame;
    //模型视图矩阵堆栈
    GLMatrixStack modelViewMatrixStack;
    //投影矩阵堆栈
    GLMatrixStack projectionMatrixStack;
    //变换管道
    GLGeometryTransform transformPipeLine;
    //地板批次类
    GLBatch floorBatch;
    //小球批次类
    GLTriangleBatch sBallBatch;
    //大球批次类
    GLTriangleBatch lBallBatch;
    //小球个数
    #define SMALL_BALL_COUNT 50
    //纹理数量
    #define TEXTURE_COUNT 3
    //小球坐标数组
    GLFrame smallBallFrame[SMALL_BALL_COUNT];
    //纹理标记数组
    GLuint nTextures[TEXTURE_COUNT];
    
    //点光源位置
    static GLfloat nLightPos[] = {0.f,3.f,0.f,1.f};
    //漫反射颜色
    static GLfloat nWhite[] = {1.f,1.f,1.f,1.f};
    //地板颜色数组
    static GLfloat nfloorColor[] = {1.f,1.f,0.f,0.75f};
    
    
    #pragma mark - 函数
    //初建或重塑窗口
    void ChangeSize(int w , int h)
    {
        
        //避免除数为0
        if (h == 0) {
            h = 1;
        }
        
        //设置视口大小
        glViewport(0, 0, w, h);
        
        //设置投影方式和参数
        viewFrustum.SetPerspective(36.f, float(w)/float(h), 1.f, 120.f);
        
        //获取投影矩阵并放置到投影矩阵堆栈栈顶
        projectionMatrixStack.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //设置变换管道
        transformPipeLine.SetMatrixStacks(modelViewMatrixStack, projectionMatrixStack);
        
    }
    
    //从TGA文件读取纹理、加载纹理、设置纹理
    bool LoadTextureFromTGA (const char *fileName , GLenum minFilter , GLenum magFilter , GLenum wrapMode)
    {
        
        //定义字节类型变量,用来存储从tga文件读取出来的纹理数据
        GLbyte *nByte;
        //定义变量,用来存储从tga文件中读取出的纹理的各项属性
        int nWidth,nHeight,nComponent;
        GLenum nFormat;
        
        //从tga文件中读取纹理
        nByte = gltReadTGABits(fileName, &nWidth, &nHeight, &nComponent, &nFormat);
        
        if (nByte == NULL) {
            printf("没有纹理数据");
            return false;
        }
        
        //设置纹理属性
        //设置纹理过滤器
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, magFilter);
        
        //设置纹理环绕模式
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapMode);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrapMode);
        
        //载入纹理
        glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0, nFormat, GL_UNSIGNED_BYTE, nByte);
        
        //使用完了纹理数据就可以释放了
        free(nByte);
        
        //生成Mipmap贴图
        if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
        minFilter == GL_LINEAR_MIPMAP_NEAREST ||
        minFilter == GL_NEAREST_MIPMAP_LINEAR ||
        minFilter == GL_NEAREST_MIPMAP_NEAREST)
            
        glGenerateMipmap(GL_TEXTURE_2D);
        
        return true;
        
    }
    
    //设置地板
    void SetFloor ()
    {
        
        //参数
        //(1).图元绘制方式
        //(2).顶点数量
        //(3).纹理数组数量
        floorBatch.Begin(GL_TRIANGLE_FAN, 4,1);
        
        //设置纹理坐标和顶点坐标
        //这里将纹理坐标扩大了,属于特殊的情况,因为tga纹理有点小,但是地板有点大,所以放
        //大一点
        floorBatch.MultiTexCoord2f(0, 0.f, 0.f);
        floorBatch.Vertex3f(-20.f, -0.5f, 20.f);
        
        floorBatch.MultiTexCoord2f(0, 10.f, 0.f);
        floorBatch.Vertex3f(20.f, -0.5f, 20.f);
        
        floorBatch.MultiTexCoord2f(0, 10.f, 10.f);
        floorBatch.Vertex3f(20.f, -0.5f, -20.f);
        
        floorBatch.MultiTexCoord2f(0, 0.f, 10.f);
        floorBatch.Vertex3f(-20.f, -0.5f, -20.f);
        floorBatch.End();
        
    }
    
    
    //设置渲染环境
    void SetUpRC ()
    {
        
        //设置清屏颜色
        glClearColor(0.f, 0.f, 0.f, 1.f);
        //初始化着色器管理器
        shaderManager.InitializeStockShaders();
        
        //开启正背面剔除和深度测试
        glEnable(GL_CULL_FACE);
        glEnable(GL_DEPTH_TEST);
        
    /********************************设置地板*********************************/
        //设置地板顶点和纹理顶点数据
        SetFloor();
        //生成纹理标记,分配纹理对象
        glGenTextures(3, nTextures);
        //绑定纹理对象(地板)
        glBindTexture(GL_TEXTURE_2D, nTextures[0]);
        //读取纹理、设置纹理、载入纹理
        LoadTextureFromTGA("Marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
    /********************************设置大球*********************************/
        //设置大球属性
        gltMakeSphere(lBallBatch, 0.5f, 60, 120);
        //绑定纹理对象(大球)
        glBindTexture(GL_TEXTURE_2D, nTextures[1]);
        //读取纹理、设置纹理、载入纹理
        LoadTextureFromTGA("Marslike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
    /********************************设置公转小球*********************************/
        //设置公转小球属性
        gltMakeSphere(sBallBatch, 0.2f, 36, 72);
        //绑定纹理对象(公转小球)
        glBindTexture(GL_TEXTURE_2D, nTextures[2]);
        //读取纹理、设置纹理、载入纹理
        LoadTextureFromTGA("MoonLike.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
    /********************************设置随机位置小球*********************************/
        for (int i = 0; i < SMALL_BALL_COUNT; i++) {
            
            GLfloat x = ((rand() % 400) - 200) * 0.1f;
            GLfloat z = ((rand() % 400) - 200) * 0.1f;
            
            smallBallFrame[i].SetOrigin(x, 0.f, z);
            
        }
        
    }
    
    //绘制除地板以外的其他图形
    void DrawOthers (GLfloat yRot)
    {
    
    /********************************绘制随机位置小球*********************************/
        glBindTexture(GL_TEXTURE_2D, nTextures[2]);
        for (int i = 0; i < SMALL_BALL_COUNT; i++) {
            modelViewMatrixStack.PushMatrix();
            modelViewMatrixStack.MultMatrix(smallBallFrame[i]);
            shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
            sBallBatch.Draw();
            modelViewMatrixStack.PopMatrix();
        }
        
    /********************************绘制大球*********************************/
        //先将大球从观察者位置移动一个距离,不然最开始就看不到整体的大球了
        modelViewMatrixStack.Translate(0.f, 0.2f, -2.5f);
        //压栈,之所以在移动了大球的位置后压栈,是因为公转小球要围绕着大球,公转小球的位置是要相对于大球位置,而不是观察者
        modelViewMatrixStack.PushMatrix();
        //大球围绕y轴做旋转
        modelViewMatrixStack.Rotate(yRot, 0.f, 1.f, 0.f);
        //绑定一下大球的纹理
        glBindTexture(GL_TEXTURE_2D, nTextures[1]);
        //设置着色器
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
        //绘制大球
        lBallBatch.Draw();
        //绘制完大球就出栈
        modelViewMatrixStack.PopMatrix();
    /********************************绘制公转小球*********************************/
        //压栈
        modelViewMatrixStack.PushMatrix();
        //让小球旋转的速度变成2倍,先旋转再平移,不然是没有公转效果的,因为平移是物体坐标系在世界坐标系中平移
        modelViewMatrixStack.Rotate(-2 * yRot, 0.f, 1.f, 0.f);
        //相对大球再在x轴多平移出来一些距离,这样才能看到小球
        modelViewMatrixStack.Translate(0.8f, 0.f, 0.f);
        glBindTexture(GL_TEXTURE_2D, nTextures[2]);
        //设置着色器
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF,transformPipeLine.GetModelViewMatrix(),transformPipeLine.GetProjectionMatrix(),nLightPos,nWhite,0);
        sBallBatch.Draw();
        modelViewMatrixStack.PopMatrix();
        
        
    }
    
    //渲染
    void RenderScene ()
    {
        
        //定义定时器
        static CStopWatch nTimer;
        //获取每秒的旋转角度
        GLfloat yRot = nTimer.GetElapsedSeconds() * 60.f;
        
        //清空缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        
        //压栈
        modelViewMatrixStack.PushMatrix();
        //定义一个矩阵变量,用以获取观察者矩阵
        M3DMatrix44f mCamera;
        //获取观察者矩阵并放入矩阵变量中
        cameraFrame.GetCameraMatrix(mCamera);
        //将观察者矩阵变化放入模型视图矩阵堆栈栈顶
        modelViewMatrixStack.MultMatrix(mCamera);
        
    /********************************绘制镜面部分*********************************/
        //镜面其实就是画了两遍,只不过相对于x和z轴构成的平面是对称的,所以要将Y轴的正方向旋转180度
        //为了让镜面和非镜面的旋转方向是一样的,可以把正背面的顺时针逆时针调换,因为球体的背面旋转和正面旋转刚好是相反的
        //所以,先压栈
        modelViewMatrixStack.PushMatrix();
        //然后旋转Y轴180度,可以稍微再做下缩放,这样更逼真
        modelViewMatrixStack.Scale(1.f, -0.9f, 1.f);
        //让镜面往下面移动多一点,不然和非镜面的图形会重叠
        modelViewMatrixStack.Translate(0.f, 1.2f, 0.f);
        //设置顺时针旋转为正面
        glFrontFace(GL_CW);
        //绘制镜面
        DrawOthers(yRot);
        //绘制完成把正面换回顺时针
        glFrontFace(GL_CCW);
        //出栈
        modelViewMatrixStack.PopMatrix();
        
    /********************************绘制地板部分*********************************/
        //开启混合
        glEnable(GL_BLEND);
        //指定颜色混合方程式
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        //绑定一下纹理,因为绘制的不只是一个图形,避免图形上的纹理出现错误
        glBindTexture(GL_TEXTURE_2D, nTextures[0]);
        
        //设置着色器管理器
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,transformPipeLine.GetModelViewProjectionMatrix(),nfloorColor,0);
        
        //绘制地板
        floorBatch.Draw();
        
        //绘制完成,取消混合
        glDisable(GL_BLEND);
        
    /********************************绘制非镜面部分*********************************/
        //绘制其他部分(非镜面)
        DrawOthers(yRot);
    /****************************************************************************/
        //绘制完成,出栈
        modelViewMatrixStack.PopMatrix();
        
        //交换缓冲区
        glutSwapBuffers();
        
        //因为球每秒都要旋转,所以发送渲染通知
        glutPostRedisplay();
        
    }
    
    //特殊键位
    void SpecialKeys (int key , int x , int y)
    {
        
        //每次前后移动的距离
        GLfloat nStep = 0.1f;
        //每次旋转的角度
        GLfloat nRot = (GLfloat)(m3dDegToRad(5.f));
        switch (key) {
            case GLUT_KEY_UP:
                cameraFrame.MoveForward(nStep);
                break;
                
            case GLUT_KEY_DOWN:
                cameraFrame.MoveForward(-nStep);
                break;
            case GLUT_KEY_LEFT:
                cameraFrame.RotateWorld(nRot, 0.f, 1.f, 0.f);
                break;
            case GLUT_KEY_RIGHT:
                cameraFrame.RotateWorld(-nRot, 0.f, 1.f, 0.f);
                break;
        }
        
    }
    
    //关闭纹理
    void ShutdownTexture ()
    {
        
        glDeleteTextures(TEXTURE_COUNT, nTextures);
        
    }
    
    #pragma mark - main
    int main (int argc,char *argv[])
    {
        
        //设置工作目录和项目目录在同一文件夹下
        gltSetWorkingDirectory(argv[0]);
        
        //初始化glut
        glutInit(&argc, argv);
        
        //初始化显示模式
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_STENCIL | GLUT_DEPTH);
        
        //初始化窗口尺寸
        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();
        
        //关闭纹理
        ShutdownTexture();
        
        return 0;
        
    }
    
    
    

    效果图如图1.1所示:

    1.1.png

    纹理图网盘链接,里面M开头的三个文件就是这节要用到的纹理图tga,提取码uhtv

    相关文章

      网友评论

        本文标题:第二十一节—纹理填充卫星图

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