主要内容:将公式计算得到的投影矩阵与从OpenGL中获得的投影矩阵进行比较,验证公式正确与否。
注:
1、OpenGL中矩阵以1维数组形式;
2、OpenGL中矩阵以列为主序;
3、OpenGL中矩阵乘法为矩阵乘以列向量,即如下形式:
p2 = M * p1
其中p1、p2为列向量。
投影参数:
令left=13,right=24,bottom=5,top=18,zNear=7,zFar=15,
fovy = 45, aspect = 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");
}
}