视图矩阵的推导
把物体从世界坐标系转化到视点坐标系的矩阵称为视图矩阵。
下面我们先看下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)
假设视点坐标系初始和世界坐标系重合,它先进行一个旋转变化,然后再进行一个平移,得到现在视点位置和方位。则此时进行的矩阵变化为,其中T是平移变化,R是旋转变化,因为OpenGL顶点坐标,使用列向量,所以我们使用逆变换。
T的逆矩阵为:
对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
则转化矩阵为:
则gluLookAt(0.0, 3.0, 4.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0);最终视图矩阵是
你可以用下面的代码来验证它:
#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