Ryo Kuroyanagi
Posted on April 1, 2022
Recently I'm working on handtracking ARs. To align 3D models to hands, I had to match coordinates of 3D model objects to a coordinate of a hand to make effects like 3D models on a hand etc.
It is a bit complicated to align them, so let me explain how to do it here.
Assumptions
Let's say we have a target coordinate XYZ (e.g. hand) and a coordinate xyz (e.g. 3D model) to align. We already know the vectors of XYZ and xyz. X/Y/Z are orthogonal each other and normalized (the length is 1) and x/y/z are also. The goal is to align the xyz vectors to XYZ.
Aligning the origin positions are not explained in this article because we can do it just by setting the XYZ origin position to xyz origin position.
In the following steps, I align z/Z and x/X. However, the order of operation does not matter. You may align y/Y first and z/Z next.
Align z to Z
First, let's align the z axis.
Calculate the cross vector z × Z
and θ with the dot product and Arccosine which is used to rotate the coordinate around the cross vector. To achieve the rotation, we can use a quaternion. Calculating quaternion by ourselves is a bit complicated and usually 3D CG engine has built-in function to calculate it from the axis to rotate around and the angle. If you really want to implement by yourself, you may refer this article.
This is an example pseudocode. Your programming language / 3D CG engine (e.g. Three.js / Unity / Unreal Engine) should have cross / dot / normalize / acos / fromAxisAngle.
Vec3 z = ... // Your z
Vec3 Z = ... // Your Z
Vec3 axisZ = normalize(cross(z, Z)); // Normalized cross vector.
float angleZ = acos(dot(z, Z)); // The angle between z and Z --> theta in the image.
Quaternion qz = fromAxisAngle(axisZ, angleZ); // Later we will make other quaternions so let's name it qz.
Now we've got the quaternion qz
to align z to Z axis.
Align x to X
By applying the quaternion qz
to your coordinate, you should have x'/y'/z' like the following image.
Aligning x/X axes is basically the same process as we did for z/Z. Since z/Z have been aligned already, x and X is on the same plane. When x/X is aligned, y/Y is automatically aligned. However, please do not forget apply the quaternion qz
to the x vector. Let's name the rotated x vector as x'
.
Pseudocode should be like this. applyQuaternion
is a function to apply a quaternion to a vector. Your programming language / 3D engine should have an equivalent.
Vec3 rotatedX = applyQuaternion(x, qz); // x'
Vec3 X = ... // Your X
Vec3 axisX = normalize(cross(rotatedX, X));
float angleX = acos(dot(rotatedX, X)); // The angle between x' and X --> phi in the image
Quaternion qx = fromAxisAngle(axisX, angleX);
We've got the next quaternion qx
.
Combine quaternions
Now we have qz
and qx
. By combining the quaternions, we will have the final quaternion we can realize our coordinate alignment.
multiplyQuaternions
is a function to combine two quaternions into one. Your programming language / 3D engine should have an equivalent. Please be careful that the order of qz
and qx
is important.
Quaternion q = mltiplyQuaternions(qx, qz);
By applying the quaternion q
to x/y/z respectively, you'll get vectors identical to X/Y/Z.
Appendix: Three.js code
I showed pseudo code in the above. I also give you guys an example in Three.js.
// Your coordinates.
// x/y/z are vectors that will be aligned FROM.
const x = new Vector3(...);
const y = new Vector3(...);
const z = new Vector3(...); // <-- You do not need this actually.
// X/Y/Z are vectors that will be aligned TO.
const X = new Vector3(...);
const Y = new Vector3(...);
const Z = new Vector3(...); // <-- You do not need this actually.
const axisZ = z.clone().cross(Z).normalize();
const angleZ = Math.acos(z.dot(Z));
const qz = new Quaternion().setFromAxisAngle(axisZ, angleZ);
const rotatedX = x.clone().applyQuaternion(qz);
const axisX = rotatedX.clone().cross(X).normalize();
const angleX = Math.acos(rotatedX.dot(X));
const qx = new Quaternion().setFromAxisAngle(axisX, angleX);
// Final quaternion
const q = new Quaternion().multiplyQuaternions(qx, qz);
// You can use q like this.
yourModel.rotation.setFromQuaternion(q)
You can check my Demo, if you want!
Posted on April 1, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.