Vector rotation via Quaternion

Started by
8 comments, last by Michael Wojcik 11 years, 9 months ago
Hello,

I am using this webpage to build some of my methods for my game engine's quaternion class.

I am having trouble with the vector rotation via quaternion code. In the webpage cited above, this is what it looks like.


// Multiplying a quaternion q with a vector v applies the q-rotation to v
Vector3 Quaternion::operator* (const Vector3 &vec) const
{
Vector3 vn(vec);
vn.normalise();

Quaternion vecQuat, resQuat;
vecQuat.x = vn.x;
vecQuat.y = vn.y;
vecQuat.z = vn.z;
vecQuat.w = 0.0f;

resQuat = vecQuat * getConjugate();
resQuat = *this * resQuat;

return (Vector3(resQuat.x, resQuat.y, resQuat.z));
}


So I use it in my quaternion class but I get incorrect results.

This is what I do to test the method...

Vector3 pos = new Vector3(1.0f, 0.0f ,0.0f);

Quaternion q = new Quaternion(0.0f, 0.0f, 45f);

pos = q.Rotate(pos);


To my current perspective, this will give me <~-0.707, ~0.707, 0.0f>. But it doesn't, I get <-0.13529904, 0.3535534, -0.3744762>, which seems to me to be way off.

Here is my code for the quaternion-vector rotation method...


/**
* Multiplys a quaternion q with a vector v to apply the q-rotation to v
*/
public Vector3 Rotate (Vector3 vec)
{
vecnormal.Set(vec);
vecnormal.Normalize();
qVec.x = vecnormal.X;
qVec.y = vecnormal.Y;
qVec.z = vecnormal.Z;
qVec.w = 0.0f;

qConjugate.Set(this).Conjugate();

Quaternion qRes = qVec.Multiply(qConjugate);
qTmpThis.Set(this);
qRes = qTmpThis.Multiply(qRes);

vec.X = qRes.x; vec.Y = qRes.y; vec.Z = qRes.z;
return vec;
}


My code is in Java, so that is why it looks different. I apprecaite any help or ideas!!

Thanks!
Generalist Game Developer and Cofounder at Voidseer Realms
Advertisement
What does the quaternion constructor with three arguments do?
Quaternion q = new Quaternion(0.0f, 0.0f, 45f);

Initializes the quaternion in euler angles which calls this method

public void FromEulerAngles(float x, float y, float z)
{
float roll = MathHelper.DegreesToRadians(z);
float pitch = MathHelper.DegreesToRadians(x);
float yaw = MathHelper.DegreesToRadians(y);

float cyaw, cpitch, croll, syaw, spitch, sroll;
float cyawcpitch, syawspitch, cyawspitch, syawcpitch;

cyaw = MathHelper.Cos(0.5f * yaw);
cpitch = MathHelper.Cos(0.5f * pitch);
croll = MathHelper.Cos(0.5f * roll);
syaw = MathHelper.Sin(0.5f * yaw);
spitch = MathHelper.Sin(0.5f * pitch);
sroll = MathHelper.Sin(0.5f * roll);

cyawcpitch = cyaw*cpitch;
syawspitch = syaw*spitch;
cyawspitch = cyaw*spitch;
syawcpitch = syaw*cpitch;

this.w = cyawcpitch * croll + syawspitch * sroll;
this.x = cyawspitch * croll + syawcpitch * sroll;
this.y = syawcpitch * croll - cyawspitch * sroll;
this.z = cyawcpitch * sroll - syawspitch * croll;
}


The Quaternion.Set sets to the calling quaternion's members, the x,y,z,w from that of the quaternion passed into set.

Here's Quaternion.Set
public Quaternion Set(Quaternion rhs)
{
this.x = rhs.x;
this.y = rhs.y;
this.z = rhs.z;
this.w = rhs.w;
return this;
}


Heres my Quaternion.Multiply, just in case
public Quaternion Multiply(Quaternion qLocal)
{
w = ((w * qLocal.w) - (x * qLocal.x) - (y * qLocal.y) - (z * qLocal.z));
x = ((w * qLocal.x) + (x * qLocal.w) + (y * qLocal.z) - (z * qLocal.y));
y = ((w * qLocal.y) + (y * qLocal.w) + (z * qLocal.x) - (x * qLocal.z));
z = ((w * qLocal.z) + (z * qLocal.w) + (x * qLocal.y) - (y * qLocal.x));

normalRegenerationCount++;
return this;
}
Generalist Game Developer and Cofounder at Voidseer Realms
Use a debugger to see where things are going wrong. I built a quaternion using you code to convert Euler angles to a quaternion and then tested the same rotation of (1,0,0) using angles (0,0,45), but my results were correct:
#include <iostream>
#include <boost/math/quaternion.hpp>
#include <cmath>

typedef boost::math::quaternion<double> quaternion;

quaternion euler_to_quaternion(double x, double y, double z) {
double const degrees = std::atan(1.0)/45.0;

float roll = z * degrees;
float pitch = x * degrees;
float yaw = y * degrees;

float cyaw, cpitch, croll, syaw, spitch, sroll;
float cyawcpitch, syawspitch, cyawspitch, syawcpitch;

cyaw = std::cos(0.5f * yaw);
cpitch = std::cos(0.5f * pitch);
croll = std::cos(0.5f * roll);
syaw = std::sin(0.5f * yaw);
spitch = std::sin(0.5f * pitch);
sroll = std::sin(0.5f * roll);

cyawcpitch = cyaw*cpitch;
syawspitch = syaw*spitch;
cyawspitch = cyaw*spitch;
syawcpitch = syaw*cpitch;

return quaternion(cyawcpitch * croll + syawspitch * sroll,
cyawspitch * croll + syawcpitch * sroll,
syawcpitch * croll - cyawspitch * sroll,
cyawcpitch * sroll - syawspitch * croll);
}

struct Vector3D {
double x, y, z;

Vector3D(double x, double y, double z) : x(x), y(y), z(z) {
}
};

Vector3D apply(quaternion const &q, Vector3D const &v) {
quaternion qv(0.0, v.x, v.y, v.z);
quaternion result = q * qv * conj(q);
return Vector3D(result.R_component_2(),
result.R_component_3(),
result.R_component_4());
}

std::ostream &operator<<(std::ostream &os, Vector3D const &v) {
return os << '(' << v.x << ',' << v.y << ',' << v.z << ')';
}

int main() {
quaternion q = euler_to_quaternion(0.0, 0.0, 45.0);
Vector3D v(1.0, 0.0, 0.0);
std::cout << q << " applied to " << v << " is " << apply(q,v) << '\n';
}


The output is this:
(0.92388,0,0,0.382683) applied to (1,0,0) is (0.707107,0.707107,0)

Heres my Quaternion.Multiply, just in case
public Quaternion Multiply(Quaternion qLocal)
{
w = ((w * qLocal.w) - (x * qLocal.x) - (y * qLocal.y) - (z * qLocal.z));
x = ((w * qLocal.x) + (x * qLocal.w) + (y * qLocal.z) - (z * qLocal.y));
y = ((w * qLocal.y) + (y * qLocal.w) + (z * qLocal.x) - (x * qLocal.z));
z = ((w * qLocal.z) + (z * qLocal.w) + (x * qLocal.y) - (y * qLocal.x));

normalRegenerationCount++;
return this;
}



Oh, that's all messed up. You are using the same w,x,y,z variables for input and output, and as you do the computations you forget the old values, which you still need for the lines following. You are probably better off implementing a function that takes two quaternions and returns a fresh one, instead of having one of the quaternions be both source and destination.
Cool! Thats was indeed the issue! You saved me alot of time, thanks! I have another question regarding the result of quaternion - vector rotation. I noticed that If I were to pass <5.0, 0.0,0.0> I get a normal (0.707107,0.707107,0) as If I passed in <1.0, 0.0,0.0>.

Does quaternion rotation with vector only work with vector normals? Or might I have messed something else up in my class?
Generalist Game Developer and Cofounder at Voidseer Realms
I don't understand your last question. You pass <5,0, 0.0, 0.0> to what?
To the method to rotate the vector by a quaternion.



/**
* Multiplys a quaternion q with a vector v to apply the q-rotation to v
*/
public Vector3 Rotate (Vector3 vec)
{
vecnormal.Set(vec);
vecnormal.Normalize();
qVec.x = vecnormal.X;
qVec.y = vecnormal.Y;
qVec.z = vecnormal.Z;
qVec.w = 0.0f;

qConjugate.Set(this).Conjugate();

Quaternion qRes = qVec.Multiply(qConjugate);
qTmpThis.Set(this);
qRes = qTmpThis.Multiply(qRes);

vec.X = qRes.x; vec.Y = qRes.y; vec.Z = qRes.z;
return vec;
}


The result is always normal, no matter the magnitude of the input vector. Is this correct in the context of vector rotation via a quaternion? I was expecting the vector to be returned as a coordinate. I could ofcourse, could mutiply the result normal by the magnitude of the input vector. But I want to be sure if by definition, q * p * q-1 returns a rotated normal, or coordinate.
Generalist Game Developer and Cofounder at Voidseer Realms
Well, look at this line in your code:
vecnormal.Normalize();

What do you expect? ;)
True! Lol, Thanks for pointing that out, I should have noticed that normalization was not critical to the rotation calculation. Thanks for all your help!
Generalist Game Developer and Cofounder at Voidseer Realms

This topic is closed to new replies.

Advertisement