Cg Programming/Unity/Rotations

From Wikibooks, open books for an open world
Jump to: navigation, search
Orientation (or “attitude”) of a plane measured by bank (= Z = φ), elevation (= X = θ), and heading (= Y = ψ). (The axes are labeled differently in the main text.)

This tutorial discusses different representations of local rotations in Unity. It is based on Section “Vertex Transformations”.

This is the first of a few tutorials that demonstrate useful functionality of Unity that is not directly associated with shaders. Therefore, the presented code is in JavaScript.

Unity's Euler Angles[edit]

If you create a GameObject and select it, the Inspector will show three values X, Y, Z under Transform > Rotation. These are three Euler angles measured in degrees. (More precisely spoken, they are one possible set of three Tait-Bryan angles, or nautical angles or Cardan angles, since they describe rotations about three different axes while “proper Euler angles” would describe a set of three angles that describe three rotations where two rotations are about the same axis.)

In JavaScript you can access these three angles as the Vector3 variable Transform.localEulerAngles. The documentation states that the angles represent — in this order — a rotation by Z degrees about the z axis, X degrees about the x axis, and Y degrees about the y axis. More precisely spoken, these are rotations about the fixed(!) axes of the parent object (or the world axes if there is no parent object). Since these rotations use fixed axes, they are also called “extrinsic rotations”.

These three angles can describe any rotation of an object in three dimensions. In the case of Transform.localEulerAngles they actually describe the orientation of an object relative to the parent's coordinate system (or the world's coordinate system if there is no parent). Rotation in the sense of a rotating motion is discussed below.

In aviation, Euler angles are used when the orientation of a plane is specified relatively to the fixed axes of the ground (e.g. a tower of an airport) as illustrated in the figure above. In this case, they are called “bank” (corresponding to Z), “elevation” (corresponding to X), and “heading” (corresponding to Y). The following JavaScript code can be attached to an object to set the Euler angles in terms of these names. (In the Project view select Create > Javascript, double-click it to open it, copy & paste the code from below into the script, and drag the script from the Project view over the game object in the Hierarchy view, then select the game object and find the public variables of the script in the Inspector.)

@script ExecuteInEditMode() 
#pragma strict
 
public var bankZ : float;
public var elevationX : float;
public var headingY :float;
 
function Update() 
{
   transform.localEulerAngles = 
      Vector3(elevationX, headingY, bankZ);
}

Interact with the three variables in the Inspector to get familiar with their meaning. You could start with all three angles set to 0. Then change bank, elevation, and heading (in this order) and observe how the object rotates about the parent's (or world's) z axis (the blue axis in Unity) when changing bank, about the parent's x axis (red) when changing elevation, and about the parent's y axis (green) when changing heading.

Rotation of the vector (1,0) by angle t.

Computing the Rotation Matrix[edit]

In order to establish the connection with the elementary model matrices in Section “Vertex Transformations”, this subsection presents how to compute a rotation matrix from the three angles X, Y, Z (or θ, ψ, and φ).

The 4×4 rotation matrix \mathrm{R}_{z}(\varphi) for a rotation by an angle φ (= Z) about the z axis is:

\mathrm{R}_{z}(\varphi) = \left[ \begin{matrix}
\cos\varphi & -\sin\varphi & 0 & 0 \\
\sin\varphi & \cos\varphi & 0 & 0 \\
0 & 0 & 1 & 0 \\
0 & 0 & 0 & 1 
\end{matrix} \right]

The first two coordinates of the leftmost column can be understood as the rotation of the vector (1,0) by an angle \varphi, which results in the rotated vector (\cos\varphi, \sin\varphi). (The illustration uses the angle t; thus, the result is (\cos t, \sin t).) Similarly, the first two components of the next column can be understood as the rotation of the vector (0,1) by the angle \varphi with the result (-\sin\varphi, \cos\varphi).

Analogously, the 4×4 rotation matrix \mathrm{R}_{x}(\theta) for a rotation by an angle θ (= X) about the x axis is:

\mathrm{R}_{x}(\theta) = \left[ \begin{matrix}
1 & 0 & 0 & 0 \\
0 & \cos\theta& -\sin\theta & 0 \\
0 & \sin\theta & \cos\theta& 0 \\
0 & 0 & 0 & 1 
\end{matrix} \right]

And the 4×4 rotation matrix \mathrm{R}_{y}(\psi) for a rotation by an angle ψ (= Y) about the y axis is:

\mathrm{R}_{y}(\psi) = \left[ \begin{matrix}
 \cos\psi & 0 & \sin\psi & 0 \\
0 & 1& 0 & 0 \\
-\sin\psi & 0 & \cos\psi& 0 \\
0 & 0 & 0 & 1 
\end{matrix} \right]

Note that the sign of the sine terms depend on some conventions. Here we use the conventions of Unity.

These three matrices have to be combined in one matrix product to form the total rotation. Since (column) vectors are multiplied from the right to transformation matrices, the first rotation \mathrm{R}_{z}(\varphi) has to be in the rightmost place and the last rotation \mathrm{R}_{y}(\psi) has to be in the leftmost place. Thus, the correctly ordered matrix product is:

\mathrm{M}_\text{rotation}(\varphi, \theta, \psi) = \mathrm{R}_{y}(\psi) \mathrm{R}_{x}(\theta) \mathrm{R}_{z}(\varphi)

The matrix \mathrm{M}_\text{rotation}(\varphi, \theta, \psi) can then be multiplied with other elementary transformations matrices to form the model matrix as discussed in Section “Vertex Transformations”.

In Unity, you could compute a 4×4 matrix m for the Euler angles X, Y, Z in this way (quaternions are discussed in more detail below):

var m : Matrix4x4 = Matrix4x4.TRS(Vector3(0,0,0), 
   Quaternion.Euler(X, Y, Z), Vector3(1, 1, 1));
No gimbal lock: the axes of the three gimbals are different.
Gimbal lock: the axes of two gimbals are parallel.

Gimbal Lock[edit]

One interesting feature of Euler angles (and one reason why Unity and most other graphics applications don't use them internally) is that they can result in a situation that is called “gimbal lock”. In this situation, two of the rotation axes are parallel and, therefore, only two different rotation axes are used. In other words, one degree of freedom is lost. This happens with Unity's Euler angles if the 2nd rotation (elevation) is X = ±90°. In this case, the z axis is rotated onto the y axis; thus, the (rotated) first rotation axis is the same as the (fixed) third rotation axis.

The situation is easily constructed with the script from above if you set the elevation angle to 90° or -90°.

An articulated robot: the rotations about z, y, and x are referred to as yaw, pitch, and roll. (In the main text, these axes are labeled y, x, and z.)

Moving Rotation Axes[edit]

In some cases, for example robotics, it is more interesting to work with moving rotation axes (i.e. intrinsic rotations) instead of the fixed rotation axes (i.e. extrinsic rotations) discussed so far. Usually, the rotation relative to the base of the robot is referred to as “yaw”, the up-down rotation of the arm as “pitch”, and the next rotation as “roll”. Since the joints are rotated by each rotation, a description of rotations with moving rotation axes is required.

Fortunately, there is a simple equivalence: the extrinsic rotations specified by Z, X, and Y correspond to the following three intrinsic rotations (in this order): a rotation about the y axis by Y (“yaw”), a rotation about the rotated x axis by X (“pitch”), and a rotation about the rotated z axis by Z (“roll”). In other words, the rotation angles for moving rotation axes are the same as for fixed rotation axes if they are applied in reverse order (Y, X, Z instead of Z, X, Y). For most people, this equivalence is not intuitive at all; however, you might gain a better understanding by playing with the following script that lets you specify yaw, pitch, and roll.

@script ExecuteInEditMode() 
#pragma strict
 
public var yawY : float;
public var pitchX : float;
public var rollZ :float;
 
function Update() 
{
   transform.localEulerAngles = 
      Vector3(pitchX, yawY, rollZ);
}
Aircraft principal axes: yaw, pitch, and roll.

Roll, Pitch, and Yaw in Aviation[edit]

Roll, pitch, and yaw are also used to describe the orientation of vehicles; see the Wikipedia article on “Axes Conventions”. Moreover, the principal axes of an aircraft are also called roll (z axis in Unity), pitch (x axis in Unity), and yaw (y axis in Unity). These axes are always fixed to the aircraft; thus, rolling, pitching, and yawing always refers to rotations about these aircraft principal axes regardless of the orientation of the aircraft. The mathematics of these rotations is therefore different from the Euler angles of the same name. In fact, the rotation about aircraft principal axes is more similar to the rotation about arbitrary axes discussed next.

Rotation about an Axis by an Angle[edit]

As mentioned in Section “Vertex Transformations”, the rotation matrix for a rotation about a normalized axis (x, y, z) by an angle α is:

\mathrm{M}_{\text{rotation}}(x,y,z,\alpha) = \left[ \begin{matrix}
(1-\cos\alpha) x\,x + \cos\alpha    & (1-\cos\alpha) x\,y - z \sin\alpha   & (1-\cos\alpha) z\,x + y \sin\alpha  & 0 \\
(1-\cos\alpha) x\,y + z \sin\alpha  & (1-\cos\alpha) y\,y + \cos\alpha     & (1-\cos\alpha) y\,z - x \sin\alpha  & 0 \\
(1-\cos\alpha) z\,x - y \sin\alpha  & (1-\cos\alpha) y\,z + x \sin\alpha   & (1-\cos\alpha) z\,z + \cos\alpha    & 0 \\
0                                   & 0                                    & 0                                   & 1 
\end{matrix} \right]

Fortunately, this matrix is almost never needed when working with Unity. Instead, the object's local “rotation” can be set to a rotation about axis by angle in degrees in this way:

@script ExecuteInEditMode() 
#pragma strict
 
public var angle : float;
public var axis : Vector3;
 
function Update() 
{
   transform.localRotation = 
      Quaternion.AngleAxis(angle, axis);
}

Actually, this sets the orientation of the object relative to its parent (or to the world if there is no parent). In order to rotate about a given axis at a given angular velocity in degrees per second, the function Transform.Rotate can be used:

#pragma strict
 
public var degreesPerSecond : float;
   // angular speed
public var axis : Vector3;
 
function Update() 
{
   transform.Rotate(axis, 
      degreesPerSecond * Time.deltaTime,
      Space.Self);
}

degreesPerSecond is an angular speed, which specifies how fast the object is rotating. However, Transform.Rotate requires the rotation angle in degrees. To compute this angle, the angular speed has to be multiplied with the time (in seconds) that has passed since the last call to Update, which is Time.deltaTime.

Furthermore, Transform.Rotate takes a third argument that specifies the coordinate system in which the rotation axis is specified: either Space.Self for the object's local coordinate system or Space.World for the world coordinate system. With Space.Self we can easily simulate rolling, pitching, and yawing by specifying the axes (1,0,0) for pitching,(0,1,0) for yawing, and (0,0,1) for rolling.

Quaternions[edit]

As you might have noticed in the code above, Unity is using quaternions (actually normalized quaternions) to represent rotations. For example, the variable Transform.localRotation is of type Quaternion.

In some sense, quaternions are simply four-dimensional vectors with some special functions. Normalized quaternions of length 1 correspond to rotations and are easy to construct when you know the normalized rotation axis (x, y, z) and the rotation angle α. The corresponding quaternion q is just:

q = (x_q, y_q, z_q, w_q) = (x \sin(\alpha/2), y \sin(\alpha/2), z \sin(\alpha/2), \cos(\alpha/2))

In Unity, however, you can just use the constructor Quaternion.AngleAxis(alpha, Vector3(x, y, z)) with alpha in degrees to construct the normalized quaternion. (See the previous subsection for an example.)

Conversely, a normalized quaternion q with components (x_q, y_q, z_q, w_q) corresponds to a rotation by the angle 2 \arccos(w_q). The direction of the rotation axis can be determined by normalizing the 3D vector (x_q, y_q, z_q). In Unity, you can use the function Quaternion.ToAngleAxis to compute the corresponding axis and angle.

As normalized quaternions correspond to rotations, they also correspond to rotation matrices. In fact, the product of two normalized quaternions corresponds to the product of the corresponding rotation matrices in the same order. Computing the product of two quaternions is actually a bit tricky but in Unity you can just use the * operator. In the case of products of quaternions (and also inverse quaternions), it is therefore useful to think of a quaternion as “something like a rotation matrix” instead of a four-dimensional vector.

For example, the code for the rotation at a given angular speed about an axis in the object's local coordinate system would require to multiply — in this order — the previous rotation matrix (which specifies the previous orientation) with the new (incremental) rotation matrix. The quaternion for the previous orientation is transform.localRotation and the new incremental quaternion is called q in this code:

#pragma strict
 
public var degreesPerSecond : float;
   // angular speed
public var axis : Vector3;
 
function Update() 
{
   var q : Quaternion = Quaternion.AngleAxis(
      degreesPerSecond * Time.deltaTime, axis);
   transform.localRotation = 
      transform.localRotation * q;
}

If the quaternion product in the last line is changed to

   transform.localRotation = 
      q * transform.localRotation;

it corresponds to applying the incremental rotation in the parent's coordinate system (or the world's coordinate system if there is no parent) just like one would expect for applying a rotation matrix after transforming the vertices into the parent's coordinate system. (Remember that matrix products should be read from right to left because column vectors are multiplied from the right.)

At least in computer graphics, (normalized) quaternions are therefore rather harmless and never more complicated than rotation matrices or axis-angle representations (depending on the context). Their specific advantages are that they show no gimbal lock (as opposed to Euler angles), they can be easily combined by multiplication (as opposed to Euler angles and the angle-axis representation of rotations), and they are easy to normalize (as opposed to the orthogonalization of rotation matrices). Therefore, most graphics applications use quaternions internally to represent rotations — even if Euler angles are used in the user interface.

Summary[edit]

In this rather theoretical tutorial, we have looked at:

  • Unity's Euler angles for extrinsic rotations (i.e., elevation, heading, and bank).
  • Unity's Euler angles for intrinsic rotations (i.e., pitch, yaw, and roll).
  • The axis-angle representation of rotations.
  • The quaternion representation of rotations.

Further reading[edit]

If you want to know more

< Cg Programming/Unity

Unless stated otherwise, all example source code on this page is granted to the public domain.