#723 旋转相关的一些思考   concept     UE4     11 months ago (owner) Document
针对物体当前的姿态,以及从当前姿态变化到另一个姿态的变化量,我们没有用两种不同的描述形式来区分这两个概念。即,姿态与姿态的变化量虽然是两个不同的概念,但是用同一种形式描述的。也有把姿态叫做方位的。不管怎样,也可以找到一个角度,从这个角度出发可以说姿态与姿态的变化量是同一个事物。姿态的变化量是指从一个姿态到另一个姿态的变化量,而姿态也可以定义为从一个全局唯一初始姿态到当前的姿态的变化量。因此,这种理解下,它们就有了用相同的描述形式来进行描述的逻辑根据。
FTransform(Location(0,0,0), Rotation(0,0,0), Scale3D(1,1,1))就是这个初始姿态。通常叫Identity Transform,这里简称为ID、零姿态注意:这里蕴含着在一个明确的参照系里定义一个姿态,并将该姿态的数值描述记为Identity Transform
当用一个FTransform描述一个物体当前的姿态时,应该把这个Transform理解为从零姿态到当前姿态的变化量。
旋转的表示,不管是欧拉角(FRotator)还是四元数(FQuat),针对的都只是在坐标原点的旋转。四元数的轴角对表示中,旋转轴也是指穿过坐标原点的轴。
如果要表达在世界其他点旋转,则要结合位移来表达。FTransform有3个分量,分别是Location、Rotation、Scale3D。对一个具体的FTransform来说,其Location与Rotation是互相独立的,比如FTransform(FVector(100,0,0), FRotator(0,0,90)),其描述的是物体从所处世界的零姿态开始旋转90度后,平移到该世界的(100,0,0)点,那么它看起来就像是在(100,0,0)处的物体原地旋转了90度。因此,这个物体的姿态,就不能只用旋转来表达,必须将位置与旋转结合起来表达。FTransform(FVector::ZeroVector, FRotator(0,0,90))表达的是在原点转了90度的物体。
 案例1:如果物体要在当前姿态T0的基础上额外在原地进行一个世界旋转D(FRotator表达),则物体最终的姿态T1=FTransform(T0.Location(), T0.Rotation() * D)。想一想D为0时的特殊情形加深理解。上面也说了,Location与Rotation是互相独立的部分,因此旋转先全部执行到位,就是T0.Rotation()*D了,然后执行位移,由于这里变换只有原地旋转,因此位移保持不变,就是T0.Location()了。这里有一个很重要的问题:D是否就是T0.Invert() * T1? 答案为否。
FTransform的乘法(见TransformVectorized.h的Multiply函数),作用就是在姿态T0的基础上进行增量变换DeltaTransform,最后变成姿态T1。这个变换过程可以直观的理解:在保持物体处于T0姿态的前提下,给它添加一个父节点构成刚性连接,且该父节点处于零姿态(世界原点、无初始旋转),对该父节点应用DeltaTransform,由于刚性连接,该父节点会带动物体也进行完全相同的DeltaTransform变换。因此,很明显,物体最终的姿态就是T1=FTransform(NewLocation, NewRotation),其中:
  • NewRotation=T0.Rotation() * DeltaTransform.Rotation()
  • NewLocation=DeltaTransform.Rotation().Rotate(T0.Location()) + DeltaTransform.Location()
回到案例1最后的问题。案例1中T1的计算过程,明显不是FTransform的乘法实现,因此那个世界旋转D并不是T0.Invert() * T1。要知道T0*Delta=T1,这个Delta才是T0.Invert()*T1,而T1不是由T0*D实现的,因此Delta != D
 案例2:在案例1中,如果该物体还有一个父节点,但整个刚性构造需要以该物体作为中心进行变换,求父节点变换后的姿态?
在案例2这个变换过程中,父节点反而变成该物体逻辑上的子节点被带动。由于是刚性连接,父节点与该物体具有完全相同的变化量:绕相同的轴进行了相同量的旋转,以及进行了相同的平移。如果想求出父节点在变换后的姿态,则先求出物体的变化量即可:DeltaTransform = T0.Invert() * T1。这里要注意:求出DeltaTransform后,不能用Parent->AddWorldTransform(DeltaTransform)来求出父节点变换后的姿态,因为AddWorldTransform执行的并不是FTransform的乘法。需要通过Parent->GetWorldTransform()*DeltaTransform来求得父节点最终的姿态。这是方法一。
那么能否通过Parent->AddWorldTransform(D)的方式求出父节点的姿态呢?也不行。因为仅靠D只能实现父节点原地旋转,并不是绕物体进行变换。需要增加位移信息,用NewRotation=Parent->WorldRotation()*D作为旋转信息,用D.Rotate(Parent->WorldLocation()-Object->WorldLocation())+Object->WorldLocation()求出父节点的位移信息,即可求出正确的姿态,而这个实现其实就是FTransform的乘法过程。
以上思考均在UE蓝图里验证正确。