视图矩阵的推导

        把物体从世界坐标系转化到视点坐标系的矩阵称为视图矩阵。

     下面我们先看下OpenGL中视图矩阵的推导过程:

     假设视点或camera的局部坐标系为UVN,UVN分别指向右方、上方和后方从而构成右手坐标系,视点则处于局部坐标系的原点位置。

     就如OpenGL中的函数gluLookAt(eyex, eyey, eyez, lookatx, lookaty, lookatz, upx, upy, upz)一样,给定视点、观察点、以及up向量之后,我们就可以求得视图矩阵。

1、首先我们来求得N = eye – lookat,并把N归一化。

2、up和N差积得到U, U= up X N,归一化U。

3、然后N和U差积得到V

假定设定摄像机参数如下:gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);

则N=Normalize((0.0,3.0,4.0)-(0.0,0.0,0.0))=Normalize((0.0,3.0,4.0))= (0.0,0.6,0.8)

    U=up X N= (1.0*0.8-0.6*0.0,0.0*0.0-0.0*0.8, 0.0*0.6-0.0*1.0)=(0.8,0.0,0.0), 归一化后为U=(1.0,0.0,0.0)

    V=N X U=(0.0,0.8,-0.6),归一化后为(0.0,0.8,-0.6)

image

image

 

 

      假设视点坐标系初始和世界坐标系重合,它先进行一个旋转变化,然后再进行一个平移,得到现在视点位置和方位。则此时进行的矩阵变化为image,其中T是平移变化,R是旋转变化,因为OpenGL顶点坐标,使用列向量,所以我们使用逆变换。

image

T的逆矩阵为:

image

对gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);  (Tx,Ty,Tz)=(0.0,3.0,4.0)

     当相机变换进行完Inverse Translation这一步之后,相机的原点和世界原点就重合了,也就是处理完了关于平移的变换。

     我们要把一个世界坐标系点K(Kx, Ky, Kz),表示成(U,V,P)坐标系的点(假设此时,已经经过平移操作,摄像机在世界坐标系的原点),则其公式为:

因为(Lx, Ly, Lz)=(Kx,Ky,Kz)*(U,V,P),【因为世界坐标系的三个基乘以(U,V,P),就会把世界坐标系转变到uvp坐标系,所以世界坐标的顶点坐标,乘以这个矩阵,也会转化到uvp坐标系中的顶点坐标】则有

Lx = Kx * Ux + Ky * Uy + Kz * Uz;

Ly = Kx * Vx + Ky * Vy + Kz * Vz;

Lz = Kx * Px + Ky * Py + Kz * Pz

则转化矩阵为:

image

则完整的公式为:image      image

 

则gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);最终视图矩阵是image

你可以用下面的代码来验证它:

#include <stdio.h>
#include <GL/glew.h>
#include <GL/freeglut.h>
#include <stdlib.h> 
#include <cmath>

void init(void)
{
    glClearColor(0.0, 0.0, 0.0, 0.0); //背景黑色 
}

void display(void)
{
    glClear(GL_COLOR_BUFFER_BIT);
    glColor3f(1.0, 1.0, 1.0); //画笔白色 

    glLoadIdentity();  //加载单位矩阵 

    gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    glutWireCube(1.0f);
    glutSwapBuffers();
}

 

void reshape(int w, int h)
{
    glViewport(0, 0, (GLsizei)w, (GLsizei)h);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(60.0, (GLfloat)w / (GLfloat)h, 1.0, 20.0);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);
    GLdouble mv[16] = { 0 }, pv[16] = { 0 };
    glGetDoublev( GL_MODELVIEW_MATRIX, mv );
    printf("view1:%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f,%5.3f, %5.3f\n", mv[0], mv[1], mv[2], mv[3], mv[4], mv[5], mv[6], mv[7], mv[8], mv[9], mv[10], mv[11], mv[12], mv[13], mv[14], mv[15]);
    glGetDoublev(GL_PROJECTION_MATRIX, pv);

}

 


int main(int argc, char** argv)
{
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
    glutInitWindowSize(500, 500);
    glutInitWindowPosition(100, 100);
    glutCreateWindow(argv[0]);
    init();
    glutDisplayFunc(display);
    glutReshapeFunc(reshape);
    glutMainLoop();
    return 0;
}

以上是OpenGL的视图矩阵,对于D3D,由于使用行向量,左乘,以及左手坐标系,所以视图稍有不同,在D3D11教程中,曾加做过推导:

http://www.cnblogs.com/mikewolf2002/archive/2012/03/11/2390669.html

posted on 2012-11-25 15:43  迈克老狼2012  阅读(...)  评论(...编辑  收藏

导航