H3DU’s Math Functions
The HTML 3D library includes a collection of math functions for working with vectors, matrices, and quaternions.
Here is an overview of these data types.
Contents
Vectors
A vector is a line segment pointing in a certain direction in space and having a certain length and an unspecified starting point. A particular vector can instead be treated as describing a position (by pointing to that position from an origin (0,0,0)), or a color.
In MathUtil
, vectors are stored in arrays of numbers (usually
three or four numbers), and functions dealing with vectors begin
with “vec”.
If a 4-element vector describes a position or direction, the elements are given as X, Y, Z, and W, in that order.
If a 4-element vector describes a color, the elements are given as red, green, blue, and alpha, in that order (where each element ranges from 0-1).
If a 3D direction is used in a 4-element vector function (one beginning with “vec4”), use 0 as the fourth element. If a 3D position (point) is used in a 4-element vector function, the fourth element is generally 1. (If the fourth element is anything other than 0, the vector is in homogeneous coordinates, where the 3D position equals the first three elements divided by the fourth.)
Unit Vectors
A unit vector is a vector with a length of 1. (A vector’s length, or norm, is the square root of the sum of the squares of its components.) A vector can be “normalized” to a unit vector by dividing each of its components by its length (doing so won’t change the vector’s direction).
The following functions normalize vectors and find their length.
- MathUtil.vec3normalize - Converts a 3-element vector to a unit vector.
- MathUtil.vec4normalize - Converts a 4-element vector to a unit vector.
- MathUtil.vec3length - Finds a 3-element vector’s length.
- MathUtil.vec4length - Finds a 4-element vector’s length.
Note that due to rounding error, normalizing a vector with a MathUtil
method
might not necessarily result in a vector with a length of 1.
Matrices
A matrix is a rectangular array that can describe a transformation from one coordinate system to another. Transformations include translation (shifting), scaling, and rotation. Functions dealing with matrices begin with “mat”. A 3x3 or 4x4 matrix has 9 or 16 elements, respectively. For more details, see the Matrix Details tutorial.
Translation
A translation is a shifting of an object’s position.
To create a translation matrix, use MathUtil.mat4translated(), and specify the X-offset, the Y-offset, and the Z-offset. For example, an X-offset of 1 moves an object 1 unit to the right, and a Y offset of -1 moves it 1 unit down.
To multiply an existing matrix by a translation, use MathUtil.mat4translate(). This will put the translation before the other transformations.
Scaling
Scaling changes an object’s size.
To create a scaling matrix, use MathUtil.mat4scaled(), and specify the scaling factors for the X, Y, and Z axis. Each point is multiplied by the scaling factors to change the object’s size. For example, a Y-factor of 2 doubles an object’s height.
To multiply an existing matrix by a scaling, use MathUtil.mat4scale(). This will put the scaling before the other transformations.
Rotation
Rotation changes an object’s orientation.
To create a rotation matrix, use MathUtil.mat4rotated(), and specify the angle (in degrees) to rotate, and the axis of rotation. For example:
- Specifying
(45, [1, 0, 0])
means a 45-degree rotation of the point around the X axis. - Specifying
(80, [0, 2, 3])
means a 45-degree rotation of the point around the axis that starts at the origin (0, 0, 0) and points toward the point (0, 2, 3).
When describing an axis of rotation, [1, 0, 0]
is the X axis,
[0, 1, 0]
is the Y axis, and [0, 0, 1]
is the Z axis.
To multiply an existing matrix by a rotation, use MathUtil.mat4rotate(). This will put the rotation before the other transformations.
Combining Transforms
The order in which you do transforms is important. In general, scaling then translating is not the same as translating then scaling. Assuming your geometry is centered at the origin (0, 0, 0), you should create a transformation in this order:
- Call
MathUtil.mat4identity()
, creating a matrix without a transformation. - Do your translations if needed, using
mat4translate()
. - Do your rotations if needed, using
mat4rotate()
. - Do your scalings if needed, using
mat4scale()
.
This way, the scalings and rotations will affect the object while it’s still centered, and before the translations (shifts) take place.
You can also multiply transforms using MathUtil.mat4multiply(). This takes two matrices and returns one combined matrix. The combined matrix will have the effect of doing the second matrix’s transform, then the first matrix’s transform.
Describing Rotations
Rotations in 3D space can be described in many ways, including quaternions, Tait-Bryan angles, and an angle and axis.
Axis of Rotation
A rotation of vectors or points can be described using an angle and an axis of rotation, for example, in the MathUtil.mat4rotate method.
An axis of rotation is a vector pointing in a certain direction. When a point (or vector) is rotated at any angle around this axis, the new point (or vector) will lie on the same plane as the previous point. The axis of rotation describes a vector that is perpendicular to that plane’s surface (the plane’s normal). Here are examples of an axis of rotation.
- The X axis of rotation (upward or downward turn) is (1, 0, 0).
- The Y axis of rotation (leftward or rightward turn) is (0, 1, 0).
- The Z axis of rotation (side-by-side sway) is (0, 0, 1).
While the axis of rotation points backward from the “eye”, if the angle’s value is positive and the coordinate system is…
- …right handed, then the angle runs counterclockwise.
- …left handed, then the angle runs clockwise.
While the axis of rotation points backward from the “eye”, if the angle’s value is negative, then the angle runs in the opposite direction.
Vectors that point in the same direction (for example, vectors (1, 0, 0) and (2, 0, 0)) describe the same axis of rotation.
Unless stated otherwise, an axis of rotation passed to a MathUtil
method need not be a unit vector.
Quaternions
A quaternion is a 4-element vector that can describe a 3D rotation. Functions dealing with quaternions begin with “quat”.
Generating Quaternions
Functions that generate quaternions include:
- MathUtil.quatIdentity - Generates a quaternion describing an absence of rotations.
- MathUtil.quatFromVectors - Generates a quaternion describing a rotation from one vector to another.
- MathUtil.quatFromMat4 - Generates a quaternion from a 4x4 matrix.
- MathUtil.quatFromAxisAngle - Generates a quaternion from an angle and axis of rotation.
- MathUtil.quatFromTaitBryan - Generates a quaternion from Tait-Bryan angles.
Using Quaternions
For best results when using quaternions:
- Store the rotation of each object as a single quaternion.
- As rotations happen each frame, convert the rotation (which may be in pitch/yaw/roll or another form, depending on the input device) to a quaternion (see “Generating Quaternions” and multiply that quaternion by the current quaternion to get the object’s new rotation.
- Normalize the rotation quaternion (using
quatNormalize()
orquatNormalizeInPlace()
) every few frames. (Quaternions that describe a 3D rotation should be unit vectors.)
Multiplying Quaternions
When two quaternions are multiplied (for example, with {@MathUtil.quatMultiply}), the result is a combined rotation in which the second rotation happens before the first rotation (when applied in the global coordinate frame). Like matrix multiplication, the order in which you multiply quaternions is important.
Tait-Bryan angles
Pitch-yaw-roll angles (also called Tait-Bryan angles) describe three different rotations of the same vector around three different axes, called the pitch, yaw, and roll axes (or the X, Y, Z axes, respectively), which occur one after the other. However:
- There are multiple conventions for pitch-yaw-roll angles, including the order of rotations (for example: pitch-roll-yaw, roll-pitch-yaw), and whether the rotations occur around the object’s original axes (“extrinsic”) or its new axes (“intrinsic”).
- Rotations are multiplied like in quaternions and matrices, so the order the rotations occur is important. For example, a 30-degree pitch followed by a 20-degree roll is not the same as a 20-degree pitch followed by a 30-degree roll.
- Pitch-yaw-roll angles can cause a problem called “gimbal lock”, in which a rotation along one axis (say, a pitch) can cause a vector to be parallel to another axis (say, the roll axis), so that a rotation along that axis will do nothing.
Related functions:
- MathUtil.quatFromTaitBryan() - Converts from Tait-Bryan angles to a quaternion
- MathUtil.quatToTaitBryan() - Converts from a quaternion to Tait-Bryan angles
4x4 Matrices
A 4x4 matrix can describe a 3D vector rotation; see “Rotation”, above.
Planes
A 4-element array can describe a plane in the following manner:
-
The 4 elements, labeled A, B, C, and D in that order, describe a plane whose points satisfy the equation—
Ax + By + Cz + D = 0
where x, y, and z are the coordinates of any point lying on the plane. * A, B, and C are the X, Y, and Z components of the plane’s normal vector. * D is the signed distance from the plane to the origin (0,0,0). It’s positive if the plane’s normal points toward the origin, and negative if it points away from the origin. * D is the negative dot product of the plane’s normal and any point on the plane.
There is one method that deals with planes:
- MathUtil.planeNormalizeInPlace - Converts the plane to a form in which its normal has a length of 1.
Boxes
An array of six numbers can describe an axis-aligned bounding box (AABB). If it does, the first three numbers are the box’s minimum X, Y, and Z coordinates, and the last three numbers are the box’s maximum X, Y, and Z coordinates.
If a minimum coordinate is greater than a maximum coordinate, then the box is considered empty.
Methods that deal with boxes include:
- MathUtil.boxCenter - Finds a box’s center.
- MathUtil.boxDimensions - Finds a box’s dimensions.
- MathUtil.boxIsEmpty - Determines whether a box is empty.
Coordinate Systems
There are two conventions of 3D coordinate systems, left-handed and right-handed:
- In a left-handed coordinate system, the positive Z axis points forward from the “eye” whenever the positive X axis points to the right and the positive Y axis points up.
- In a right-handed coordinate system, the positive Z axis points backward from the “eye” whenever the positive X axis points to the right and the positive Y axis points up.
To show this more visually, point one hand’s thumb to your right and its index finger up, and bend the other three fingers halfway down. In a coordinate system named after that hand (left-handed or right-handed), if the positive X axis points in the thumb’s direction and the positive Y axis points in the index finger’s direction, the Z axis will point in the direction the other three fingers point.
As used here, the Z axis is the cross product of two perpendicular axes, namely the X axis and the Y axis, in that order. Which of the X, Y, or Z axes is the right, up, or forward axis is arbitrary; for example, some conventions may have the Z axis, rather than Y, be the up axis. Therefore, these three axes are defined here to avoid confusion.
Differences in Behavior
Projection and view matrices
The difference between a left-handed and right-handed coordinate system affects how 3D points are transformed, mainly in the projection and view matrices. The projection and view matrices returned by Math matrix methods are designed for a right-handed coordinate system. Their documentation describes how to adjust them for a left-handed coordinate system.
Rotation angles (such as used in mat4rotate
and quatRotate
)
While the axis of rotation points backward from the “eye”, if the angle’s value is positive and the coordinate system is…
- …right handed, then the angle runs counterclockwise.
- …left handed, then the angle runs clockwise.
While the axis of rotation points backward from the “eye”, if the angle’s value is negative, then the angle runs in the opposite direction.
Cross product (vec3cross
) and normals
Given a triangle formed by…
- points (A minus C), (B minus C), and C, in that order, or
- points A, B, and (0, 0, 0), in that order,
the cross product of the first point with the second, in that order, is a normal of that triangle (a vector that’s perpendicular to the triangle’s surface).
While this particular normal points backward from the “eye”, the triangle’s vertices run in a counterclockwise path for right-handed coordinate systems, or a clockwise path for left-handed systems. (In general, there are two possible choices for normals, which each point in opposite directions.)
Winding and face classification
A two-dimensional triangle has counterclockwise winding if its vertices are ordered in a counterclockwise path from the first to second to third to first vertex. Otherwise, it has clockwise winding. If the triangle is in 3D space, it’s first transformed into 2D window coordinates before its winding is found. (Window coordinates roughly correspond to screen pixels.)
By default, in the WebGL pipeline, triangles with counterclockwise winding are front faces, and other triangles are back faces.
Finding a triangle’s winding
To find a triangle’s winding, do the following calculation (X1, X2, X3 and Y1, Y2, Y3 are the window coordinates of its vertices). Note that half of the result will be the triangle’s signed area.
(X3 - X1) * (Y3 - Y2) - (X3 - X2) * (Y3 - Y1)
If the result is positive, and the window space X axis points right and the positive Y axis points…
- …up (which is the case in WebGL), then the triangle has counterclockwise winding.
- …down, then the triangle has clockwise winding.
If the result is negative, then the triangle has the opposite winding.