岛马游戏编程之路(二)

 很久没更了,放寒假学习积极性就是不高……《3D数学基础:图形与游戏开发》本来想赶紧看完的到现在也就看了一大半,今天写博客的主要目的是想给“物体—世界坐标系和世界—物体坐标系变换”这块儿做个笔记。
 首先上一篇博客我说过变换坐标系需要用到蜜汁矩阵乘法,虽然经过了半个月的马马虎虎的学习,算是了解了一些蜜汁的过程,但总体上还是没有大概念,所以感觉有必要做一些整理。
 根据书上的规定,先要有以下概念:

  1. 物体坐标系和世界坐标系都好懂,但惯性坐标系是干什么的?具体在3.2.4节(简化操作)
  2. 由3.5节和8.1节,变换物体意味着变换(旋转平移缩放…)物体上所有的点,这些点将被移动到一个新的位置,我们使用同一坐标系来描述变换前和变换后点的位置。变换坐标系时,物体上的点实际没有移动,我们只是在另一个坐标系中描述它的位置而已。变换物体上所有点等价于变换坐标系来重新描述物体上的所有点,它们互为相反的量
  3. 本书使用左手坐标系和行向量,本书的变换矩阵公式讨论的是变换物体(上所有点)

有了这些概念,再让我们回顾一下7.2节矩阵是怎么一回事。

  1. 矩阵的每一行都能解释为转换后的基向量
  2. 为了将向量(点)从原坐标系变换到新坐标系,需要用它乘以一个矩阵

沿用上一个博客的例子,比如把一个人的模型直直的放在世界坐标系里,对他进行一些操作会比较直观。这里写图片描述
但如果他变成了这样,那用世界坐标系里的位置来操作他显然不如自身的物体坐标系直观。这里写图片描述![这里写图片描述
所以假如在世界坐标系里小人右手的坐标为p=[a b c],现在想把小人沿右胳膊方向移动一个胳膊的距离,就可以用旋转矩阵R来把p从世界坐标系变换到物体坐标系,由本书的规定的矩阵乘法,pR=[x y z]就是小人右手经过变换坐标系后在它自身物体坐标系里的坐标了(这个旋转矩阵,就在该例没有平移的情况下,可以称作世界—物体坐标系变换矩阵),这时想移动小人就很简单了,直接把全身的坐标都加[x y z]即可,是不是很直观了呢?
这里写图片描述
但是有一个问题,这样还是很笼统,旋转矩阵R究竟是什么?
  从世界坐标系(蓝色)看小人右手的坐标为(a,b,c),从物体坐标系(红色)看右手的坐标则是(x,y,z),但是物体本身并没有动,只是通过矩阵乘法变换了坐标系来重新描述了该点的位置而已,当然这是从变换坐标系角度来构造旋转矩阵R(从z轴正方向看,假设坐标系逆时针旋转了π/6,但由于本书变换矩阵的公式讨论的都是变换物体,可以看出,从变换后的物体坐标系看小人和把小人以z轴正方向顺时针旋转-π/6的原世界坐标系看小人是一样的,这样就得出了由8.2.2节公式8.4计算的旋转矩阵R=Rz(-π/6))
  所以说变换物体上所有点等价于变换坐标系来重新描述物体上的所有点,它们互为相反的量。这跟你开着车向前走,世界在向后走一个道理。
  可是还有一个问题,你可以发现我们的小人一直在原点,可实际情况中小人会在世界坐标系的任何地方,比如
  这里写图片描述
这里写图片描述
这里写图片描述
  这时候仅3X3旋转矩阵就不好用了,因为它无法表示平移。这时需要的就是9.4.2节的4X4齐次矩阵M,它相当于两个矩阵,一个旋转矩阵R一个平移矩阵T,且是先旋转后平移,即M=RT。


  有了上面这些之后,就可以来看看11.5节的代码了,来看看旋转矩阵R究竟是什么。
  我们先看世界—物体坐标系的转换

void setupParentToLocal(const Vector3 &pos, const EulerAngles &orient);
void setupParentToLocal(const Vector3 &pos, const RotationMatrix &orient);

首先一点就是变换不是凭空就能完成的,像上面必须提前知道小人右手p在世界坐标系中的坐标(a,b,c)一样(不同的是上面只把右手当作一个点p来处理,而一个物体的角度可以是多样的,所以不光要给出物体的位置,还要给出方向),需要给出物体坐标空间在世界坐标空间的位置和方向,位置用的是向量Vector3来表示,方向则用的欧拉角来表示。
  关于欧拉角的其他东西就不说了,具体可见10.3节。但有一点需要明白,欧拉角的三个量h,p,b通过按次序旋转y轴,x轴,z轴,就可以把物体(和它的坐标空间)从惯性坐标系变换到物体坐标系,因为它们就相当于Ry*Rx*Rz矩阵乘法的组合,所以欧拉角可以描述物体相对于父坐标空间的方位。再由10.6.1节的从欧拉角转换到矩阵的公式,来得到相应的旋转矩阵R。和上面讨论的一样,需要注意的一点是,本书中的公式是变换物体的,所以需要用相反的旋转量来计算惯性—物体坐标系的旋转矩阵R,即R=Ry(-h)*Rx(-p)*Rz(-b),进而得到以下代码:
  

void Matrix4x3::setupParentToLocal(const Vector3 &pos, const EulerAngles &orient){
    RotationMatrix orientMatrix;
    orientMatrix.setup(orient);
    setupParentToLocal(pos, orientMatrix);
}

其中的orientMatrix.setup(orient);就是用的10.6.1节的公式10.21。这样就可以找到4X4齐次矩阵中的旋转矩阵R了,R=orientMatrix。现在只需要找到平移矩阵T即可,因为参数pos和orient是父坐标空间的坐标,且pos就代表了物体在父坐标空间的位置,即距离原点的平移。所以由上面提到的4X4齐次矩阵先旋转,后平移,父—局部坐标系平移矩阵T的tx,ty,tz分量就是负的经过旋转后的位置的三个分量pos.x,pos.y,pos.z了。
这里写图片描述
至于为什么是负的,像下图所示,局部坐标系里哪有平移,物体就在原点上。这里写图片描述
所以下面的代码就不难理解了:

void Matrix4x3::setupParentToLocal(const Vector3 &pos, const RotationMatrix &orient){
    m11 = orient.m11; m12 = orient.m12; m13 = orient.m13;
    m21 = orient.m21; m22 = orient.m22; m32 = orient.m23;
    m31 = orient.m31; m32 = orient.m32; m33 = orient.m33;
    tx = -(pos.x*m11 + pos.y*m21 + pos.z*m31);
    ty = -(pos.x*m12 + pos.y*m22 + pos.z*m32);
    tz = -(pos.x*m13 + pos.y*m23 + pos.z*m33);
}

然后就是两个相反的函数了,从局部坐标空间变换到父坐标空间:

void setupLocalToParent(const Vector3 &pos, const EulerAngles &orient);
void setupLocalToParent(const Vector3 &pos, const RotationMatrix &orient);

同上面一样,pos和orient代表的也是物体坐标空间在世界坐标空间的位置和方向,然后由10.6.1节的公式10.22就可以推出局部—父坐标空间的旋转矩阵R,而它相当于撤销了父—局部坐标空间的变换(9.2.2节可逆变换即存在一个逆变换可以撤销原变换),所以此旋转矩阵R就是公式10.21的逆矩阵即转置矩阵。因为旋转矩阵是正交矩阵,正交矩阵的逆矩阵等于原矩阵的转置。我们可以得到以下代码:

void Matrix4x3::setupLocalToParent(const Vector3 &pos, const EulerAngles &orient){
    RotationMatrix orientMatrix;
    orientMatrix.setup(orient);
    setupLocalToParent(pos, orientMatrix);
}

这部分与之前并无不同,转置体现在下面这个函数里:

void Matrix4x3::setupLocalToParent(const Vector3 &pos, const RotationMatrix &orient){
    m11 = orient.m11; m12 = orient.m21; m13 = orient.m31;
    m21 = orient.m12;8 m22 = orient.m22; m32 = orient.m32;
    m31 = orient.m13; m32 = orient.m23; m33 = orient.m33;
    tx = pos.x; ty = pos.y; tz = pos.z;
}

像这样先旋转完后,就已经从局部坐标系变换到惯性坐标系了,然后因为pos就是物体坐标空间在世界坐标空间的位置,所以直接赋给平移部分tx,ty,tz就行。
  像这样写了一大顿,我也不确定是不是百分百全对,全当暂时的个人笔记了,要是有大神发现哪里有错误,还望指正。。。祝大家2018新年快乐!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值