Efficiency Tips on Switching Spaces and Transformation Matrices in Unity

This article will help you choose between the different methods you have to perform a trasnformation of a point/vector/direction in Unity.

If you know nothing about spaces, switching spaces and transformation matrices, please read my small introduction to this article here.

Should I care which method I use to switch spaces?

This only matters truly if you are worried about efficiency and garbage collection. This means that it depends on what operation you perform and how many times. Othewise, it doesn't matter, but it is good to always choose the proper one beforehand in case you end up making it grow.

Space switch operations to use

Performing a space switch is the same as multiplying a 4x4 matrix to a Vector3. You can perform a space switch of a Vector3 using two methods basically:

1) Using a Matrix4x4 to apply the transformation

You can use the transformation matrix given by the Transform using localToWorldMatrix or worldToLocalMatrix.  Or you can build your own. I don't recommend this last one in general.

The Matrix4x4 class has these three methods to apply the transformation:

  • Matrix4x4.MultiplyPoint will apply rotation, translation and scale of the tranfsormation.
  • Matrix4x4.MultiplyVector will NOT apply the translation, so it is only used if you just care about the rotation and scale.
  • Matrix4x4.MultiplyPoint4x3 avoids some operations, but is similar to the first one. We will talk about this later.

2) Using the Transform class to apply the transformation

You can directly gather the Transform component of the object that represents the space you want to switch from or to, and use its TransformXXX methods.

Use "TransformXXX" if you want to change a Vector3 in world space to the local space of that transform , and "InverseTransformXXX" if you want to pass a Vector3 expressed in the space of that particular object, and get a Vector3 in world space.

The three methods available are:

  • Transform.(inverse)TransformVector The same as Matrix4x4. Rotation + scale
  • Transform.(inverse)TransformPoint. The same as Matrix4x4. Rotation + scale + translation.
  • Transform.(inverse)TransformDirection . This one is unique here, will apply only rotation.

You will understand why there is no Matrix4x4.MultiplyDirection . Doing so would be really really slow!

Matrix multiplication. Point, Direction and Vector differences.

Matrix4x4 and Transform classes, as we saw, always ask if you are applying that transformation to a Point, Direction or Vector. 

point represents a position relative to a given space, a direction just an orientation. A vector is basically the same as a point but the concept works differently when applying transformations!

When you use Matrix4x4.MultiplyPoint it will think your Vector3 passed is a point.

To perform a matrix multiplication of sizes:

aXb * cXd   (where aXb means a rows and b columns)

the rule says b and c must be the same. So it is  aXb by bXc   , where a and b are 4   4X4 * 4X1 . So we can't work with a Vector3 (3x1), it has to be a 4x1!. It will add another item to build a Vector4. Then it will return a Vector3 without you noticing about it.

Depending on how you want it to interpret it (point or vector) it will add a 0 or a 1 to that 4th item.

Why all this? Because the right side of the matrix, the red part, is the "translation" part. If you remember how multiplying two matrices was done (fun example), you multiply each row by each element of the Vector4, and sum all up. That gives the first item on the new Vector4. Then repeat for the second row... get the second item... etc.

The red part will be multiplied by the last item in the Vector4.  (x,y,z,1) will add m14 m24 m34 to the three x,y,z values because each of them will be multiplied by 1 and summed to the rest... thus applying the translation to the resulting Vector4.

Instead, if you say it is a direction, it will use (x,y,z,0), and the red part will never be applied. So it will not translate the direction, it will just apply the blue part, which is rotation and scale.

Summary. Which method to use?

Method A)

Transform.(inverse)transformXXX

This method is a one-liner, it is fast enough, but slower if you do it many many times. If not used many many times and you want to save lines of code, it's ok.

Method B)

Transform.worldToLocalMatrix.MultiplyXXX

If you use this directly and use it just once, it's ok. But I recommend using the method A before this one. If you end up using it many times, it's the slowest one. Careful with this! 

Method C)

Matrix4x4 savedMatrix = Transform.worldToLocalMatrix; 
savedMatrix.MultiplyPoint...

This is the fastest method of all if you need to perform a lot of transform operations. But not fast if you use it for very few operations. If you are working with positions, use MultiplyPoint3x4 instead of MultiplyPoint for an even faster operation if you are not doing anything weird with transformations.

Why?

The transformation of each gameObject is stored in the transform component. It is never stored as a matrix. It is instead stored as 3 items, T, R and S: A translation, a rotation and a scale, as you know. That means it stores a Vector3, a Quaternion, and  a Vector3.

Method A performs operations directly using the transform items, that means: Performing the scale by multiplying the scale vector, performing the rotation by applying the quaternion and then moving the element to the position by adding the translation. This means A LOT of operations.

Method B uses worldToLocalMatrix. This is a c# property that creates and returns a Matrix4x4 and this matrix is a struct with 16 values created combining the elements that are stored (S, R, T) in a certain way. It needs performing Sin, Cos and other operations. So as you probably guessed, it is a slow operation. Using this all the time will create a new Struct with all that data all the time. BAD!

Method C stores the worldToLocalMatrix into a variable of type Matrix4x4. This is great if you are going to use it many many times. It is only generated once, and then used. To transform a vector using a matrix is just multiplying both.

In my simulations a matrix like this is used with 500.000 items, so yes. It is faster. Why? Because multiplying a matrix by a vector is basically performing 28 operations: 16 multiplications (4 per row by vector) and 12 sums (sum 4 multiplication per row), which is way less than creating the struct, building the matrix and returning it. Learn more about matrix multiplication (4x4) by a vector, which is a matrix of (1x4) here.

Even better, with Matrix4x4 we have the oportunity to use this awesome method called MultiplyPoint3x4. which is faster because the last row of the matrix is completely ignored, removing 4 multiplications and 3 sums from the list or operations.

What is the fourth row by the way?

It contains shear values operations, which are normally seen when you are trying to perform a transformation that changes the perspective, in projections of points, for example, when you want to put a 3D object on top of a character, and you need to project that 3D point into the 2D flat plane of the camera.

Those operations are not normally used unless you are doing something fancy like this. In case you are doing regular operations with positions of objects, moving objects, rotating, and not doing any projection or shear operation, feel free to use Multiply3x4 and save even more time!

Hope you learned something today with this efficiency tip. Let me know if you need more of these and please suggest any topic you need to know about, specially related to efficiency and linear algebra.