前提:opengl使用右手坐标系,向量列向量,所有矩阵变换均是右乘的形式。
V4*1 =M4*4 * V4*1
参加之前的一篇blog:http://blog.youkuaiyun.com/dizuo/archive/2008/04/17/2302364.aspx
一、理论
父坐标系:
(x,y,z)T是父坐标系中任意一点,三个坐标基: (1,0,0), (0,1,0), (0,0,1)。
孩子坐标系:
原点在父坐标系的坐标为:P = (Px,Py,Pz)T ,三个坐标基为Right,Up,Look,缩写为:R,U,L, (x',y',z')T点是孩子坐标系中的任意一点。
1)公式的数学推导
将孩子坐标系的(x',y',z')T点转化到父坐标系中:
笔误:
- 转化到齐次坐标系以后,应该还是1*4的行向量左乘以4*4的变换矩阵。上图底部最后两部应该是(x,y,z,1) 乘矩阵。。
- 最后一个等式 44矩阵第一列的前三行应该分别是Rx, Ry, Rz
下面是转化成齐次坐标后的结果:
上面推理采用行向量,opengl是列向量优先,原理类似。
2)公式的直观理解:4*4矩阵可以分为3*3的旋转矩阵RotMat和一个3*1的平移向量TransVec:
[RT, UT, LT], [-RT.dot(P), -UT.dot(P), -LT.dot(P) ]
3*3的矩阵将父坐标系的三个坐标轴旋转成R,U,L:
(1,0,0) *RotMat=(Rx, Ry, Rz)
(0,1,0) *RotMat=(Ux, Uy, Uz)
(0,0,1)*RotMat=(Lx, Ly, Lz)
根据上面三个等式也可以确定RotMat = [RT, UT, LT],将RotMat扩展为4*4的矩阵:
M44 =
该矩阵可以把父坐标系中的P点变换为孩子坐标系的原点,即:
(Px, Py, Pz,1) * M44 = (0,0,0,1)
进而解方程得出:
a = -P.dot(R)
b = -P.dot(U)
c = -P.dot(L)
二、代码实现
opengl中的gluLookAt函数:eye,target,up
对比最终的Matrix4矩阵和第一部分中的推导,会发现第三列差个符号:因为opengl中视点必须位于z轴负方向,视线方向为-z方向,所以最终4*4矩阵中L分量取负,而LT.dot(P) 平移分量为正。
代码中返回的矩阵,与第一章中给出的矩阵不太一样,正好取了一个转置矩阵。因为opengl的列优先向量。
三、应用
1,世界坐标系变换到视点坐标系:
opengl中gluLookAt函数:
gluLookAt(eyex, eyey, eyez, centerx, centery, centerz, upx, upy, upz)
计算视点坐标系:
Look向量为:(centerx-eysx, centery-eyey,centerz-eyez)T,单位化Look;
Up单位化
Right = Cross( Look, UP )
再从新计算一次:Up = Cross(Right, Look)
gluLookAt函数定义:http://www.opengl.org/sdk/docs/man/xhtml/gluLookAt.xml
2,场景中层次坐标系转化,世界坐标系和局部坐标系的转化,一个M44矩阵搞定。
History
2017-6-21 修正齐次坐标的结果