一、变换的前提
考虑坐标变换时候,一定要分清楚几个前提条件:
1.当前是否需要进行变换,通过一些相对位置是否能够求取出来; 通过中间位置(屏幕位置)得到结果,或者通过方便的函数计算出来,不需要自己计算,如UnityEngine.RectTransformUtility.ScreenPointToLocalPointInRectangle。
2.当前是左手坐标系还是右手坐标系。(cocos2dx中用的就是右手坐标系,而u3d中世界坐标系是左手坐标系),不同坐标系统,z轴定义不同基于观察坐标系的左右翻转,旋转角度不同,绕序正面不同,矩阵是行矩阵还是列矩阵定义不同,向量乘以矩阵顺序不同,四元数乘法规则和乘法顺序不同。
3.当前是变换物体,还是变换坐标系,变换坐标系也要基于变换物体的逆来考虑。
4.变换的顺序,连续变换的顺序,基于父节点要先进行缩放->旋转->平移才能得到正确的结果。
二、理论层面理解
1.不同坐标体系,旋转定义规则,不同矩阵定义规则,四元数乘法定义规则,变换会不同。
2.简化变换的思想
:
使用
惯性坐标系
(惯性坐标系是世界坐标系平移到物体坐标系的位置)可以简化物体-世界,世界-物体,或者说任何两个坐标系间的转换做一个中间的转换,使问题变得简单。使用
嵌套坐标系(父坐标系-子坐标系-子坐标系)也可以分解问题,简化复杂的变换逻辑。
其实把问题简单化思考的方法,不外乎实例化,拆分它,转换它,再组合转换抽象回来,就可以很elegant的解决问题了。
很多物体间的方位和运动,需要在同一个坐标系中才能进行,以及渲染管道中本身要求多坐标系间转换来实现渲染
。
3.物体变换和坐标系变换的联系区别,变换坐标系只是理论上的,一般都要转换到变换物体:
将物体进行变换(例如把物体坐标系当做世界坐标系而不变,那么物体进行世界坐标系看到的要求进行缩放旋转平移变换),等价于相反的顺序相反的量变换物体坐标系(例如把物体定住不变,那么需要坐标系进行相反的先相反方向平移相反方向旋转的变换)。其实变换了坐标系也会将坐标内所有的物体重新计算位置, 只是有时候那种更加合适那么使用那种变换,一般的是思考问题时是变换物体,但是实际中是变换物体坐标系(假定物体不变),因为变换坐标系更加方便处理多个坐标系间的变换,最终物体只需要重新计算位置一次。
4.嵌套坐标系,针对坐标系间的双向变换:
物体到世界(因为都是相对本地世界坐标系的,而不是嵌套坐标系)要先进行缩放(改变轴大小,受轴走向和原点影响)旋转(改变轴走向,受原点影响,不受轴大小影响)后进行平移(改变原点,不受轴大小和轴走向影响),世界到物体先进行相反的平移后进行相反的旋转和缩放,其它坐标系间相互转换也符合该规律。相反的顺序主要是因为矩阵的组合变换求逆的要求。
三、工业领域中的实际应用理解:
1.物惯变换:在一个坐标系中变换物体的变换顺序,物体到世界,参考坐标系是惯性坐标系(世界),无论是S,R,T矩阵的值都是相对于惯性坐标系(世界)的,所以顺序要是S*R*T才能得到正确变换。
单纯的变换坐标系实现物体变换只是理论上的(视图变换也是基于物体变换求逆实现的),具体还是要变换物体来实现变换,进行逆变换即可。
2.惯物变换:世界坐标系变换到物体坐标系,变换了坐标系,也就是参考坐标系是物体坐标系的变换,那么做物体到世界的逆变换即可。
M=T^-1 * R^-1 * S^-1。因为最终是物体向量V*M矩阵变换,虽然纯粹的变换坐标系来实现变换物体也是可行的,但是还是转换到变换物体。
3.嵌套坐标系变换,变换矩阵值是相对于不同的参考坐标系的要区分清楚,嵌套坐标系中每个子节点的变换是相对于父节点作为原点来描述的,且描述的是子节点到父节点(世界)的变换,所以子节点上的物体要转换到父节点按照Vparent= Vchild*Schild*Rchild + Tchild即可,得到相对于父节点的位置Vparent, 然后对Vparent * Sparent * Rparent + Tparent得到上层的坐标系。
一般是严格按照缩放->旋转->平移得到节点的父节点位置的(U3D中也是),
但如果需要转换到父节点的不是节点上面的物体或者子节点而是节点位置本身,节点位置本身不受自身S,R影响,将它进行父坐标的转换即可,例如U3D中,求取Transform child的pos位置在世界坐标系中的位置,因为它的S,R只是影响它上面的物体和子节点,因此S,R不用考虑,故子节点在父节点的父节点上的位置(假如为世界) = Transform Child pos * Transform parent Scale * Transform parent Rotation + Transform parent Pos。
如果是嵌套坐标系中的逆变换,那么用相反的顺序变换的逆,变换即可。
4.复杂变换实例,骨骼动画中的变换, 相对位置应用(参考坐标系改变),中间变换层(嵌套坐标系),变换是在左手还是右手坐标系,四元数的理解,连续变换的在不同乘法规则下的乘法顺序:
世界坐标系中变换骨骼矩阵整体表达:Mskin-world * (Mbone-world ^ -1) * Mcurrentbone-world的顺序来变换皮肤网格顶点。Mskin-world * (Mbone-world ^ -1)得到网格相对于骨骼坐标系的相对位置是恒定的只需要计算一次,Mcurrentbone-world骨骼在世界坐标系中的变换由关键帧指定,每帧插值变化,因此网格也要每帧计算。具体网格的位置还要涉及网格相对于骨骼的权重计算。
(1).变换在物体坐标系和坐标体系中的理解:物体连续变换和坐标系变换差异,不同坐标系叉乘和四元数旋转不一样,观察相对位置不同。 骨骼父子节点关系中四元数旋转变换理解,左手坐标系如果不变换标准乘法那么需要(ba) v(ba)-1从后到先来旋转, 右手坐标系在标准乘法下顺时针变则需要(ab)-1v(ab)连续变换即可。 世界坐标系中变换骨骼矩阵整体表达:Mskin-world * (Mbone-world ^ -1) * Mcurrentbone-world的顺序来变换皮肤网格顶点。
(2).父子节点变换传递性:父子骨骼节点中的整体变换,可以分解为缩放旋转平移,只有缩放旋转可传递(累加变换),平移不传递(不累加变换,只加结果)。 无论变换子节点还是绑定在节点中的网格顶点变换顺序都是先缩放在旋转,最后平移。
(3).蒙皮权重和相对位置:顶点附加到骨骼节点之前还有一个权重,需要累加。在OGRE中动画集合(状态)下面每个动画有多个骨骼节点轨迹,每个节点轨迹有多个keyFrame, 一个动画的一瞬间就有多个动画节点轨迹(其实是一个frame)确定整体骨骼节点位置。OGRE中的蒙皮附加到骨骼节点,骨骼节点的变换是 相对于根骨骼节点的也就是相对于Local Model Space的,没有用相对于世界坐标系,目的是为了插入中间变换层,方便多个实体对象共用 一套相同的骨骼变换。
(4).四元数新的理解: 虽然四元数的逆,无论是轴取反还是旋转取反,结果都是(w,-v),但是在变换向量时候,(w,-v)和(-w,v)等价的,证明见: (-w1,v1) x (-w1,-v1) = (w1w1 + v1v1, w1v1 -w1v1 - v1xv1); (w1,-v1) x (w1,v1) = (w1w1 + v1v1, w1v1 - w1v1 -v1xv1) 故在.md5mesh和.md5anim存放的四元数,为了配合骨骼节点右手坐标系中顺时针连续变换(ab)-1v(ab),则四元数取反为: Quaternion MD5Model::buildQuat(float x, float y, float z) const { float w = 1.0f - xx - yy - z*z; w = w < 0.0 ? 0.0f : (float)-sqrt(double(w)); Quaternion q(x, y, z, w);// 等价于w= sqrt(w)时候的q(-x, -y, -z, w) q.normalize();
return q;
}
5.变换的技巧
// 矩阵就是:老的坐标轴基向量,经过怎样的分解到新的坐标基向量(该新基向量分配到原来各坐标轴上),这样分解的组合构成了一个坐标变换,例如绕Y轴的旋转。
而坐标系都是相对的,可以任意定,可以从源坐标系到目标坐标系,目标坐标系到源坐标系, 空间是相对于坐标系的刻画。
变换之间关系:
// 摄像机和屏幕分辨率,不同摄像机用屏幕坐标系作为中介:不同摄像机坐标系间找到中间等价值:坐标系的对应是将世界坐标系的转换到屏幕坐标系,ui Input操作其实是和uiCamera对应的;
然后以屏幕坐标系为中间等价变量,转换到ui上的坐标位置(用世界坐标或者屏幕设计坐标定位)。
// 利用坐标系之间映射变换(确定映射轴和缩放比例):
物体选择不同的坐标系:这里是在士兵的摄像机坐标系位置(乘以摄像机绕y轴旋转的逆矩阵得到结果),映射到小地图上(3D场景中的z映射为UI上面y,且乘以缩放比例)。
// 物体相对位置定位:利用好相对位置(不用变换到屏幕坐标系,直接用相对位置变化即可)
:在设计屏幕坐标系和3D世界坐标系,摄像机坐标系,摄像机移动之间找到对应关系,避免频繁在摄像机和屏幕坐标系设计分辨率之间转换。
// 复杂嵌套坐标系,物体在世界(父坐标系)中变换要先缩放->旋转->平移,世界(父坐标)变换到物体本地坐标系取反即可(U3D的transform就是变换到世界坐标系用的):两个相关的物体定位关联问题,可以在摄像机,屏幕分辨率,坐标系,相对位置,复杂的骨骼动画的坐标系转换,找到对应关系。