View矩阵详解

计算机3D图像相关技术的讨论

View矩阵详解

帖子awakening3d » 2017年 8月 27日 19:09

我们知道模型渲染前的空间变换有三步:World, View, Projection; 在模型通过 World 变换到统一的世界空间之后,下一步要根据 Camera 的方位生成 View 矩阵,变换到视图空间。这个 View 矩阵本质上是把 世界对象 先平移,再旋转;平移向量就是 Camera 的位置取反,旋转量就是 Camera 的朝向角度取反。为什么是这样呢?所谓的 视图空间 本来就是以 Camera 的位置为原点的,这样平移就很好理解了。关于旋转,想像一下生活中的摄像机,往左转时,里面的图像是不是往右转?所以角度也是取反的。

知道原理,我们构造一个 View 矩阵就很简单了:
假设 Camera 的位置为 vPosition,方位角度我们用直观的 Yaw, Pitch, Roll 表示(就是分别围绕 y, x, z 依次旋转),记作 fYaw, fPitch, fRoll. 在D3D里,代码如下:
代码: 全选
   D3DXMATRIX mt;
   D3DXMATRIX mrx, mry, mrz;

   D3DXMatrixTranslation( &mt, -vPosition.x, -vPosition.y, -vPosition.z );
   D3DXMatrixRotationX( &mrx, D3DXToRadian(-fPitch) );
   D3DXMatrixRotationY( &mry, D3DXToRadian(-fYaw) );
   D3DXMatrixRotationZ( &mrz, D3DXToRadian(-fRoll) );

   matView = mt * mry * mrx * mrz;


注意顺序是先平移,然后先 绕 Y 旋转,再是 X,最后 Z.

有时候我们只有已有的 View 矩阵,需要反推出 Camera 的位置和旋转,如何算呢?
先算逆矩阵。既然逆矩阵就是还原变换过程,那么几何意义就是上面的一系列反向,即先反向旋转,再平移。
由向量与矩阵的乘法规则我们得知,矩阵变换可拆分成 旋转 + 平移,即前面 3 x 3 元素为旋转,底下第4行为平移(在逆矩阵里刚好就是 Camera 的位置)。在D3D里,可以这样写:
代码: 全选
D3DXMATRIX matViewInv;
D3DXMatrixInverse( &matViewInv, NULL, &matView );

vPosition = D3DXVECTOR3( matViewInv._41, matViewInv._42, matViewInv._43 );


再说旋转,我们可以设想把视图空间里的 x, y, z 三个方向向量通过逆矩阵变换回世界空间,因为视图空间就是用 Camera 的方位来定义的,那变换回去后的向量就是 Camera 在世界空间中的 right, up, forward 方向。3x3矩阵与向量乘法规则为 ( v. col1, v. col2, v. col3 ),v 表示三维向量, col表示矩阵的列, v. col 为 点积。那对于轴对齐向量,比如 x(1,0,0 ),点积结果其实就是矩阵的第一行;同理,y,z 轴分别就是第二,三行,那代码就很简单了:
代码: 全选
vRight   = D3DXVECTOR3( matViewInv._11, matViewInv._12, matViewInv._13 );
vUp      = D3DXVECTOR3( matViewInv._21, matViewInv._22, matViewInv._23 );
vForward = D3DXVECTOR3( matViewInv._31, matViewInv._32, matViewInv._33 );


求出的这三个是单位向量,知道了三个方向,就能求出 Yaw, Pitch, Roll 了。

还有,其实这种规则旋转的 3x3 矩阵的 逆 就是其 转置,那( matViewInv._11, matViewInv._12, matViewInv._13 ) 就等于 ( matView._11, matView._21, matView._31 ),就是行列互换,所以只求角度的话就无需算逆矩阵了。

由上面我们看出View 矩阵 3x3部分 的每一列其实就对应 right, up, forward,如果已知 Camera 位置和三个方位轴,如何构造View矩阵?
3x3旋转部分很简单了,每一列依次放对应方位向量。最底下一行是偏移,由于这里是先旋转,偏移就不是直接的 Camera 位置了,可以看D3DXMatrixLookAtLH函数的实现:
代码: 全选
zaxis = normal(At - Eye)
xaxis = normal(cross(Up, zaxis))
yaxis = cross(zaxis, xaxis)
   
xaxis.x           yaxis.x           zaxis.x          0
xaxis.y           yaxis.y           zaxis.y          0
xaxis.z           yaxis.z           zaxis.z          0
-dot(xaxis, eye)  -dot(yaxis, eye)  -dot(zaxis, eye)  1

代码里面的 xaxis, yaxis, zaxis 就相当于 right, up, forward,eye 就是 Camera 的位置。矩阵最底下一行的偏移是 -dot(xaxis, eye),而不是直接 -eye。因为这个变换里是先旋转,再平移;eye经过变换后必须为0,我们可以设想(可以在二维中想像,同理)世界坐标中的eye的x分量本来是偏移-eye.x和原点对齐,那要是先旋转后,再偏移多少才能和原点对齐?这里eye的旋转计算是 (xaxis . eye, yaxis . eye, zaxis . eye),很明显,旋转后需要偏移 -xaxis . eye,即 -dot(xaxis, eye) 才能为 0;和单位向量xaxis点积,就是在其上的投影,画图就清楚了。


再扩展一下, 由以上可以看出,旋转一个向量是与三新轴分别做点积,结果也就是向量在 right, up, forward 上的投影;由此可导出 空间中的一个点变换到由 org原点, right, up, forward三轴构造的新坐标系的 方法:先平移 -org,再分别与 right, up, forward 点积得出新坐标系下的 三个分量。
awakening3d
网站管理员
 
帖子: 150
注册: 2010年 10月 28日 17:39

回到 学习交流

在线用户

正在浏览此版面的用户:没有注册用户 和 1 位游客

cron