Quaternion local-axis

Started by
6 comments, last by Alundra 10 years, 11 months ago

Hi all,

Using the view matrix, computed like that :


const CMatrix4 ViewMatrix = m_WorldMatrix.Inverse();

You can get the local-axis :


// Forward vector.
m_ForwardVector.x = ViewMatrix( 2, 0 );
m_ForwardVector.y = ViewMatrix( 2, 1 );
m_ForwardVector.z = ViewMatrix( 2, 2 );
m_ForwardVector.Normalize();

// Right vector.
m_RightVector.x = ViewMatrix( 0, 0 );
m_RightVector.y = ViewMatrix( 0, 1 );
m_RightVector.z = ViewMatrix( 0, 2 );
m_RightVector.Normalize();

// Up vector.
m_UpVector.x = ViewMatrix( 1, 0 );
m_UpVector.y = ViewMatrix( 1, 1 );
m_UpVector.z = ViewMatrix( 1, 2 );
m_UpVector.Normalize();

Using quaternion you can get the local-axis like that :


CVector3 CQuaternion::GetForwardVector() const
{
const float x2 = 2.0f * x;
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float x2w = x2 * w;
const float y2w = y2 * w;
const float x2x = x2 * x;
const float z2x = z2 * x;
const float y2y = y2 * y;
const float z2y = z2 * y;
return CVector3( z2x + y2w, z2y - x2w, 1.0f - ( x2x + y2y ) );
}

CVector3 CQuaternion::GetRightVector() const
{
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float y2w = y2 * w;
const float z2w = z2 * w;
const float y2x = y2 * x;
const float z2x = z2 * x;
const float y2y = y2 * y;
const float z2z = z2 * z;
return CVector3( 1.0f - ( y2y + z2z ), y2x + z2w, z2x - y2w );
}

CVector3 CQuaternion::GetUpVector() const
{
const float x2 = 2.0f * x;
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float x2w = x2 * w;
const float z2w = z2 * w;
const float x2x = x2 * x;
const float y2x = y2 * x;
const float z2y = z2 * y;
const float z2z = z2 * z;
return CVector3( y2x - z2w, 1.0f - ( x2x + z2z ), z2y + x2w );
}

When I compare the value from the quaternion and from the view matrix, I don't get the same values.

Compute euler from a matrix or a quaternion is not possible, is it the same for local-axis from a quaternion ?

Thanks

Advertisement

In both cases I would obtain the three vectors like this:


  CVector3 right_vector   = apply_rotation_to_vector(CVector3(1, 0, 0));
  CVector3 up_vector      = apply_rotation_to_vector(CVector3(0, 1, 0));
  CVector3 forward_vector = apply_rotation_to_vector(CVector3(0, 0, 1));

When I compare the value from the quaternion and from the view matrix, I don't get the same values.

Can you post the quaternion, the matrix and the values that don't match? How did you obtain them? Try to make the example as simple as possible.

Compute euler from a matrix or a quaternion is not possible, is it the same for local-axis from a quaternion ?

That part I didn't understand at all.

Ok, it was a problem of space I think since i'm using left-handed coordinate system.

The corrected function is :


CVector3 CQuaternion::GetForwardVector() const
{
const float x2 = 2.0f * x;
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float x2w = x2 * w;
const float y2w = y2 * w;
const float x2x = x2 * x;
const float z2x = z2 * x;
const float y2y = y2 * y;
const float z2y = z2 * y;
return CVector3( z2x - y2w, z2y + x2w, 1.0f - ( x2x + y2y ) );
}

CVector3 CQuaternion::GetRightVector() const
{
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float y2w = y2 * w;
const float z2w = z2 * w;
const float y2x = y2 * x;
const float z2x = z2 * x;
const float y2y = y2 * y;
const float z2z = z2 * z;
return CVector3( 1.0f - ( y2y + z2z ), y2x - z2w, z2x + y2w );
}

CVector3 CQuaternion::GetUpVector() const
{
const float x2 = 2.0f * x;
const float y2 = 2.0f * y;
const float z2 = 2.0f * z;
const float x2w = x2 * w;
const float z2w = z2 * w;
const float x2x = x2 * x;
const float y2x = y2 * x;
const float z2y = z2 * y;
const float z2z = z2 * z;
return CVector3( y2x + z2w, 1.0f - ( x2x + z2z ), z2y - x2w );
}

Log result (R = from inverse matrix, Q = from quaternion) :

(21:57:02) RF : -0.278453, -0.092483, -0.092483

(21:57:02) RR : 0.960248, -0.006399, -0.006399

(21:57:02) RU : -0.019692, 0.995694, 0.995694

(21:57:02) QF : -0.278433, -0.093478, -0.093478

(21:57:02) QR : 0.960248, -0.006399, -0.006399

(21:57:02) QU : -0.019971, 0.995601, 0.995601

You can see precision difference, I don't know who is the more accurate.

About :

That part I didn't understand at all.

It's possible but the most of time you get weird results.It's why it's better to store the euler angle manually.

The most accurate one is the one that does the least amount of floating point operations :)

Just count the number of +-*/

You can see precision difference, I don't know who is the more accurate.

In both cases I would obtain the three vectors like this:


  CVector3 right_vector   = apply_rotation_to_vector(CVector3(1, 0, 0));
  CVector3 up_vector      = apply_rotation_to_vector(CVector3(0, 1, 0));
  CVector3 forward_vector = apply_rotation_to_vector(CVector3(0, 0, 1));

that's exactly what he's doing, but he's factored the zero's and ones out (it seems like he's deriving the quat to matrix equations from first principles)

Compute euler from a matrix or a quaternion is not possible, is it the same for local-axis from a quaternion ?

That part I didn't understand at all.

converting from eulers to a matrix or quat is a lossy process. You can never go back (although you can get +-180 degree approximations using the bog standard ken shoemake code, and you can apply an euler filter over a range of changing rotations to give a reasonable flip free set of FCurves).

Yea, you have the quaternion from eulers too :


void CQuaternion::FromEulerAngles( const float X, const float Y, const float Z )
{
const float HP = 0.5f * X;
const float HY = 0.5f * Y;
const float HR = 0.5f * Z;
const float SinHP = CMath::Sin( HP );
const float SinHY = CMath::Sin( HY );
const float SinHR = CMath::Sin( HR );
const float CosHP = CMath::Cos( HP );
const float CosHY = CMath::Cos( HY );
const float CosHR = CMath::Cos( HR );
x = SinHY * CosHP * SinHR + CosHY * SinHP * CosHR;
y = SinHY * CosHP * CosHR - CosHY * SinHP * SinHR;
z = CosHY * CosHP * SinHR - SinHY * SinHP * CosHR;
w = CosHY * CosHP * CosHR + SinHY * SinHP * SinHR;
}

About Ken Shoemake, he has done a great job on quaternion.Paper can be found on google.

Yeah, I think you might have misunderstood my point. I was saying that quat (or matrix) to euler is a conversion that has limited success, because euler-to-quat is a lossy conversion.


i.e. Given a rotation order of ZXY, and Euler angles of 987, 377, and -448 degrees, convert to a quat, and then back again, you will have lost information along the way. The resulting eulers will be limited to the +180 to -180 range, and the resulting Euler F-Curves will need filtering to remove the flips.

This is one of the problem I had in my editor since I showed the X/Y/Z value.

When I moved the object I changed the text using Quat-To-Euler but most of time the result was weird.

The best option, I think, is to store the X/Y/Z value in a property by object for the editor.

The bad point too was I used this value to set the new rotation, better to use the quaternion operator *.

This topic is closed to new replies.

Advertisement