SpacesLibrary

From Odwiki

Jump to: navigation, search


Contents

Tools of the trade

Here's a brief look at the tools that VEX makes available to us for working with spaces. Wherever they exist, their VOP equivalent is also listed. We'll look at examples of usage later on, but for now, we'll just list them all so as to have them handy as a reference.

We can roughly divide the available functions into two categories: the set that transform 3- or 4-dimentional vectors (which, again, could represent points, normals, directional vectors, homogeneous points, etc.), and the group that deals directly with matrices. And even though the colour-transformation functions should technically also be included (they also transform vectors), we won't be mentioning them here.

One key quality that sets the two groups appart, is that the vector functions only perform transformations to and from VEX's predefined spaces (the ones listed in the previous section), whereas the matrix functions give you the flexibility of working with any arbitrary space -- including spaces that we may create ourselves "from scratch".

Vector Functions

TRANSFORMATION FUNCTION OPERATOR NOTES
World to Object vector4 wo_space(vector4)
vector wo_space(vector)
Space Change
World To Object
Use for transforming points only.
vector wo_vspace(vector)
vector4 wo_vspace(vector4)
Direction Space Change
Direction Other Than Normal
World To Object
Use for transforming directional vectors only.
vector wo_nspace(vector)
vector4 wo_nspace(vector4)
Direction Space Change
Normal Vector
World To Object
Use for transforming normals only.
Object to World vector4 ow_space(vector4)
vector ow_space(vector)
Space Change
Object To World
Use for transforming points only.
vector ow_vspace(vector)
vector4 ow_vspace(vector4)
Direction Space Change
Direction Other Than Normal
Object To World
Use for transforming directional vectors only.
vector ow_nspace(vector)
vector4 ow_nspace(vector4)
Direction Space Change
Normal Vector
Object To World
Use for transforming normals only.
World To Texture vector4 wt_space(vector4)
vector wt_space(vector)
Space Change
World To Texture
Use for transforming points only.
vector wt_vspace(vector)
vector4 wt_vspace(vector4)
Direction Space Change
Direction Other Than Normal
World To Texture
Use for transforming directional vectors only.
vector wt_nspace(vector)
vector4 wt_nspace(vector4)
Direction Space Change
Normal Vector
World To Texture
Use for transforming normals only.
Texture To World vector4 tw_space(vector4)
vector tw_space(vector)
Space Change
Texture To World
Use for transforming points only.
vector tw_vspace(vector)
vector4 tw_vspace(vector4)
Direction Space Change
Direction Other Than Normal
Texture To World
Use for transforming directional vectors only.
vector tw_nspace(vector)
vector4 tw_nspace(vector4)
Direction Space Change
Normal Vector
Texture To World
Use for transforming normals only.
<Context> To NDC vector toNDC(vector) To NDC
For surface and displacement shaders,
the transformation is from World to NDC.
For light and shadow shaders,
the transformation is from Object to NDC.
This is only meaningful for points.
NDC To <Context> vector fromNDC(vector) From NDC
For surface and displacement shaders,
the transformation is from NDC to World.
For light and shadow shaders,
the transformation is from NDC to Object.
This is only meaningful for points.
World To
Named Space

Geometry and Null Objects

vector otransform(string,vector); Space Change To Object
Geometry/Null
Position
Use for transforming points only.
vector ovtransform(string,vector); Space Change To Object
Geometry/Null
Direction Other Than Normal
Use for transforming directional vectors only.
vector ontransform(string,vector); Space Change To Object
Geometry/Null
Normal Vector
Use for transforming normals only.

Light Objects

vector ltransform(string,vector); Space Change To Object
Light
Position
Use for transforming points only.
vector lvtransform(string,vector); Space Change To Object
Light
Direction Other Than Normal
Use for transforming directional vectors only.
vector lntransform(string,vector); Space Change To Object
Light
Normal Vector
Use for transforming normals only.

Fog (Atmosphere) Objects

vector ftransform(string,vector); Space Change To Object
Fog
Position
Use for transforming points only.
vector fvtransform(string,vector); Space Change To Object
Fog
Direction Other Than Normal
Use for transforming directional vectors only.
vector fntransform(string,vector); Space Change To Object
Fog
Normal Vector
Use for transforming normals only.

The most immediate thing to note from the table above, is the fact that none of these functions return a matrix. They all take a vector, manipulate it, and return it to us in its transformed state. This means that if we wanted to, say, pass the "toNDC" matrix from one of our light shaders to one of our surface shaders, we wouldn't be able to do it by using the above functions.

What I call "named spaces" are simply spaces that have a label. In this case, the label happens to be the name of an object. e.g: /obj/geo1, /obj/light1, etc. And for reasons that are internal to Mantra and/or the scene description (IFD), VEX requires us to make a distinction between the different types of objects; so that there are separate functions for each type. But don't let this proliferation of functions confuse you: they all transform to the space of an existing, named object.

Now let's have a look at the matrix functions.

Matrix Functions

FUNCTION OPERATOR NOTES
matrix maketransform
(int trs, xyz; vector t, r, s, p)
matrix maketransform
(int trs, xyz; vector t, r, s)
Make Transformer
Transform (indirectly)
UV Transform (indirectly)
Builds a 4x4 transform matrix from separate
vectors representing translation, rotation,
scale, and (optionally) pivot.
Obviously only useful if these quantities are
known. The returned matrix should only be
used for transforming points.
matrix otransform(string name);
matrix ltransform(string name);
matrix ftransform(string name);
N/A
Use the Inline VOP
As with their vector counterparts, these
create the matrix for transforming
from world space to the space of a
geometry, light, or fog object respectively.
The returned matrix should only be used for
transforming points.
matrix3 lookat( vector from, to, up)
matrix3 lookat( vector from, to; float roll)
matrix3 lookat( vector from, to)
Look At Builds a 3x3 rotation matrix (i.e: no
translation) from a reference "look at" vector.
A system under this transformation will
have its +Z axis pointing in the
"look at" direction. The resulting system
can optionally be "rolled" about its Z axis in
one of two ways: through
an "up" vector reference, or through
an explicit angle of rotation (in radians!).
The returned matrix should only be used for
transforming points.
Multiplication Operator * Multiply
vector * matrix
vector * matrix3
vector4 * matrix
Multiplying a vector times a matrix (or a matrix3)
is equivalent to "transforming" the vector.
If the matrix used in the operation is
any one of the matrices returned by any one
of the functions listed in this table, then
the vector being transformed should represent
a point! (an incorrect transformation will
result if it represents a vector or a normal!).
vector vtransform(vector, matrix) N/A
Use the Inline VOP
The multiplication operator "*" can be used to
transform points, but it can be somewhat
dangerous (unless you know what you're doing)
for transforming directional vectors.
For transforming directional vectors (by a matrix),
use this function instead.
void rotate( matrix &; float amount; vector axis)
void rotate( matrix3 &; float amount; vector axis)
void scale( matrix &; vector scale_vector)
void scale( matrix3 &; vector scale_vector)
void translate( matrix &; vector4 amount)
void translate( matrix &; vector amount)
Rotate
Scale
Translate
The three fundamental transformations available
separately so they may be applied (composed)
as needed. All angles are in radians!
Note that they all take a matrix, and apply
their respective transformations directly onto it.
In other words, the matrix that is given
as a parameter is modified by these functions.

There are other matrix functions, but they are not directly related to building spaces, so I've left them out for now.

The first thing that should jump out after a quick glance at the table of matrix functions, is that these don't seem to have three versions of each type of operation (one for points, one for directional vectors, and one for normals), whereas the vector functions do. There's only one exception: the function vtransform(), which is a specialization for directional vectors (and has - at time of writing - no direct equivalent in VOPs). So, what's going on? Is it because they don't need them?

Early on in these notes I mentioned that, even though points, vectors, and normals are all stored as vectors, they still represent different things, and so should be treated differently. This is especially true when transforming them. So yes, different versions of the same matrix are needed to properly transform each type of geometric element. But VEX doesn't provide us with these extra versions. All the listed functions (again, with the exception of vtransform) return the full matrix; and this is as it should be.

You see; in reality, the matrices that transform directional vectors and normals, are really special versions of the full matrix (the one that transforms points -- also known as the model matrix). They are specializations that take into account the properties of the entity being transformed. But the "real thing" is the model matrix, not the specialized versions. Still; we need to work with vectors and normals just as often as with points (which is why the vector functions come in all those flavours).

The first order of business then, is to apply some of the things we already know from these notes, along with some concepts covered in the Matrix page, and extend the suite of matrix functions so that henceforth we can comfortably forget about this little complication.


Extending the matrix functions

Since all the built-in functions return the full matrix, we can safely assume that we will have that version always available. What we need then, is a couple of functions that can convert the full matrix into a form that is appropriate for transforming vectors and normals. And the first thing we need to do to achieve this, is to define exactly what it is that makes these matrices different.

As mentioned earlier in these notes, "Points are sensitive to all forms of transformation: translation, rotation, scaling, etc.". This means that points get transformed by the full matrix; and since that's the matrix that the built-in functions return, we can go ahead and use it "as-is" to transform points -- points need no special handling.

When talking about directional vectors however, I mentioned that "...we never consider translations when transforming them from one space to another.". So, for vectors, we use all aspects of the transformation except for translations. This means we need to somehow remove translations from the full matrix if we intend to use it to transform directional vectors. But where in the matrix are translations defined?

Image:Mgm_space_0003.jpg
Figure 3: The locations of the x, y, and z translations in a row major matrix..

For a more in-depth explanation, please see the Matrix page, but briefly, in VEX, matrices are in "row-major" order, so translations can be found in the locations M41, M42, and M43 (see figure 3). Also note that translations only exist in 4x4 matrices; 3x3 matrices (corresponding to the matrix3 type in VEX) don't support translations and so can be used to transform either points or vectors without modification.

Armed with this information, we can sketch a VEX function to do the conversion from a point-matrix to a vector-matrix:

// Converts a 4x4 point-matrix to a 4x4 vector-matrix
matrix vmatrix ( matrix m ) {
   return (matrix3)m;
}

// Converts a 3x3 point-matrix to a 3x3 vector-matrix
matrix3 vmatrix3 ( matrix3 m ) {
   return m;
}

The conversion is very simple: it strips the last row (where the transforms are) by casting it to a 3x3 matrix, and then relies on automatic type conversion (from matrix3 back to matrix) to return a 4x4 matrix. The resultant matrix has the last row and column set to R4 = C4 = {0,0,0,1}, and all translations removed.

Here I've made the choice of having the functions return a matrix instead of directly modifying the matrix passed in as the parameter m. This may be a tiny bit wasteful in that it could potentially create a temporary value (although that depends on the expression and VEX's optimizer), but it also means that we can nest it and chain it with other expressions on the right hand side of an assignment, and, in my opinion, that is by far the most frequent use of such a converter function.

Note that I've also included a version for converting a 3x3 matrix that does absolutely nothing. This dummy function is there for completeness, and because this way, you don't have to remember that 3x3 matrices don't need to be converted for vectors. After compilation and optimization it should dissapear from the code and create no run-time overhead whatsoever; but it should help a great deal with our own sanity. (It is also not defined as a macro just to impose a tiny bit of type-safety by allowing the compiler to catch improper usage)

Converting the model matrix into a form suitable for transforming normals is a little bit trickier. Again; please refer to the Matrix page for the more gory mathematical details, or, if this stuff doesn't make sense, just copy the code given below, and have it handy for your own use -- i.e: you don't need to understand it to use it.

Normals, like vectors, don't get translated when transformed. But normals are special vectors: they are an implicit representation of the tangent space of the surface at a point -- i.e: they uniquely describe the tangent space at a point (in 3D, the tangent space is a plane, in 2D, a line). By definition then, normals must always remain perpendicular to this tangent space. We will skip the derivation here, but it is this "must remain perpendicular" requirement which gives rise to the special handling that normals get when being transformed.

To transform normals then, we need to use the transpose of the inverse of the linear part of the model matrix (where the "linear part" of the model matrix is just the upper 3x3 submatrix). If the model matrix is M, then the version we need to transform normals is (M-1)T. But since we know that we will generally need to normalize the transformed normal anyway, we can use the adjoint instead of the full inverse -- as long as we understand that lengths are not preserved by this transformation. Here it is in VEX:

// Converts a 3x3 model matrix into a 3x3 normal matrix
// by computing the transposed adjoint of the input matrix
matrix3 nmatrix3(matrix3 M) {
   float  m00,m01,m02, m10,m11,m12, m20,m21,m22;
   assign(m00,m01,m02, m10,m11,m12, m20,m21,m22, M);
   return matrix3( set(
      m11*m22 - m12*m21 , m12*m20 - m10*m22 , m10*m21 - m11*m20,
      m21*m02 - m22*m01 , m22*m00 - m20*m02 , m20*m01 - m21*m00,
      m01*m12 - m02*m11 , m02*m10 - m00*m12 , m00*m11 - m01*m10
   ) );
}

NOTE: I have yet to determine if the above is in fact faster than the literal transpose(inverse(M)). My gut says yes, but I haven't tested it yet...hmmm... that's 18 multiplies and 9 subtracts plus assignment.... gotta test it....(Mark?)

To be continued...

See Also

© 2009 od[force].net | advertise