Jump to content

  • Log In with Google      Sign In   
  • Create Account

Vector rotation via Quaternion


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 Michael Wojcik   Members   -  Reputation: 163

Like
0Likes
Like

Posted 09 July 2012 - 10:52 PM

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, 09 July 2012 - 10:56 PM.

Generalist Game Developer and Cofounder at Voidseer Realms

Sponsor:

#2 Álvaro   Crossbones+   -  Reputation: 13935

Like
1Likes
Like

Posted 10 July 2012 - 04:55 AM

What does the quaternion constructor with three arguments do?

#3 Michael Wojcik   Members   -  Reputation: 163

Like
0Likes
Like

Posted 10 July 2012 - 11:23 AM

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, 10 July 2012 - 11:51 AM.

Generalist Game Developer and Cofounder at Voidseer Realms

#4 Álvaro   Crossbones+   -  Reputation: 13935

Like
1Likes
Like

Posted 10 July 2012 - 12:06 PM

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)

#5 Álvaro   Crossbones+   -  Reputation: 13935

Like
1Likes
Like

Posted 10 July 2012 - 12:09 PM

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.

#6 Michael Wojcik   Members   -  Reputation: 163

Like
0Likes
Like

Posted 10 July 2012 - 03:57 PM

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

#7 Álvaro   Crossbones+   -  Reputation: 13935

Like
1Likes
Like

Posted 10 July 2012 - 04:10 PM

I don't understand your last question. You pass <5,0, 0.0, 0.0> to what?

#8 Michael Wojcik   Members   -  Reputation: 163

Like
0Likes
Like

Posted 10 July 2012 - 05:09 PM

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, 10 July 2012 - 06:51 PM.

Generalist Game Developer and Cofounder at Voidseer Realms

#9 Álvaro   Crossbones+   -  Reputation: 13935

Like
1Likes
Like

Posted 10 July 2012 - 07:09 PM

Well, look at this line in your code:
vecnormal.Normalize();

What do you expect? ;)

#10 Michael Wojcik   Members   -  Reputation: 163

Like
0Likes
Like

Posted 10 July 2012 - 08:19 PM

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




Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS