Closed rotu closed 1 month ago
Your reading here is incorrect. xx
means $2x^2
$, not $x^3
$. Both xy
and yx
are $2xy
$. You can find the standard formula for a matrix from a quaternion here.
Your reading here is incorrect.
xx
means $2x^2$ , not $x^3$. Bothxy
andyx
are $2xy$.
D'oh! You're right and I need my eyes checked.
You can find the standard formula for a matrix from a quaternion here.
gl-matrix
and three.js
both modify that by rewriting the main diagonal, assuming the quaternion is unit length.
When the quaternion is not unit, in the original formula, scaling the quaternion means the matrix will scale a vector uniformly. A real quaternion [x,y,z,w]=[0,0,0,r] scales a vector by $r^2$. The purely imaginary quaternion [x,y,z,w]=[2,0,0,0] results in a diagonal matrix with diagonal [4,-4,-4].
In the modified formula, scaling the quaternion uniformly results in a distortion. A real quaternion [x,y,z,w]=[0,0,0,r] leaves a vector unchanged. The purely imaginary quaternion [x,y,z,w]=[2,0,0,0] results in a diagonal matrix with diagonal [1,-3,-3].
Was this intentional?
We practically never use non-unit quaternions in computer graphics, so that simplification holds. See this page for more details on the full derivations.
Are you seeing differences between three.js's compose and mat4.fromQuat for unit quaternions?
We practically never use non-unit quaternions in computer graphics, so that simplification holds.
I guess? It arises if you interpolate between quaternions without renormalizing. Or if you just want to do arbitrary quaternion arithmetic.
I learned vector rotation by the Hamilton product, where scaling the quaternion distributes over the multiplication quite nicely but it's written as $qpq^*$ instead of the $qpq^{-1}$ here. These coincide when the quaternion is unit (but differ by a factor of $\left| q\right|^2$ (which is accounted for by the "s" term in that derivation).
Keeping the factor s is equivalent to normalizing the quaternion before you convert it to rotation matrix.
See this page for more details on the full derivations.
That derivation is killing me! Representing a quaternion as an ordered pair and using both the outer product and cross product is just cruel - just write it out as quaternions!
More seriously, that derivation messes up on the last step - it doesn't distribute s when expanding out the expression as a matrix. You can observe this easily by seeing that, if you factor out s, every term in the derivation is a homogeneous polynomial of degree 2 in the components of q.
Are you seeing differences between three.js's compose and mat4.fromQuat for unit quaternions?
No, I was wondering where the non-uniform scaling comes from when setting a rotation to a non-unit quaternion in https://github.com/donmccurdy/glTF-Transform/issues/1533 and I thought it might be due to code adopted from here. But it's not - three.js would exhibit the same odd behavior with multiplying matrices derived from quaternions. This issue, as written, is incorrect.
Though now I'm still left wondering why THREE, gl-matrix, babylon.js all use this mechanism, instead of prenormalizing the quaternion or keeping the uniform scale factor. Is it really just to save a few add operations?
I guess? It arises if you interpolate between quaternions without renormalizing.
Scaling with quaternions is pretty rare. I doubt that most people using quaternions want them to scale, I suspect they only want rotations. If you do quat.lerp
and then plug the result directly into mat3.fromRotation
without normalizing, you'll indeed get garbage.
Though now I'm still left wondering why THREE, gl-matrix, babylon.js all use this mechanism, instead of prenormalizing the quaternion or keeping the uniform scale factor.
I suspect it's because code is copied from other places, and I suspect this formulation is copied all the way back from Ken Shoemake in his 1985 paper Animating Rotation with Quaternion Curves, who indeed mentions that it's valid only for unit quaternions.
Fixing this in every math library will mostly be tilting at windmills, I suspect.
It’s not that the scale factor is important in itself - it’s that matrix multiplication and quaternion multiplication only correspond if you either keep or normalize out that scale factor.
(you don’t actually need to take the square root - you can store the squared norm as a global scale factor and normalize it to 1 when reading off the end result. I think that’s equivalent to just putting it in the bottom right corner of a 4x4 matrix in homogeneous coordinates)
Good find! That paper is indeed trying to account for every single add and multiply!
Anyway I’m closing this issue. I do think it should change, but this clearly isn’t the typo I thought it was!
In comparing
mat4.fromquat
to three.js'sMatrix4.compose
, there are some striking differences;Both store the matrix in column-major order, so they should have the same entries (ignoring scale and translation). Instead, I see the following
(where:xx
means $x^3$ andxy
means $xy^2$)m[0] = 1 - yy - zz; m[1] = xy + wz != yx + wz m[2] = xz - wy != zx - wy m[3] = 0 m[4] = xy - wz != yx - wz m[5] = 1 - xx - zz m[6] = yz + wx != zy + wx m[7] = 0 m[8] = xz + wy != zx + wy m[9] = yz - wx != zy - wx m[10] = 1 - xx - yy m[11] = 0 m[12] = 0 m[13] = 0 m[14] = 0 m[15] = 0