OpenGL代码学习(15)--理解透视投影矩阵

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

OpenGL在Mac项目上的配置

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

#include "GLTools.h"
#include "GLMatrixStack.h"
#include "GLFrame.h"
#include "GLFrustum.h"
#include "GLGeometryTransform.h"
#include "GLBatch.h"
#include "math.h"
#include <GLUT/GLUT.h>


GLFrame viewFrame;
GLFrustum viewFrustum;
GLBatch tubeBatch;
GLBatch innerBatch;
GLMatrixStack modelViewMatix;
GLMatrixStack projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager shaderManager;
float fZ = 100.0f;
float bZ = -100.0f;

// 窗口渲染调用
void RenderScene(void) {
    // 清除缓存区
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    // 压栈,物体矩阵
    modelViewMatix.PushMatrix(viewFrame);
    
    // 默认红色光源着色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
    tubeBatch.Draw();
    
    // 默认灰色光源着色
    GLfloat vGray[] = { 0.75f, 0.75f, 0.75f, 1.0f };
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vGray);
    innerBatch.Draw();
    
    // 出栈,还原为单位矩阵
    modelViewMatix.PopMatrix();
    
    // 双缓存模式,后台缓存切换到前台进行显示
    glutSwapBuffers();
}

void SetupTubeBatch() {
    tubeBatch.Begin(GL_QUADS, 200);
    tubeBatch.Color4f(1.0f, 0.0f, 0.0f, 1.0f);
    // 后面
    GLfloat vBack[] = {
        // Left Pannel
        -50.0f, 50.0f, fZ,
        -50.0f, -50.0f, fZ,
        -35.0f, -50.0f, fZ,
        -35.0f, 50.0f, fZ,
        // Right Panel
        50.0f, 50.0f, fZ,
        35.0f, 50.0f, fZ,
        35.0f, -50.0f, fZ,
        50.0f,-50.0f, fZ,
        // Top Panel
        -35.0f, 50.0f, fZ,
        -35.0f, 35.0f, fZ,
        35.0f, 35.0f, fZ,
        35.0f, 50.0f, fZ,
        // Bottom Panel
        -35.0f, -35.0f, fZ,
        -35.0f, -50.0f, fZ,
        35.0f, -50.0f, fZ,
        35.0f, -35.0f, fZ
    };
    for(int i = 0; i < 16; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(0.0f, 0.0f, 1.0f);
        tubeBatch.Vertex3f(vBack[index], vBack[index+1], vBack[index+2]);
    }
    
    // 前面
    GLfloat vFront[] = {
        // Left Pannel
        -35.0f, 50.0f, bZ,
        -35.0f, -50.0f, bZ,
        -50.0f, -50.0f, bZ,
        -50.0f, 50.0f, bZ,
        // Right Panel
        50.0f, -50.0f, bZ,
        35.0f, -50.0f, bZ,
        35.0f, 50.0f, bZ,
        50.0f, 50.0f, bZ,
        // Top Panel
        35.0f, 50.0f, bZ,
        35.0f, 35.0f, bZ,
        -35.0f, 35.0f, bZ,
        -35.0f, 50.0f, bZ,
        // Bottom Panel
        35.0f, -35.0f, bZ,
        35.0f, -50.0f, bZ,
        -35.0f, -50.0f, bZ,
        -35.0f, -35.0f, bZ
    };
    for(int i = 0; i < 16; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(0.0f, 0.0f, -1.0f);
        tubeBatch.Vertex3f(vFront[index], vFront[index+1], vFront[index+2]);
    }
    
    // 上面
    GLfloat vTop[] = {
        -50.0f, 50.0f, fZ,
        50.0f, 50.0f, fZ,
        50.0f, 50.0f, bZ,
        -50.0f, 50.0f, bZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(0.0f, 1.0f, 0.0f);
        tubeBatch.Vertex3f(vTop[index], vTop[index+1], vTop[index+2]);
    }
    
    // 下面
    GLfloat vBottom[] = {
        -50.0f, -50.0f, fZ,
        -50.0f, -50.0f, bZ,
        50.0f, -50.0f, bZ,
        50.0f, -50.0f, fZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(0.0f, -1.0f, 0.0f);
        tubeBatch.Vertex3f(vBottom[index], vBottom[index+1], vBottom[index+2]);
    }
    
    // 左面
    GLfloat vLeft[] = {
        50.0f, 50.0f, fZ,
        50.0f, -50.0f, fZ,
        50.0f, -50.0f, bZ,
        50.0f, 50.0f, bZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(1.0f, 0.0f, 0.0f);
        tubeBatch.Vertex3f(vLeft[index], vLeft[index+1], vLeft[index+2]);
    }
    
    // 右面
    GLfloat vRight[] = {
        -50.0f, 50.0f, fZ,
        -50.0f, 50.0f, bZ,
        -50.0f, -50.0f, bZ,
        -50.0f, -50.0f, fZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        tubeBatch.Normal3f(-1.0f, 0.0f, 0.0f);
        tubeBatch.Vertex3f(vRight[index], vRight[index+1], vRight[index+2]);
    }
    tubeBatch.End();
}

void SetupInnerBatch() {
    innerBatch.Begin(GL_QUADS, 40);
    innerBatch.Color4f(0.75f, 0.75f, 0.75f, 1.0f);
    // 上面
    GLfloat innerTop[] = {
        -35.0f, 35.0f, fZ,
        35.0f, 35.0f, fZ,
        35.0f, 35.0f, bZ,
        -35.0f, 35.0f, bZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        innerBatch.Normal3f(0.0f, -1.0f, 0.0f);
        innerBatch.Vertex3f(innerTop[index], innerTop[index+1], innerTop[index+2]);
    }
    
    // 下面
    GLfloat innerBottom[] = {
        -35.0f, -35.0f, fZ,
        -35.0f, -35.0f, bZ,
        35.0f, -35.0f, bZ,
        35.0f, -35.0f, fZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        innerBatch.Normal3f(0.0f, 1.0f, 0.0f);
        innerBatch.Vertex3f(innerBottom[index], innerBottom[index+1], innerBottom[index+2]);
    }
    
    // 左面
    GLfloat innerLeft[] = {
        35.0f, 35.0f, fZ,
        35.0f, -35.0f, fZ,
        35.0f, -35.0f, bZ,
        35.0f, 35.0f, bZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        innerBatch.Normal3f(-1.0f, 0.0f, 0.0f);
        innerBatch.Vertex3f(innerLeft[index], innerLeft[index+1], innerLeft[index+2]);
    }
    
    // 右面
    GLfloat innerRight[] = {
        -35.0f, 35.0f, fZ,
        -35.0f, 35.0f, bZ,
        -35.0f, -35.0f, bZ,
        -35.0f, -35.0f, fZ
    };
    for(int i = 0; i < 4; i++) {
        int index = i * 3;
        innerBatch.Normal3f(1.0f, 0.0f, 0.0f);
        innerBatch.Vertex3f(innerRight[index], innerRight[index+1], innerRight[index+2]);
    }
    
    innerBatch.End();
}

// 程序初始化环境
void SetupRC() {
    // 设置背景颜色为淡蓝色
    glClearColor(0.0f, 0.0f, 0.75f, 1.0f);
    
    // 开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    // 着色器初始化
    shaderManager.InitializeStockShaders();
    
    // 物体向里面移动 450 距离
    viewFrame.MoveForward(450.0f);
    
    // 创建长方体暴露在外面的红色面
    SetupTubeBatch();
    
    // 创建长方体里面的灰色面
    SetupInnerBatch();
}

void SpecialKeys(int key, int x, int y) {
    // 按下上、下、左、右方向键,对物体进行旋转,m3dDegToRad = 角度 -> 弧度
    switch(key) {
        case GLUT_KEY_UP:
            // angle=-5, x=1, y=0, z=0 表示绕x轴正方向顺时针旋转(从x轴正方向看去)
            viewFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_DOWN:
            // angle=5, x=1, y=0, z=0 表示绕x轴正方向逆时针旋转(从x轴正方向看去)
            viewFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
            break;
        case GLUT_KEY_LEFT:
            // angle=-5, x=0, y=1, z=0 表示绕y轴正方向顺时针旋转(从y轴正方向看去)
            viewFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
            break;
        case GLUT_KEY_RIGHT:
            // angle=5, x=0, y=1, z=0 表示绕y轴正方向逆时针旋转(从y轴正方向看去)
            viewFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
            break;
    }
    
    // 重写渲染窗口
    glutPostRedisplay();
}

// 窗口变换回调
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, 1000.0f);
    
    // 保持透视投影矩阵
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    // 利用变换管线,管理2个堆栈
    transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}

// 程序入口
int main(int argc, char* argv[]) {
    // 针对 Mac OS 设置工作目录路径
    gltSetWorkingDirectory(argv[0]);
    
    // 初始化 GLUT
    glutInit(&argc, argv);
    
    // 初始化渲染模式
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    
    // 初始化窗口大小
    glutInitWindowSize(800, 720);
    
    // 创建窗口并命名
    glutCreateWindow("Perspective Projection Example");
    
    // 检测驱动程序是否初始化成功
    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();
    
    return 0;
}

效果图如下所示:

 

 

 

 

 

posted @ 2021-05-02 21:02  码出境界  阅读(313)  评论(0编辑  收藏  举报