Jump to content
  • Advertisement
Sign in to follow this  
Michael Wojcik

Vector rotation via Quaternion

This topic is 2352 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

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! Edited by Michael Wojcik

Share this post


Link to post
Share on other sites
Advertisement
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;
}
Edited by Michael Wojcik

Share this post


Link to post
Share on other sites
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)

Share this post


Link to post
Share on other sites

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.

Share this post


Link to post
Share on other sites
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?

Share this post


Link to post
Share on other sites
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. Edited by Michael Wojcik

Share this post


Link to post
Share on other sites
Well, look at this line in your code:
vecnormal.Normalize();

What do you expect? ;)

Share this post


Link to post
Share on other sites
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!

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!