视图模型转换矩阵的推导也是关于gluLookAt函数即视图转换矩阵的推导及代码实现

来源:互联网 发布:php用户注册系统源码 编辑:程序博客网 时间:2024/05/17 07:46

关于gluLookAt函数即视图转换矩阵的推导及代码实现

视图模型转换矩阵的推导也是关于gluLookAt函数即视图转换矩阵的推导及代码实现

  gluLookAt函数提供给用户完成模式变换(model-view transformation)中,在将模型坐标系转换都世界坐标系后,进行世界坐标系到照相机坐标系的转换。实际上,照相机的定位也是在世界坐标系下定义的,这里的转换,可以理解为:  从照相机的角度解释世界坐标系中物体的坐标。通过构造一个UVN坐标系来简化这一转换。

先直观感受下UVN,UVN坐标系中的照相机模型如下图所示:


  借助下图正式定义UVN相机坐标系:


   与UVN相关的概念包括:

  •            相机位置,或者叫做视点(eyepoint):  观察参考点 (View Reference Point)
  •            相机镜头方向,通过观察平面的法向量指定:   观察平面法向量VPN (View Plane Normal)
  •            相机顶部正朝向:VUV (View Up Vector)

     形象的表达为:


gluLookAt        通过指定一个视点、表面场景中心的参考点以及up向量来构造一个视变换矩阵。

这个矩阵将代表场景中心的参考点映射到-Z轴,视点映射成为原点。当使用一个特定的投影矩阵时,场景的中心就映射到视口的中心。类似地,由up向量描述的方向投影到投影平面成为+y轴,这样它在视口中向上指向。up向量必须不能与从视点到参考点的直线平行。
        
那么如何确定u-v-n坐标系呢?计算公式如下:


OpenGL中使用的相机坐标系是右手坐标系,UVN坐标系是左手坐标系。在构造实际变换矩阵的过程中,OpenGL

需要将-n轴翻转为相机坐标系的+z轴,uv轴定位相机坐标系的+x和+y轴。这与推导相机变换矩阵一文最后的结果矩阵有所不同。


      如何构造视变换矩阵?

     视变换就是在相机坐标系下解释世界坐标系下的点。而这个变换矩阵的构造,可以看做将相机坐标系变换到与原来的世界坐标系重合。而将世界坐标系变换到与相机坐标系重合,可以看做是这个所求变换的逆过程。

    将世界坐标系变换到与相机坐标系重合,实际上进行了两个步骤:  第一步将世界坐标系旋转一定角度记作变换R,再将世界坐标系平移到视点位置记作T,那么这个变换矩阵记为M=TR。要将世界坐标系的点变换到照相机坐标系下,需要使用矩阵M的逆矩阵,即: inverse(M)=inverse(R)*inverse(T)。即所求变换矩阵为inverse(M)。

平移矩阵的逆矩阵形式简单,就是取平移量(eyex,eyey,eyez)的相反数,即:


那么现在的关键是如何求出旋转矩阵R?

上面我们构造的UVN坐标系u-v-n3个基向量可以构造成矩阵:


注意这里对n轴进行了翻转,构成右手照相机坐标系。

怎么看这个矩阵A呢,矩阵A实际上代表的就是一个旋转矩阵(从矩阵形式上看出)。

旋转矩阵的一个特点就是它是正交矩阵,即有inverse(A) = transpose(A).(A^-1 = A^T)

很多教材和博客都说,这里A矩阵可以看做是将世界坐标系转换到与照相机坐标系重合时的旋转矩阵,这一点怎么理解呢?

个人理解,矩阵A第四列为0,0,0,1,可以看做是世界坐标系和照相机坐标系原点重合;矩阵前3列即变换后的基向量,那么这个基向量(都是单位向量)是如何计算出来的呢?就是通过旋转原来的世界坐标系的基向量来构造的。因此,可以说矩阵A代表的就是将世界坐标系旋转到与相机坐标系重合时的旋转矩阵R,即R = A。

则inverse(R) = inverse(A) = transpose(A)      即为:


所以gluLookAt所求变换矩阵inverse(M)为:



代码实现如下:
//计算gluLookAt矩阵  #pragma once#include <windows.h>#include "Math_3d.h"#define GLUT_DISABLE_ATEXIT_HACK#include <gl/glew.h>/*GL_BGR*/#include <gl/glut.h>#include <atlimage.h>#include <math.h> float g_RotAngle = 0.6;//旋转角度M3DMatrix44f rotateMatrix;void userInit();void display(void);void keyboardAction(unsigned char key, int x, int y);void reshape(int w, int h);//相机坐标参数M3DVector3d eye = { 2.0, 0.0, 1.8 };M3DVector3d center = { 0.0, 0.0, 0.0 };M3DVector3d vup = { 0.0, 1.0, 0.0 };void TimerFunction(int value){g_RotAngle += 1.0;glutPostRedisplay();glutTimerFunc(10, TimerFunction, 1);}int main(int argc, char **argv){glutInit(&argc, argv);//初始化GLUT  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE);glutInitWindowPosition(100, 100);glutInitWindowSize(800, 600);glutCreateWindow("gluLookAt推导及代码实现");glutReshapeFunc(reshape);glutDisplayFunc(display);glutKeyboardFunc(keyboardAction);glutTimerFunc(1, TimerFunction, 1);glutMainLoop();return 0;}//设置视图变换矩阵double类型  void m3dSetViewMatrix(M3DMatrix44d viewMatrix, M3DVector3d eye,M3DVector3d center,M3DVector3d vup){//构造n轴 M3DVector3d nvec = {center[0] - eye[0],center[1] - eye[1],center[2] - eye[2],};m3dNormalizeVector(nvec);//单位化    //n轴(相当于正z轴)m3dNormalizeVector(vup);//构造u轴 (相当于正x轴)M3DVector3d uvec;m3dCrossProduct(uvec, nvec, vup);//结果已经单位化//构造v轴 (相当于正y轴)M3DVector3d vvec;m3dCrossProduct(vvec, uvec, nvec);//结果已经单位化//设置4x4矩阵并初始化0memset(viewMatrix, 0, sizeof(double)* 16);viewMatrix[0] = uvec[0];viewMatrix[4] = uvec[1];viewMatrix[8] = uvec[2];viewMatrix[12] = -m3dDotProduct(eye, uvec);viewMatrix[1] = vvec[0];viewMatrix[5] = vvec[1];viewMatrix[9] = vvec[2];viewMatrix[13] = -m3dDotProduct(eye, vvec);//注意这行数据  viewMatrix[2] = -nvec[0];viewMatrix[6] = -nvec[1];viewMatrix[10] = -nvec[2];viewMatrix[14] = m3dDotProduct(eye, nvec);viewMatrix[15] = 1.0;}void reshape(int w, int h){glViewport(0, 0, GLsizei(w), GLsizei(h));glutFullScreen();//全屏显示glMatrixMode(GL_PROJECTION);glLoadIdentity();gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 0.1, 500.0);glMatrixMode(GL_MODELVIEW);glLoadIdentity();glClearColor(0.0, 0.0, 0.0, 0.0);glColor4f(0.1f, 0.5f, 0.8, 0.0);}//绘制回调函数  void display(void){glClear(GL_COLOR_BUFFER_BIT);//清除颜色缓存//手动构造视变换矩阵  glLoadIdentity();M3DMatrix44d viewMatrix;m3dSetViewMatrix(viewMatrix, eye, center, vup);glMultMatrixd(viewMatrix);glPushMatrix();glRotatef(g_RotAngle, 1.0, 0.0, 0.0);glLineWidth(2.0);glutWireCube(1.0);glPopMatrix();//打印当前模视变换矩阵内容  double modelViewMat[16];glGetDoublev(GL_MODELVIEW_MATRIX, modelViewMat);for (int i = 0; i < 4; i++)for (int j = 0; j < 4; j++){fprintf(stdout, "%-.4f\t", modelViewMat[i + 4 * j]);if ((j + 1) % 4 == 0) fprintf(stdout, "\n");}glFlush();glutSwapBuffers();}//键盘按键回调函数  void keyboardAction(unsigned char key, int x, int y){switch (key) {case 033:  // Escape key  case 'q': case 'Q':exit(EXIT_SUCCESS);break;case 'w':eye[2] += 0.2;glutPostRedisplay(); break;case 's':eye[2] -= 0.2; glutPostRedisplay(); break;case 'a':eye[0] += 0.2; glutPostRedisplay(); break;case 'd':eye[0] -= 0.2; glutPostRedisplay(); break;}}实现结果截图:
<img src="http://img.blog.csdn.net/20151106235345325?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />

其实现的结果与调用opengl里的gluLookAt函数的实现结果完全相同。


0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 空运舱单上的收货人错了怎么办? 被诚信贷骗了怎么办 原户主不迁户口怎么办 二手房原房主没迁走怎么办 办退工没有就失业证怎么办 消防队教导员插手商场翘柜台怎么办 调动工作原单位领导不放人怎么办 脚碰了一下肿了怎么办 六十岁的犯人身体有病怎么办 比熊肛门周围发炎怎么办 夏天肛门周围皮肤发炎怎么办 直肠息肉手术后便秘怎么办 痔疮手术后肛门洞怎么办 痔手术后肛门有个洞怎么办 直肠上长了瘤怎么办 上嘴唇没有唇峰怎么办 拉屎厕所堵了怎么办啊 小孩一拉屎就哭怎么办 上火裆部有肿块特别疼怎么办 狗狗拉屎拉不出来怎么办 肛裂大便带血怎么办 辣椒不小心弄丁丁上怎么办 小鸡躺着不动还呼吸怎么办 老人身体不舒服不敢去检查怎么办 金毛上火鼻子干怎么办 狗鼻子干裂结壳怎么办 小狗吐了没精神怎么办 狗狗又吐又拉怎么办 孕妇嘴干鼻子干怎么办 狗狗打了针皮肤变硬怎么办 狗狗得了狗瘟怎么办 金毛得了犬瘟怎么办 泰迪身上起皮怎么办 狗狗鼻子干了怎么办 泰迪生病鼻子干怎么办 痔疮手术后肛门狭窄怎么办 乳腺导管扩张奶头疼怎么办 3月的宝宝要扩肛才拉便便怎么办 怀孕期间肛门长痔疮怎么办 射精后检查尿液发现蛋白尿怎么办 铁距孔被小孩拿体温计塞了怎么办