(翻译) Efficiency Tips on Switching Spaces and Transformation Matrices in Unity
原文
https://blog.lidia-martinez.com/efficiency-tips-spaces-and-transformation-matrices-unity
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.
你是否需要在切换空间(spaces)的方法上下功夫?
只有当你关注效率和垃圾回收时,这才是真正关键的。这取决于你正在执行的操作类型以及操作的频率。否则,这并不重要。但最好一开始就选择合适的方式,以防后续需要扩展它。
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!
使用哪种空间变换操作
执行空间切换的原理,就等同于将一个 4x4 矩阵与一个 Vector3 相乘。基本上有两种方法可以对 Vector3 进行空间切换:
1)使用 Matrix4x4 应用变换
你可以使用 Transform 提供的 localToWorldMatrix 或 worldToLocalMatrix 变换矩阵,或者自己构造一个矩阵(一般不推荐后者)。
Matrix4x4 类提供了以下三种方法来应用变换:
-
Matrix4x4.MultiplyPoint:应用旋转、平移和缩放变换。 -
Matrix4x4.MultiplyVector:不应用平移,仅用于你只关心旋转和缩放的情况。 -
Matrix4x4.MultiplyPoint3x4:与第一个方法类似,但省略了一些操作。我们稍后会详细讨论这个方法。
2)使用 Transform 类应用变换
你可以直接获取代表要变换空间的对象的 Transform 组件,并使用它的 TransformXXX 方法。
-
使用
TransformXXX方法:将世界空间中的 Vector3 转换到该 Transform 的局部空间。 -
使用
InverseTransformXXX方法:将以该对象空间表示的 Vector3 转换为世界空间中的 Vector3。
三种可用的方法包括:
-
Transform.(Inverse)TransformVector:等同于Matrix4x4,应用旋转 + 缩放。 -
Transform.(Inverse)TransformPoint:等同于Matrix4x4,应用旋转 + 缩放 + 平移。 -
Transform.(Inverse)TransformDirection:仅应用旋转,是此处独有的操作。
你会明白为什么没有 Matrix4x4.MultiplyDirection,因为这样做会非常非常慢!
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.
A 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
矩阵乘法:Point、Direction 和 Vector 的区别
如前所述,Matrix4x4 和 Transform 类在应用变换时,总会问你到底是要对 Point(点)、Direction(方向) 还是 Vector(向量) 进行变换。
-
Point(点):表示相对于某个空间的一个位置。
-
Direction(方向):仅表示一个方向,不包含位置信息。
-
Vector(向量):本质上和点类似,但在应用变换时概念不同!
当你使用 Matrix4x4.MultiplyPoint 方法时,它会把你传入的 Vector3 当作一个 点 来处理。
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 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.
要执行如下尺寸的矩阵乘法:
a×b × c×d(其中 a×b 表示 a 行 b 列)
根据规则,b 和 c 必须相等。
所以是 a×b 乘以 b×d,当 a 和 b 都为 4 时,就是 4×4 × 4×1。
因此我们不能直接使用一个 Vector3(3×1),它必须是一个 4×1 的向量!
这意味着它会为 Vector3 添加一个额外的分量,从而构建成一个 Vector4。
然后变换完成后,会自动返回一个 Vector3,整个过程你可能不会察觉。
根据你希望它被解释为“点”还是“向量”,它会在第4个分量上添加 0 或 1:
-
如果是 点(Point),会添加 1,这样平移部分会生效。
-
如果是 向量(Vector)或方向(Direction),会添加 0,这样平移部分被忽略,只进行旋转和缩放。
为什么要这么做?
因为矩阵右侧的那一列(通常是红色部分)表示的是 “平移”部分。
如果你还记得矩阵乘法是如何进行的(一个有趣的例子):
你会将每一行与 Vector4 的每个元素分别相乘,然后相加,得到新的向量的每一个分量。
这个过程依次进行,最终构成一个新的 Vector4。
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.
The green part of the matrix is rarely used, except for shear operations and weird projections.

矩阵中红色部分会与 Vector4 的最后一个分量相乘。
当你使用 (x, y, z, 1) 时,矩阵中的 m14、m24 和 m34 会分别加到 x、y、z 上,因为它们每一个都乘以了 1,并加到其余部分上……
这样就把平移变换应用到了最终的 Vector4 上。
而如果你说它是一个方向(Direction),那就会使用 (x, y, z, 0),这样红色部分就不会被应用。
所以这个方向不会被平移,只会应用蓝色部分(旋转和缩放)。
矩阵中的绿色部分很少被使用,除非用于剪切(Shear)变换或一些特殊的投影操作。
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!
但总体上推荐优先使用方法 A。
如果你频繁使用这种方式,它是三种里最慢的,务必注意!
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.
如果你需要执行大量的变换操作,这是最快的方法。但如果只是少量使用,反而不划算。
如果你处理的是位置(Position),而且没有涉及特别复杂的变换,建议使用 MultiplyPoint3x4 替代 MultiplyPoint,会更快。
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.
每个 GameObject 的变换信息都存储在 transform 组件中。它从不会以矩阵形式存储,而是以三个元素的形式:T、R 和 S,也就是你所知道的平移、旋转和缩放。这意味着它存储的是一个 Vector3、一个 Quaternion 和一个 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.
方法 A 是直接使用 transform 中的这些数据来操作,这表示:通过乘缩放向量来执行缩放,通过应用四元数来执行旋转,然后通过加上平移向量来移动物体。这意味着会进行大量的操作。
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!
方法 B 使用 worldToLocalMatrix。它是一个 C# 属性,会创建并返回一个 Matrix4x4 矩阵,而这个矩阵是通过以特定方式组合存储的 S、R、T 元素而生成的结构体,它需要执行正弦、余弦等运算。所以你可能已经猜到,这个操作是比较慢的。如果你频繁使用它,每次都会创建一个包含所有数据的新结构体。不好!
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.
方法 C 将 worldToLocalMatrix 缓存到一个 Matrix4x4 类型的变量中。如果你需要进行大量变换操作,这是最好的方式。它只生成一次,然后重复使用。用矩阵变换向量只是做乘法而已。
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.
在我的模拟中,类似的矩阵对 500,000 个元素进行操作,确实更快。为什么?因为一个矩阵乘以一个向量基本上只执行 28 个操作:16 次乘法(每行 4 次乘以向量),再加上 12 次加法(每行 4 个乘积求和),远比创建结构体、构建矩阵并返回要少。你可以通过这里了解更多关于 4x4 矩阵与向量的乘法。
更进一步,使用 Matrix4x4 时我们可以用一个非常棒的方法叫做 MultiplyPoint3x4,它更快,因为它完全忽略了矩阵的最后一行,从操作中移除了 4 次乘法和 3 次加法。
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!
它包含的是剪切(shear)运算值,这些通常出现在你要进行视角变化的变换中,比如投影点时使用的操作。例如,当你想把一个 3D 物体投影到角色头顶上,在相机的二维平面中展示那个 3D 点时,就会用到这种操作。
这些操作在常规使用中是不会用到的,除非你在做一些特别的事情。
如果你只是进行常规的对象位置操作,比如移动、旋转、而不是做投影或剪切变换,尽管放心使用 MultiplyPoint3x4,它会更快!

浙公网安备 33010602011771号