|Prev: Inter shader comm.||Next: Interpolation Issues|
Before we start showing some shaders, we’ll talk a little bit about spaces, or referentials, and matrices. These are fundamental concepts since we usually operate in many spaces inside a pipeline, and establishing a common ground is important to understand what is really going on in a shader.
The four relevant spaces in CG for our purpose are:
- local space: the space where the models are created.
- world space: this is where we assemble our 3D scene
- camera space: the space where the camera is at the origin, looking down on the negative Z axis.
- clip space: this is the post projection space, where the view frustum is transformed into a cube, centered in the origin, and going from -1 to 1 in every axis.
To transform between these four spaces we have three 4×4 matrices:
- model matrix: to transform from local to world space;
- view matrix: to transform from world space to camera space.
- projection matrix: to transform from camera space to clip space.
For our convenience we are also going to consider two composite matrices:
The above matrices are only shortcuts allowing us to move between spaces faster, saving on the amount of adds and multiplications required to move from spaces that are not adjacent on the diagram.
Both model and view matrices are commonly constructed based on translation, rotations, and scales. These geometric transformations represent affine transformations, i.e. a transformation that preserves straight lines and ratios – if three points are in a straight line, they will remain in a straight line after being transformed and the transformed midpoint of the line will still be the midpoint of the transformed line. Furthermore, while it does not necessarily preserve angles or lengths, two parallel lines will remain parallel after being transformed.
The matrix form for a general affine transformation, using homogeneous coordinates, is a matrix whose last row is [0 0 0 1]. The last column contains the translation, and the top 3×3 submatrix the rotations and scales.
In here, we are only going to consider affine transformations when considering transformations between the first three spaces.
The projection matrix commonly represents a perspective projection which is not in the same class of transformations, as parallel lines do not necessarily stay parallel after projection. Hence perspective projections are not affine transformations.
Transformation between spaces (local, world, camera)
To transform a point, a four element tuple (x,y,z,1.0), between spaces we use the above matrices. For instance, to transform a point P from local to camera space we write:
Since we’re only considering affine transformations we know that the transformed point will have the general form (x’, y’, z’, 1.0).
Vectors, four element tuples of the general form (x, y, z, 0.0), are transformed using the same procedure, and the transformed vector will have the general form . In particular, as the fourth element of a vector is zero, we get an equivalent result using only the first three components of the vector and the top 3×3 submatrix.
The above applies to all vectors that can be expressed as a difference between two points. Considering such a vector, then transforming the vector is equivalent to transforming each of the points, and computing the transformed point difference.
Normal vectors are a particular case, as they can not be expressed in this way. A normal vector is a vector that has constant magnitude, 1.0, and a direction which is defined not as the difference between two points, but as a direction which is perpendicular to a surface.
Hence, to transform a normal vector we must use a matrix that transforms vectors preserving the property of being perpendicular to the transformed surface. So how do we compute such a matrix?
Consider the following vectors in the image below.
Vector T is tangent to the triangle edge, and can be defined as
As shown above, the transformation of T is equivalent to the difference of the transformed points, i.e.
Hence, the transformed vector, T’, remains tangent to the edge. On the other hand, the transformed normal vector, N’, is no longer perpendicular to the edge.
As mentioned before, when transforming vectors we can consider only the first three components of the vector, and the top 3×3 submatrix. We need a new matrix to transform vector N. A matrix that guarantees that, after being transformed, N’ remains perpendicular to T. Let’s call this 3×3 matrix G and let’s call the 3×3 matrix that transforms T as M.
We know that after transforming both vectors they must remain perpendicular, hence their dot product must be zero. Hence
Rewriting the dot product as a matrix product we have
Which we know it is true. Hence, the relation between G and M is:
Therefore, multiplying by the inverse of the transpose of M on both sides we get
Therefore, the normal matrix, matrix G, must the the inverse of the transpose of M, i.e. the inverse of the transpose of the top 3×3 submatrix of the 4×4 matrix used to transform points.
A matrix is said to be orthogonal if
If M is orthogonal then
which can simplify things in some cases.
An orthogonal matrix has all rows (columns) as unit length, and these are mutually perpendicular. Starting from an orthogonal matrix, such as the identity matrix, the geometric transformations for rotations and translations, preserve these properties. The same does not apply to scales, which change the magnitude of the row (column) vectors of the matrix.
Therefore, if we use only rotations and translations we guarantee that our matrices are always orthogonal, and we can transform all vectors, including normals, with the same matrix.
To ensure that the shaders that will be presented in this tutorial can handle scales, we are going to use the normal matrix (3×3) to transform normal vectors (3×1), and regular matrices (4×4) to transform all other vectors (4×1) and points.
|Prev: Inter shader comm.||Next: Interpolation Issues|