0

I'm trying to understand quaternions and am having a lot of trouble converting it into something actually useful. I've watched some 3blue1brown videos and read some tutorials, but I can't seem to translate what I see into something simple which means I don't really understand it at all.

Starting with the simplest example I can think of, I want to start with a point at xyz (1, 0, 0) and rotate 90 degrees around the Z axis. This should give me the point (0, 1, 0). Using trigonometry, doing the rotation is simple (doSimple()).

As I said I am lacking in understanding of the fundamentals of quaternions, but from what I understand a point in 3d space can be represented using x, y, z and 0 for the w. And gl-matrix has a 'quat.rotateZ' method that seems like it should rotate around that axis by an angle. My results seem to indicate that it only rotates by 1/2 the angle and in the wrong direction (repl.it):

const { quat } = require('gl-matrix');

const doSimple = () => {
  // simple using trig:
  const x = 1, y = 0;
  const angle = Math.PI / 2; // 15 degrees
  const newX = x * Math.cos(angle) - y * Math.sin(angle);
  const newY = x * Math.sin(angle) + y * Math.cos(angle);
  console.log({ angle, x, y, newX, newY });
}

const doQuat = () => {
  let [x, y, z, w] = [1, 0, 0, 0];
  const a = quat.fromValues(x, y, z, w);
  const ninetyDegrees = Math.PI / 2;
  const b = quat.create();
  quat.rotateZ(b, a, ninetyDegrees);
  const results = { a, b }
  console.log(results)
}

const doAngle = () => {
  let [x1, y1, z1, w1] = [1, 0, 0, 0];
  let [x2, y2, z2, w2] = [0, 1, 0, 0];
  const q1 = quat.fromValues(x1, y1, z1, w1);
  const q2 = quat.fromValues(x2, y2, z2, w2);
  const angle = quat.getAngle(q1, q2);
  console.log({ angle });
}

doSimple();
doQuat();
doAngle();

doSimple() looks good to me:

{
  angle: 1.5707963267948966,
  x: 1,
  y: 0,
  newX: 6.123233995736766e-17,
  newY: 1
}

doQuat() uses quat.rotateZ() and seems to only rotate 45 degrees and in the wrong direction:

{
  a: Float32Array(4) [ 1, 0, 0, 0 ],
  b: Float32Array(4) [ 0.7071067690849304, -0.7071067690849304, 0, 0 ]
}

doAngle() seems to report that there are 180 degrees between (1,0,0) and (0,1,0):

{ angle: 3.141592653589793 }

So I know I'm totally misunderstanding something, but I don't know what it is...

---Edit---

I think a lot of my confusion stems from different terminology in examples. gl-matrix uses x,y,z,w. 3blue1brown uses <blank>, i,j,k. This paper uses q0,q1,q2,q3. This calculator from the paper doesn't seem to make sense in that Yaw rotates around the Z axis, Pitch around the Y axis, and Roll around the X axis?

If I'm thinking in 3d space the normal way I think of things would be Z facing out of the screen and X and Y representing a normal 2d graph on the screen (X points 'right', Y points 'up'). Yaw should rotate around the Y axis and pitch should rotate around the X axis.

Am I thinking wrong? Are there a set (or sets) of standard ways of looking at the coordinate system, rotations, and quaternions that I should use when trying to learn? For example I think that since gl-matrix uses x,y,z,w and the paper uses q0,q1,q2,q3 and 3b1b uses blank,i,j,k, I think the conversions are as follows:

  • q0 -> blank -> w
  • q1 -> i -> x
  • q2 -> j -> y
  • q3 -> k -> z
Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113

1 Answers1

0

I've found out a few things and think I have a good basis now for learning at least. Looking at 3b1b's interactive videos, aligning the axis how I wanted to view them, x and y like a 2d graph and z pointing at me through the screen, understanding that f(i) means a point at x=1, that i represents the x axis, and playing around helped me.

  • (blank,i,j,k) or (q0,q1,q2,q3) are indeed equivilent to (w,x,y,z)
  • Rotating a point involves two multiplications: f(p) = q * p * q-1 so it is a two step process
  • glMatrix's rotateZ function doesn't seem to do what I thought, I don't really know what it does

So this seems to work:

// fromEuler gives quat from X,Y,Z in DEGREES
const euler = quat.fromEuler(quat.create(), 0, 0, 90);

// p is my point, at (1,0,0) with w=0
let [x, y, z, w] = [1, 0, 0, 0];
const p = quat.fromValues(x, y, z, w);

// so I need q (rotation which is euler 90 degrees about Z axis)
// and inverse of q.  Formula:
// f(p) = q * p * qInverse
const q = quat.clone(euler);
const qInverse = quat.invert(quat.create(), q);

// first q * p, then result * qInverse
let pPrime = quat.multiply(quat.create(), q, p);
pPrime = quat.multiply(quat.create(), pPrime, qInverse);

That gives what I was looking for:

{
  euler: Float32Array(4) [ 0, 0, 0.7071067690849304, 0.7071067690849304 ],
  p: Float32Array(4) [ 1, 0, 0, 0 ],
  q: Float32Array(4) [ 0, 0, 0.7071067690849304, 0.7071067690849304 ],
  qInverse: Float32Array(4) [ -0, -0, -0.7071067690849304, 0.7071067690849304 ],
  pPrime: Float32Array(4) [ 0, 0.9999999403953552, 0, 0 ]
}
Ok, from point (1, 0, 0) gives (0, 1, 0)
q (euler rotation 90 degrees about z axis):
  xyzw: (x = 0,y = 0,z = 0.71,w = 0.71)
  ijk: (0.71, i = 0, j = 0, k = 0.71)
qInverse:
  xyzw: (x = 0,y = 0,z = -0.71,w = 0.71)
  ijk: (0.71, i = 0, j = 0, k = -0.71)

Quaternions use angle/2 for sin and cos, why I was getting something like 45 degrees. Now that I can

Jason Goemaat
  • 28,692
  • 15
  • 86
  • 113