修正D3DXQuaternionSquad不能正确处理反向Quaternions的BUG

修正D3DXQuaternionSquad不能正确处理反向Quaternions的BUG

微软DX库里提供了对Quaternion进行球面四边形内插的接口,能够在多个Quaternion之间平滑插值。这里涉及到两个接口:

void D3DXQuaternionSquadSetup(
  __out  D3DXQUATERNION *pAOut,
  __out  D3DXQUATERNION *pBOut,
  __out  D3DXQUATERNION *pCOut,
  __in   const D3DXQUATERNION *pQ0,
  __in   const D3DXQUATERNION *pQ1,
  __in   const D3DXQUATERNION *pQ2,
  __in   const D3DXQUATERNION *pQ3
);
D3DXQUATERNION* D3DXQuaternionSquad(
  __inout  D3DXQUATERNION *pOut,
  __in     const D3DXQUATERNION *pQ1,
  __in     const D3DXQUATERNION *pA,
  __in     const D3DXQUATERNION *pB,
  __in     const D3DXQUATERNION *pC,
  __in     FLOAT t
);
其中,D3DXQuaternionSquadSetup用于返回内插的控制点。它们具体的实现公式和用法,有兴趣的同学可以参考MSDN。在此需要说明的是,
D3DXQuaternionSquad使用了Slerp作为内部实现,会导致在两个夹角为180°左右的Quaternion之间插值会出现断裂的问题。下面代码通过
实现一个考虑了上述情况的Slerp版本,在q1和q2夹角在0°或者180°时,使用线性内插而非球面,来解决该问题。
  1. Quaternion QuatSlerpNoInvert(const Quaternion& q1 , const Quaternion& q2 , float t)
  2. {
  3.     float cosAngle = DotProduct(q1, q2);
  4.  
  5.     float c1, c2;
  6.     // Linear interpolation for close orientations
  7.     if ((1.0f - fabs(cosAngle)) < 1e-5f)
  8.     {
  9.         c1 = 1.0f - t;
  10.         c2 = t;
  11.     }
  12.     else
  13.     {
  14.         // Spherical interpolation
  15.         float angle    = acos(fabs(cosAngle));
  16.         float sinAngle = sin(angle);
  17.         c1 = sin(angle * (1.0f - t)) / sinAngle;
  18.         c2 = sin(angle * t) / sinAngle;
  19.     }
  20.  
  21.     Quaternion q = q1 * c1 + q2 * c2;
  22.     q.Normalize();
  23.  
  24.     return q;
  25. }
  26.  
  27. Quaternion QuatSquad(const Quaternion& p1 , const Quaternion& p2 , const Quaternion& p3 , const Quaternion& p4 , float t)
  28. {
  29.     static Quaternion a , b , c;
  30.  
  31.     D3DXQuaternionSquadSetup((D3DXQUATERNION*)&a , (D3DXQUATERNION*)&b , (D3DXQUATERNION*)&c ,
  32.         (D3DXQUATERNION*)&p1 , (D3DXQUATERNION*)&p2 , (D3DXQUATERNION*)&p3 , (D3DXQUATERNION*)&p4);
  33.  
  34.     return QuatSlerpNoInvert(QuatSlerpNoInvert(p2 , c , t) , QuatSlerpNoInvert(a , b , t) , 2 * t * (1-t));
  35. }

 

参考:

[1] http://msdn.microsoft.com/en-us/library/bb205419(v=vs.85).aspx

[2] http://msdn.microsoft.com/en-us/library/bb205420(v=vs.85).aspx

你可能感兴趣的:(修正D3DXQuaternionSquad不能正确处理反向Quaternions的BUG)