【OpenGL】正交投影和透视投影矩阵(二) —— 结合OpenGL代码验证

主要内容:将公式计算得到的投影矩阵与从OpenGL中获得的投影矩阵进行比较,验证公式正确与否。

注:

1OpenGL中矩阵以1维数组形式;

2OpenGL中矩阵以列为主序;

3OpenGL中矩阵乘法为矩阵乘以列向量,即如下形式:

p2 = M * p1

其中p1、p2为列向量。

投影参数:

令left=13,right=24,bottom=5,top=18,zNear=7,zFar=15,

fovy = 45aspect = 2

分别调用以下四个函数:

void glOrtho(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);

void gluOrtho2D(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);

void glFrustum(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);

void gluPerspective(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);

比较OpenGL正交投影矩阵”以及“利用公式计算出的正交投影矩阵”:

测试程序输出结果如下图所示,可以看到利用公式和从OpenGL中获得的矩阵一致:

glOrthoMat:利用glGet函数从OpenGL获得的正交投影矩阵

funOrthoMat:利用公式计算出的正交投影矩阵

glProjMat:利用glGet函数从OpenGL获得的透视投影矩阵

funProjMat:利用公式计算出的透视投影矩阵

 

测试程序需要的glut库下载地址:https://download.csdn.net/download/u012633319/10379891

测试程序下载地址:https://download.csdn.net/download/u012633319/10379989

测试程序:(为方便理解,存在重复代码)

#include <stdio.h>
#include <math.h>
#include <glut.h>

// glut回调函数:窗口尺寸改变时调用,在此调用下面四个计算投影矩阵的函数
void changeSize(int w, int h);
// glut回调函数:每次绘制窗口调用,在此仅为程序完整性使用,无需注意
void renderScene();

// -----------------【四个计算投影矩阵的函数】-------------------------
void glOrtho_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);  // 正交投影
void gluOrtho2D_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top);                              // 正交投影
void glFrustum_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar);// 透视投影
void gluPerspective_Mat(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar);                         // 透视投影

// 程序入口
int main(int argc, char** argv)
{
    // --------------glut库函数设定部分-------------------
    glutInit(&argc, argv);                          // glut库初始化
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGBA);   // 设置显示模式:单缓冲区、rgba像素格式
    glutInitWindowSize(200, 200);                   // 设置窗口初始窗口尺寸:200 X 200
    glutCreateWindow("Calculate Projection Mat");   // 创建窗口,窗口标题:Calculate Projection Mat
    glutReshapeFunc(changeSize);                    // 设置窗口改变尺寸时的回调函数
    glutDisplayFunc(renderScene);                   // 设置窗口绘制的回调函数

    glutMainLoop();                                 // 开启消息循环,简单理解为显示窗口

    return 0;
}

// 窗口尺寸改变时的回调函数,在此调用四个计算投影矩阵的函数
void changeSize(int w, int h)
{
    // 用num标识计算次数
    static int num = 0;
    num += 1;
    printf("---------------------------【第%d次】-----------------------------\n", num);

    // 设置计算投影矩阵需要的几个参数
    static GLdouble left   = 13, right  = 24;
    static GLdouble bottom = 5,  top    = 18;
    static GLdouble zNear  = 7,  zFar   = 15;
    static GLdouble fovy   = 45, aspect = 2;
    left += 2, right += 2;
    bottom+=2, top +=2;
    zNear +=2, zFar +=2;
    fovy = (fovy+2)<180 ? (fovy+2) : fovy; // 当fovy等于180度时,相当于视野无限大
    aspect += 0.1;

    glOrtho_Mat(left, right, bottom, top, zNear, zFar);
    gluOrtho2D_Mat(left, right, bottom, top);
    glFrustum_Mat(left, right, bottom, top, zNear, zFar);
    gluPerspective_Mat(fovy, aspect, zNear, zFar);
}
// 绘制回调函数
void renderScene()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glFlush();
}

// gl的核心函数:glOrtho计算正交投影矩阵
void glOrtho_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
{
    glMatrixMode(GL_PROJECTION); // 设定矩阵模式
    glLoadIdentity();            // 使当前投影矩阵为单位矩阵,免受以前投影矩阵的影响
    glOrtho(left, right, bottom, top, zNear, zFar); // 调用glOrtho投影函数
    // 利用glGet函数获得OpenGL正交投影矩阵
    GLfloat glOrthoMat[16];
    glGetFloatv(GL_PROJECTION_MATRIX, glOrthoMat);

    // 利用正交投影矩阵公式计算正交投影矩阵
    GLfloat funOrthoMat[16];
    funOrthoMat[0]  =  2 / (right-left);
    funOrthoMat[1]  =  funOrthoMat[2] = funOrthoMat[3] = 0;
    funOrthoMat[5]  =  2 / (top-bottom);
    funOrthoMat[4]  =  funOrthoMat[6] = funOrthoMat[7] = 0;
    funOrthoMat[10] = -2 / (zFar-zNear);
    funOrthoMat[8] = funOrthoMat[9] = funOrthoMat[11] = 0;
    funOrthoMat[12] = -(right+left) / (right-left);
    funOrthoMat[13] = -(top+bottom) / (top-bottom);
    funOrthoMat[14] = -(zFar+zNear) / (zFar-zNear);
    funOrthoMat[15] = 1;

    // 控制台输出结果:比较“OpenGL正交投影矩阵”以及“利用公式计算出的正交投影矩阵”
    // “OpenGL正交投影矩阵”
    printf("glOrtho_Mat()-----------------------------\n");
    printf(" left \tright \tbottom \ttop \tzNear \tzFar\n");
    printf(" %.2f \t%.2f  \t%.2f   \t%.2f\t%.2f  \t%.2f\n", left, right, bottom, top, zNear, zFar);
    printf("\tglOrthoMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", glOrthoMat[i*4+j]);
        }
        printf("\n");
    }
    // “利用公式计算出的正交投影矩阵”
    printf("\tfunOrthoMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", funOrthoMat[i*4+j]);
        }
        printf("\n");
    }
}
// glu函数:gluOrtho2D计算正交投影矩阵,此时 zNear = -1, zFar = 1,相当于z方向已经规范化
void gluOrtho2D_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top)
{
    // 设定矩阵模式
    glMatrixMode(GL_PROJECTION);
    // 使当前投影矩阵为单位矩阵,免受以前投影矩阵的影响
    glLoadIdentity();
    // 调用gluOrtho2D投影函数
    gluOrtho2D(left, right, bottom, top);
    // 利用glGet函数获得OpenGL正交投影矩阵
    GLfloat glOrthoMat[16];
    glGetFloatv(GL_PROJECTION_MATRIX, glOrthoMat);

    // 利用正交投影矩阵公式计算正交投影矩阵
    GLfloat funOrthoMat[16];
    funOrthoMat[0]  =  2 / (right-left);
    funOrthoMat[1]  =  funOrthoMat[2] = funOrthoMat[3] = 0;
    funOrthoMat[5]  =  2 / (top-bottom);
    funOrthoMat[4]  =  funOrthoMat[6] = funOrthoMat[7] = 0;
    funOrthoMat[10] = -1;
    funOrthoMat[8] = funOrthoMat[9] = funOrthoMat[11] = 0;
    funOrthoMat[12] = -(right+left) / (right-left);
    funOrthoMat[13] = -(top+bottom) / (top-bottom);
    funOrthoMat[14] = 0;
    funOrthoMat[15] = 1;

    // 控制台输出结果:比较“OpenGL正交投影矩阵”以及“利用公式计算出的正交投影矩阵”
    // “OpenGL正交投影矩阵”
    printf("glOrtho2D_Mat()-----------------------------\n");
    printf(" left \tright \tbottom \ttop\n");
    printf(" %.2f \t%.2f  \t%.2f   \t%.2f\n", left, right, bottom, top);
    printf("\tglOrthoMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", glOrthoMat[i*4+j]);
        }
        printf("\n");
    }
    // “利用公式计算出的正交投影矩阵”
    printf("\tfunOrthoMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", funOrthoMat[i*4+j]);
        }
        printf("\n");
    }
}
// gl的核心函数:glFrustum计算透视投影矩阵
void glFrustum_Mat(GLdouble left, GLdouble right, GLdouble bottom, GLdouble top, GLdouble zNear, GLdouble zFar)
{   
    // 设定矩阵模式
    glMatrixMode(GL_PROJECTION);
    // 使当前投影矩阵为单位矩阵,免受以前投影矩阵的影响
    glLoadIdentity();
    // 调用glFrustum投影函数
    glFrustum(left, right, bottom, top, zNear, zFar);
    // 利用glGet函数获得OpenGL透视投影矩阵
    GLfloat glProjMat[16];
    glGetFloatv(GL_PROJECTION_MATRIX, glProjMat);

    // 利用透视投影矩阵公式计算正交投影矩阵
    GLfloat funProjMat[16];
    funProjMat[0]  =  2*zNear / (right-left);
    funProjMat[1]  =  funProjMat[2] = funProjMat[3] = 0;
    funProjMat[5]  =  2*zNear / (top-bottom);
    funProjMat[4]  =  funProjMat[6] = funProjMat[7] = 0;
    funProjMat[8]  = (right+left) / (right-left);
    funProjMat[9]  = (top+bottom) / (top-bottom);
    funProjMat[10] = -(zFar+zNear) / (zFar-zNear);
    funProjMat[11] = -1;
    funProjMat[12] = funProjMat[13] = funProjMat[15] = 0;
    funProjMat[14] = -2*zFar*zNear / (zFar-zNear);

    // 控制台输出结果:比较“OpenGL透视投影矩阵”以及“利用公式计算出的透视投影矩阵”
    // “OpenGL透视投影矩阵”
    printf("glFrustum_Mat()-----------------------------\n");
    printf(" left \tright \tbottom \ttop \tzNear \tzFar\n");
    printf(" %.2f \t%.2f  \t%.2f   \t%.2f\t%.2f  \t%.2f\n", left, right, bottom, top, zNear, zFar);
    printf("\tglProjMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", glProjMat[i*4+j]);
        }
        printf("\n");
    }
    // “利用公式计算出的透视投影矩阵”
    printf("\tfunProjMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", funProjMat[i*4+j]);
        }
        printf("\n");
    }
}
// glu函数:gluPerspective计算透视投影矩阵
void gluPerspective_Mat(GLdouble fovy, GLdouble aspect, GLdouble zNear, GLdouble zFar)
{
    // 设定矩阵模式
    glMatrixMode(GL_PROJECTION);
    // 使当前投影矩阵为单位矩阵,免受以前投影矩阵的影响
    glLoadIdentity();
    // 调用gluPerspective投影函数
    gluPerspective(fovy, aspect, zNear, zFar);
    // 利用glGet函数获得OpenGL透视投影矩阵
    GLfloat glProjMat[16];
    glGetFloatv(GL_PROJECTION_MATRIX, glProjMat);

    // 利用透视投影矩阵公式计算正交投影矩阵
    GLfloat funProjMat[16];
    funProjMat[0]  =  2*zNear / (2*tan(fovy/2/180*3.1415926)*zNear*aspect);
    funProjMat[1]  =  funProjMat[2] = funProjMat[3] = 0;
    funProjMat[5]  =  2*zNear / (2*tan(fovy/2/180*3.1415926)*zNear);
    funProjMat[4]  =  funProjMat[6] = funProjMat[7] = 0;
    funProjMat[8]  = funProjMat[9]  = 0;
    funProjMat[10] = -(zFar+zNear) / (zFar-zNear);
    funProjMat[11] = -1;
    funProjMat[12] = funProjMat[13] = funProjMat[15] = 0;
    funProjMat[14] = -2*zFar*zNear / (zFar-zNear);

    // 控制台输出结果:比较“OpenGL透视投影矩阵”以及“利用公式计算出的透视投影矩阵”
    // “OpenGL透视投影矩阵”
    printf("gluPerspective_Mat()-----------------------------\n");
    printf(" fovy \taspect \tzNear \tzFar\n");
    printf(" %.2f \t%.2f   \t%.2f  \t%.2f\n", fovy, aspect, zNear, zFar);
    printf("\tglProjMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", glProjMat[i*4+j]);
        }
        printf("\n");
    }
    // “利用公式计算出的透视投影矩阵”
    printf("\tfunProjMat---------------------------\n");
    for(int i=0; i<4; i++)
    {
        printf("\t\t");
        for(int j=0; j<4; j++)
        {
            printf("%8.4f, ", funProjMat[i*4+j]);
        }
        printf("\n");
    }
}

猜你喜欢

转载自blog.csdn.net/u012633319/article/details/80114381