When manipulating bones in a graphics system, there are a few
constraints that are often needed. This page explores how to compute
those constraints.
1 Vectors to rotation matrices
A rotation matrix has three columns, each of which is a unit-length
vector all three of which are mutually orthogonal and the three form a
right-handed coordinate system. When applied to an object, the first
column is the direction the positive x-axis in object space ends up
pointing, the second column is where the positive y-axis ends up
pointing, and the third second column is where the positive z-axis ends
up pointing.
We can create such a set of vectors using cross products. Suppose we
have some x0, y0, and z0 that will not make a rotation matrix as-is. We first pick the
vector we want to match the most; for this example, let’s say that’s
x0. We divide it by its length to
get x and use it in the matrix:
[x??] Now we pick the second-most important; for this
example, let’s say that’s y0. We
use a pair of cross-products to find a vector orthogonal to x that is as close to y0 as possible: y1=(x×y)×x and then divide that by its length to get y, the next column: [xy?] The third column is then just the cross-product of
the other two, in an order to preserve the right-handed coordinate
system: z=x×y or
x=y×z or y=z×x. No need to
divide this one by its length: it will come out unit-length by
construction. [xyz]
Note that the least-important vector was not used at all: we only
need a primary and secondary vector to perform this rotation matrix
construction. Also note that this process will break down if the two
most important vectors are co-planar: we’ll get a zero vector from the
first cross-product and a divide-by-zero when we try to make it unit
length.
The general process of finding a rotation matrix from a set of
vectors is called orthonormalization and has other solutions,
including one based on the singular value decomposition that doesn’t
favor particular vectors, but we’ll not use those in this class.
2 Point-and-roll constraints
One common bone constraint supplied by 3D animation software is a
combination point the bone at constraint with a roll this axis
toward disambiguation. This constraint is the above vector-to-matrix
process almost directly.
To point the +z axis toward point (0,4,3) and roll the -y axis toward (0,0,1) we’d:
normalize the primary vector: z=(0,0.8,0.6)
cross-product the secondary vector: z×(0,0,1)=(0.8,0,0)
cross-product that with the primary vector: (0.8,0,0)×z=(0,−0.48,0.64)
normalize the result and handle the axis sign: y=(0,0.6,−0.8)
find the third axis: x=y×z=(1,0,0)
for the rotation matrix 10000.6−0.800.80.6
And yes, I did pick this example carefully so that the math would
work out without any radicals or long decimals.
3 Point-at constraints
Sometimes constraints omit the roll-toward part, which in turn leaves
the rotation matrix under-constrained. The usual implication is the
smallest possible change that achieves this point-at. In general,
smallest possible changes require solving linear systems of equations
but for this particular question we can do something simpler.
3.1 Matrix
The minimal change that points an axis to a point is a rotation. to
minimize change, the axis of that rotation should be perpendicular to
the axis and the target point. One way of computing a rotation is by a
rotation angle θ and unit-length
rotation axis r; the matrix for
that rotation is rx2(1−c)+crxry(1−c)+rzsrxrz(1−c)−rysrxry(1−c)−rzsry2(1−c)+cryrz(1−c)+rxsrxrz(1−c)+rysryrz(1−c)−rxsrz2(1−c)+c where c=cos(θ) and s=sin(θ).
The smallest rotation does not move points that are perpendicular to
the old and new axis positions. That means that r is perpendicular to both the old axis
a and the target point p, meaning it can be found by a cross
product. A cross product will also give us the sine of the angle (s), and a dot product can give us the cosine
of the angle (c), which is all we need.
Ergo, we have
a′=∥a∥a
p′=∥p∥p
r′=a′×p′
c=a′⋅p′
s=∥r′∥
r=sr′
which, combined with the matrix above, gives us the minimal rotation
to point a at p.
The axis-angle rotation (r,θ) represents the same rotation as the quaternion ⟨cos(2θ);sin(2θ)r⟩ (assuming
unit-length r) where ⟨w;v⟩ is
shorthand for ⟨w;vx,vy,vz⟩.
The half-angle identities cos(2θ)=21+cos(θ) and sin(2θ)=21−cos(θ) and the fact that multiplying a quaternion by
a scalar does not change the rotation it represents lets us re-write
that as ⟨1+cos(θ);1−cos(θ)r⟩.
If we want to rotate from a unit vector a′ to a unit vector p′ then the cosθ is a′⋅p′ and the axis is sin(θ)1a′×p′. Recall the identity that sin(θ)=(1+cos(θ))(1−cos(θ)) to get ⟨1+a′⋅p′;(1+a′⋅p′)(1−a′⋅p′)1−a′⋅p′a′×b′⟩.
Simplifying the fraction and multiplying the entire quaternion by
1+a′⋅p′ gives us ⟨1+a′⋅p′;a′×p′⟩. For non-normalized inputs we simply multiply
through by the magnitudes of the inputs to get the general form
quaternion ⟨∥a∥∥p∥+a⋅p;a×p⟩
To point the +y axis a=(0,1,0) at p=(3,12,4)
we’d compute