zoukankan      html  css  js  c++  java
  • OpenGL代码学习(24)--地板镜像效果的应用

    注意:需要在配置好OpenGL的编程环境中运行下列代码,环境配置文章可参考:

    OpenGL在Mac项目上的配置

    下面的代码,直接放置在main.cpp文件中即可:

    #include "GLTools.h"
    #include "GLShaderManager.h"
    #include "GLFrustum.h"
    #include "GLBatch.h"
    #include "GLFrame.h"
    #include "GLMatrixStack.h"
    #include "GLGeometryTransform.h"
    #include "StopWatch.h"
    
    #include <math.h>
    #include <stdio.h>
    
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    // 着色器
    GLShaderManager shaderManager;
    // 视图矩阵堆栈
    GLMatrixStack modelViewMatrix;
    // 投影矩阵堆栈
    GLMatrixStack projectionMatrix;
    // 视景体
    GLFrustum viewFrustum;
    // 照相机角色
    GLFrame cameraFrame;
    // 变换管线
    GLGeometryTransform transformPipeline;
    // 花托批次
    GLTriangleBatch torusBatch;
    // 地板批次
    GLBatch floorBatch;
    // 旋转小球批次
    GLTriangleBatch sphereBatch;
    
    // 随机小球群
    #define NUM_SPHERES 50
    GLFrame spheres[NUM_SPHERES];
    
    // 定义3个纹理标识
    #define TEXTURE_MARBLE      0
    #define TEXTURE_MARSLIKE    1
    #define TEXTURE_MOONLIKE    2
    #define TEXTURE_COUNT       3
    // 纹理标识数组
    GLuint uiTextures[TEXTURE_COUNT];
    // 纹理文件名数组
    const char *szTextureFiles[TEXTURE_COUNT] = { "Marble.tga", "Marslike.tga", "MoonLike.tga" };
    
    // 从 TGA 文件中加载纹理数据
    bool LoadTGATexture(const char *szFileName, GLenum minFilter, GLenum magFilter, GLenum wrapMode) {
        // 从 TGA 文件读取纹理数据
        GLbyte *pBits;
        int nWidth, nHeight, nComponents;
        GLenum eFormat;
        pBits = gltReadTGABits(szFileName, &nWidth, &nHeight, &nComponents, &eFormat);
        if(pBits == NULL)
            return false;
        
        // 设置环绕模式
        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);
        
        // 设置纹理压缩
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
        
        // 加载纹理数据到2维纹理缓冲区
        glTexImage2D(GL_TEXTURE_2D, 0, GL_COMPRESSED_RGB, nWidth, nHeight, 0, eFormat, GL_UNSIGNED_BYTE, pBits);
        
        // 释放纹理数据
        free(pBits);
        
        // 判断缩小过滤器是否是 Mip 贴图缩小过滤器,是则开启 Mip 贴图功能
        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 LoadAllTextureData() {
        // 申请纹理数据标识
        glGenTextures(TEXTURE_COUNT, uiTextures);
    
        // 加载 Marble 纹理
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MARBLE]);
        LoadTGATexture(szTextureFiles[TEXTURE_MARBLE], GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
    
        // 加载 Mars 纹理
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MARSLIKE]);
        LoadTGATexture(szTextureFiles[TEXTURE_MARSLIKE], GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
    
        // 加载 Moon 纹理
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MOONLIKE]);
        LoadTGATexture(szTextureFiles[TEXTURE_MOONLIKE], GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_CLAMP_TO_EDGE);
    }
    
    // 程序初始化
    void SetupRC() {
        // 设置窗口背景颜色为黑色
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        
        // 初始化着色器
        shaderManager.InitializeStockShaders();
        
        // 开启深度测试
        glEnable(GL_DEPTH_TEST);
        // 开启背面剔除,用于地板镜像效果实现
        glEnable(GL_CULL_FACE);
        
        // 得到花托批次数据
        gltMakeTorus(torusBatch, 0.4f, 0.15f, 30, 30);
        
        // 得到旋转小球批次数据
        gltMakeSphere(sphereBatch, 0.1f, 26, 13);
        
        // 得到地板批次数据
        GLfloat texSize = 10.0f;
        floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
        floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
    
        floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
        floorBatch.Vertex3f(20.0f, -0.41f, 20.0f);
    
        floorBatch.MultiTexCoord2f(0, texSize, texSize);
        floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
    
        floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
        floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
        floorBatch.End();
        
        // 随机小球群位置数据生成
        for(int i = 0; i < NUM_SPHERES; i++) {
            GLfloat x = ((GLfloat)((rand() % 400) - 200) * 0.1f);
            GLfloat z = ((GLfloat)((rand() % 400) - 200) * 0.1f);
            spheres[i].SetOrigin(x, 0.0f, z);
        }
        
        // 加载所有纹理数据
        LoadAllTextureData();
    }
    
    // 程序资源释放
    void ShutdownRC(void) {
        glDeleteTextures(TEXTURE_COUNT, uiTextures);
    }
    
    // 绘制球体世界除开地板外的所有物体
    void DrawSongAndDance(GLfloat yRot) {
        static GLfloat vWhite[] = { 1.0f, 1.0f, 1.0f, 1.0f };
        static GLfloat vLightPos[] = { 0.0f, 3.0f, 0.0f, 1.0f };
        
        // 获取点光源位置
        M3DMatrix44f mCamera;
        M3DVector4f vLightEyePos;
        modelViewMatrix.GetMatrix(mCamera);
        m3dTransformVector4(vLightEyePos, vLightPos, mCamera);
        
        // 绘制随机小球群,这里的做法是瞬间切换当前角色位置并开始绘制小球,然后切换回原来角色位置
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MOONLIKE]);
        for(int i = 0; i < NUM_SPHERES; i++) {
            // 保存当前矩阵状态,为了能切换回原来的角色位置
            modelViewMatrix.PushMatrix();
            // 调整当前角色位置为随机生成小球的位置
            modelViewMatrix.MultMatrix(spheres[i]);
            // 绘制随机小球
            shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, modelViewMatrix.GetMatrix(),
                                         transformPipeline.GetProjectionMatrix(), vLightEyePos, vWhite, 0);
            sphereBatch.Draw();
            // 还原矩阵状态,切换为原来角色位置
            modelViewMatrix.PopMatrix();
        }
        
        // 视图矩阵进行平移
        modelViewMatrix.Translate(0.0f, 0.2f, -2.5f);
        // 保持旋转前的视图矩阵,因为如果不在旋转前保存,会导致旋转小球也会带上花托的旋转
        modelViewMatrix.PushMatrix();
        // 继续进行旋转
        modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
        
        // 绘制中间花托圆环
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MARSLIKE]);
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, modelViewMatrix.GetMatrix(),
                                     transformPipeline.GetProjectionMatrix(), vLightEyePos, vWhite, 0);
        torusBatch.Draw();
        
        // 恢复到旋转前的视图矩阵
        modelViewMatrix.PopMatrix();
        
        // 绘制旋转小球
        modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
        modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MOONLIKE]);
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_POINT_LIGHT_DIFF, modelViewMatrix.GetMatrix(),
                                     transformPipeline.GetProjectionMatrix(), vLightEyePos, vWhite, 0);
        sphereBatch.Draw();
    }
    
    // 窗口渲染回调
    void RenderScene(void) {
        
        // 获取2次渲染之间的时间间隔
        static CStopWatch rotTimer;
        float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
        static GLfloat vFloorColor[] = { 1.0f, 1.0f, 1.0f, 0.75f };
        
        // 清空缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // 压入照相机矩阵
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        modelViewMatrix.PushMatrix(mCamera);
        
        // 绘制地板镜像,Y 轴反转实现镜像效果
        modelViewMatrix.PushMatrix();
        modelViewMatrix.Scale(1.0f, -1.0f, 1.0f);
        modelViewMatrix.Translate(0.0f, 0.8f, 0.0f);
        glFrontFace(GL_CW);
        DrawSongAndDance(yRot);
        glFrontFace(GL_CCW);
        modelViewMatrix.PopMatrix();
        
        // 开启颜色混合,使地板镜像和地板融合一体
        glEnable(GL_BLEND);
        glBindTexture(GL_TEXTURE_2D, uiTextures[TEXTURE_MARBLE]);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor, 0);
        floorBatch.Draw();
        glDisable(GL_BLEND);
        
        // 绘制地板上面的球体世界
        DrawSongAndDance(yRot);
        
        // 出栈,恢复成原始矩阵
        modelViewMatrix.PopMatrix();
        
        // 因为是双缓冲区模式,后台缓冲区替换到前台缓存区进行显示
        glutSwapBuffers();
        
        // 自动触发渲染,达到动画效果
        glutPostRedisplay();
    }
    
    // 特殊按键点击回调
    void SpecialKeys(int key, int x, int y) {
        float linear = 0.1f;
        float angular = float(m3dDegToRad(5.0f));
        
        // 控制角色向前后移动
        if(key == GLUT_KEY_UP)
            cameraFrame.MoveForward(linear);
        
        if(key == GLUT_KEY_DOWN)
            cameraFrame.MoveForward(-linear);
        
        // 控制角色向左右旋转
        if(key == GLUT_KEY_LEFT)
            cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
        
        if(key == GLUT_KEY_RIGHT)
            cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
    }
    
    // 窗口变换回调
    void ChangeSize(int width, int height) {
        
        // 防止除数为0
        if(height == 0)
            height = 1;
        
        // 设置视口
        glViewport(0, 0, width, height);
        
        // 设置透视投影
        viewFrustum.SetPerspective(35.0f, float(width)/float(height), 1.0f, 100.0f);
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        // 设置变换管线的矩阵堆栈
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    // 程序入口
    int main(int argc, char* argv[]) {
        // 设置 Mac OS 工作目录路径
        gltSetWorkingDirectory(argv[0]);
        
        // GLUT初始化
        glutInit(&argc, argv);
        
        // 设置渲染模式
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
        
        // 初始化窗口大小
        glutInitWindowSize(800, 720);
        
        // 创建窗口并命名
        glutCreateWindow("OpenGL SphereWorld");
        
        // 判断驱动程序是否初始化完毕
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "Error: %s
    ", glewGetErrorString(err));
            return 1;
        }
        
        // 窗口变化回调函数设置
        glutReshapeFunc(ChangeSize);
        
        // 窗口渲染回调函数设置
        glutDisplayFunc(RenderScene);
        
        // 特殊按键回调函数设置
        glutSpecialFunc(SpecialKeys);
        
        // 初始化环境
        SetupRC();
        
        // 主消息循环
        glutMainLoop();
        
        // 程序资源释放
        ShutdownRC();
        
        return 0;
    }

    效果如下:

  • 相关阅读:
    BZOJ 2005 能量采集
    HDU 2841 Visible Trees(莫比乌斯反演)
    hihocoder 1543
    hihocoder 1311
    hdu 6069
    hdu 6058
    hdu 6034
    拓展欧几里得
    poj 3321
    树状数组总结
  • 原文地址:https://www.cnblogs.com/cchHers/p/14726872.html
Copyright © 2011-2022 走看看