# 矩阵变换 A lot of calculations in UNIGINE are performed by using matrices. Actually, matrix transformations are one of the main concepts of 3D engines. This article contains an explanation of matrix transformations with usage examples. UNIGINE中的许多计算都是通过使用矩阵进行的。实际上，矩阵变换是3D引擎的主要概念之一。本文包含使用示例的矩阵转换说明。

### See Also也可以看看#

• An article about mat4 and dmat4 UNIGINE data types.关于 mat4 dmat4 UNIGINE数据类型的文章。
• The description of Math Matrix Functions. 数学矩阵函数的描述。

## Transformations转变# In simple terms, a matrix in 3D graphics is an array of numbers arranged in rows and columns:简单来说，3D图形中的矩阵是按行和列排列的数字数组：  Usually, 4x4 matrices are used. Such size (4x4) of matrices is caused by the translation state in 3D space. When you put a new node into the world, it has a 4x4 world transform matrix that defines the position of the node in the world.通常使用 4x4 矩阵。矩阵的这种大小（4x4）是由3D空间中的平移状态引起的。当您将新节点放到世界中时，它具有一个4x4的世界变换矩阵，该矩阵定义了该节点在世界中的位置。 In UNIGINE, matrices are column major (column-oriented). Hence, the first column of the transform matrix represents the X vector of the local coordinate system (v1), the second represents the Y vector (v2), the third represents the Z vector (v3), and the fourth represent the translation vector t. First three columns show directions of local coordinate axes (rotation) and the scale of the origin. The last column contains the translation of the local origin relatively to the world origin.在UNIGINE中，矩阵是主列（面向列）。因此，变换矩阵的第一列代表局部坐标系的 X 向量（ v1 ），第二列代表 Y 向量（ v2 ），第三个代表 Z 向量（ v3 ），第四个代表转换向量 t 。前三列显示局部坐标轴的方向（旋转）和原点的比例。最后一列包含本地起源相对于世界起源的翻译。 ### Identity Matrix身份矩阵# The world origin has the following matrix:世界起源具有以下矩阵：  This matrix is called identity matrix, a matrix with ones on the main diagonal, and zeros elsewhere. If a matrix is multiplied by the identity matrix, that won't change anything: the resulting matrix will be the same as it was before multiplying.此矩阵称为恒等矩阵，该矩阵的主对角线为 ones ，其他地方为 zeros 。如果将矩阵与单位矩阵相乘，则不会发生任何变化：所得矩阵将与乘法前相同。 If the local origin has the identity matrix, it means the local origin and the world origin are coincident.如果本地原点具有单位矩阵，则表示本地原点与世界原点是巧合 ### Rotation回转# To change the orientation of the local origin, the first three columns of the matrix should be changed.要更改本地原点的方向，应更改矩阵的前三列。 To rotate the origin along different axes, you should use proper matrices:要沿不同的轴旋转原点，应使用适当的矩阵：  In the matrices given above, α is a rotation angle along the axis.在上面给出的矩阵中，α是沿轴的旋转角度。 The next matrix shows the rotation of the local origin along the Y axis at 45 degrees:下一个矩阵显示本地原点沿 Y 轴以45度旋转：  ### Translation翻译# The last column of the transform matrix shows the position of the local origin in the world relatively to the world origin. The next matrix shows the translation of the origin. The translation vector t is (3, 0, 2).变换矩阵的最后一列显示相对于世界原点的世界上本地原点的位置。下一个矩阵显示了原点的翻译。转换向量 t （3，0，2）  ### Scaling缩放比例# The length of the vector shows the scale coefficient along the axis.向量的长度显示沿轴的比例系数。 To calculate the vector length (also known as magnitude), you should find a square root of the sum of the squares of vector components. The formula is the following:要计算矢量长度（也称为幅度），您应该找到矢量分量平方和的平方根。公式如下：

Vector Length
``|vector length| = √(x² + y² + z²)`` The following matrix scales the local origin up to 2 units along all axes.下面的矩阵沿所有轴将本地原点最多缩放2个单位。  ## Cumulating Transformations累计转换# The order of matrix transformations in code is very important.代码中矩阵转换的顺序非常重要。 If you want to implement a sequence of cumulating transformations, the transformations order in code should be as follows:如果要实现一系列累积转换，则代码中的转换顺序应如下：

Transformation order
``TransformedVector = TransformationMatrixN * ... * TransformationMatrix2 * TransformationMatrix1 * Vector`` Transformations are applied one by one starting from TransformationMatrix1 and ending with TransformationMatrixN. 从TransformationMatrix1开始到TransformationMatrixN结束，一个接一个地应用转换。

### Example例# This example shows the difference between two orders of matrix transformations.此示例显示了矩阵转换的两个顺序之间的差异。 The code example below gets the material ball object. In the first case, rotation is followed by translation and in the second case, translation is followed by rotation.下面的代码示例获取物料球对象。在第一种情况下，旋转后进行翻译，在第二种情况下，平移后进行旋转。 In the AppWorldLogic.h file, define the material_ball node smart pointer.AppWorldLogic.h文件中，定义material_ball节点智能指针。

``````// AppWorldLogic.h

/* ... */

class AppWorldLogic : public Unigine::WorldLogic {

public:
/* .. */
private:
Unigine::NodePtr material_ball;
};`````` In the AppWorldLogic.cpp file, perform the following:AppWorldLogic.cpp文件中，执行以下操作：

• Include the UnigineEditor.h, UnigineVisualizer.h, UnigineConsole.h headers. Include the UnigineEditor.h, UnigineVisualizer.h, UnigineConsole.h headers.
• Use using namespace Unigine and using namespace Unigine::Math directives: names of the Unigine and Unigine::Math namespaces will be injected into global namespace.使用using namespace Unigineusing namespace Unigine::Math指令：Unigine和Unigine::Math命名空间的名称将注入到全局命名空间中。
• Enable the visualizer by passing show_visualizer 1 command to the run() function of the Console class.通过将show_visualizer 1 命令传递给run() function of the Console class.
• Get the material ball from the Editor.从编辑器中获取material ball
• Create new rotation and translation matrices.创建新的旋转和平移矩阵。
• Calculate new transformation matrix and apply it to the material ball.计算新的变换矩阵并将其应用于material ball
• Render the world origin by using renderVector() method of the Visualizer class.使用VisualizerrenderVector()类的方法渲染世界原点。

``````// AppWorldLogic.cpp file
#include "AppWorldLogic.h"
#include "UnigineWorld.h"
#include "UnigineVisualizer.h"
#include "UnigineConsole.h"

// inject Unigine and Unigine::Math namespaces names to global namespace
using namespace Unigine;
using namespace Unigine::Math;

/* ... */

int AppWorldLogic::init() {

// enable the visualizer for world origin rendering
Console::run("show_visualizer 1");

// get the material ball
material_ball = World::getNodeByName("material_ball");

// create rotation and translation matrices
Mat4 rotation_matrix = (Mat4)rotateZ(-90.0f);
Mat4 translation_matrix = (Mat4)translate(vec3(0.0f, 3.0f, 0.0f));

// create a new transformation matrix for the material ball
// by multiplying the current matrix by rotation and translation matrices
Mat4 transform = translation_matrix * rotation_matrix * material_ball->getTransform();

// set the transformation matrix to the material ball
material_ball->setTransform(transform);

return 1;
}

int AppWorldLogic::update() {
// render world origin
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(1.0f,0.0f,0.1f), vec4(1.0f,0.0f,0.0f,1.0f));
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,1.0f,0.1f), vec4(0.0f,1.0f,0.0f,1.0f));
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,0.0f,1.1f), vec4(0.0f,0.0f,1.0f,1.0f));

return 1;
}`````` To change the order, just change the line of cumulating transformations:要更改顺序，只需更改累积转换的行即可：

``Mat4 transform = rotation_matrix * translation_matrix * material_ball->getTransform();`` The result will be different. The pictures below show the difference (camera is located at the same place).结果将有所不同。下图显示了差异（相机位于同一位置）。   Order: rotation and translation顺序：旋转和平移 Order: translation and rotation顺序：平移和旋转 The pictures above show the position of the meshes related to the world origin.上面的图片显示了与世界原点相关的网格的位置。

## Matrix Hierarchy矩阵层次结构# One more important concept is the matrix hierarchy. When a node is added into the world as a child of another node, it has a transformation matrix that is related to the parent node. That is why the Node class distinguishes between the functions getTransform(), setTransform() and getWorldTransform(), setWorldTransform(), which return the local and the world transformation matrices respectively.一个更重要的概念是矩阵层次结构。当一个节点作为另一个节点的子节点添加到世界中时，它具有与父节点相关的转换矩阵。这就是为什么Node类区分函数getTransform(), setTransform()getWorldTransform(), setWorldTransform()的原因，它们分别返回局部和世界变换矩阵。 If the added node has no parent, this node uses the World transformation matrix.如果添加的节点没有父节点，则此节点使用 World转换矩阵 What is the reason of using a matrix hierarchy? To move a node relative to another node. And when you move a parent node, child nodes will also be moved.使用矩阵层次结构的原因是什么？相对于另一个节点移动一个节点。而且，当您移动父节点时，子节点也将被移动。   Parent origin is the same with the world origin父原点与世界原点相同 Parent origin has been moved and the child origin has also been moved父原点已移动，子原点也已移动 Pictures above show the main point of the matrix hierarchy. When the parent origin (node) is moved, the chlld origin will also be moved and the local transformation matrix of the child would not be changed. But the world transformation matrix of the child will be changed. If you need the world transformation matrix of the child related to the world origin, you should use the getWorldTransform(), setWorldTransform() functions; in case, when you need the local transformation matrix of the child related to the parent, you should use the getTransform(), setTransform() functions.上面的图片显示了矩阵层次结构的要点。当父原点（节点）移动时，chlld原点也将移动，并且子节点的局部转换矩阵也不会更改。但是孩子的世界转换矩阵将会改变。如果需要与世界原点相关的子项的世界变换矩阵，则应使用getWorldTransform(), setWorldTransform()函数；如果需要与父级相关的子级的局部转换矩阵，则应使用getTransform(), setTransform()函数。

### Example例# The following example shows how important the matrix hierarchy is.下面的示例显示矩阵层次结构的重要性。 In this example, we get the node and clone it. Then we change transformation matrices of these nodes. We review two cases:在此示例中，我们获取节点并将其克隆。然后，我们更改这些节点的变换矩阵。我们回顾两种情况：

1. The two nodes are independent.两个节点是独立的。
2. One node is the child of the other.一个节点是另一个节点的子节点。 In the AppWorldLogic.h file, define smart pointers of the material_ball child and parent nodes.AppWorldLogic.h文件中，定义material_ball子节点和父节点的智能指针。

``````// AppWorldLogic.h

/* ... */

class AppWorldLogic : public Unigine::WorldLogic {

public:
/* .. */
private:
Unigine::NodePtr material_ball_child;
Unigine::NodePtr material_ball_parent;
};`````` In the AppWorldLogic.cpp, implement the following code:AppWorldLogic.cpp中，实现以下代码：

``````// AppWorldLogic.cpp
#include "AppWorldLogic.h"
#include "UnigineEditor.h"
#include "UnigineVisualizer.h"
#include "UnigineConsole.h"
#include "UnigineLog.h"

using namespace Unigine;
using namespace Unigine::Math;

int AppWorldLogic::init() {

// enable the visualizer for world origin rendering
Console::run("show_visualizer 1");

// get the material ball and clone it
material_ball_child = World::getNodeByName("material_ball");
material_ball_parent = material_ball_child->clone();

// make the one node the child of another

// create rotation and translation matrices for the first material_ball
Mat4 rotation_matrix = (Mat4)rotateZ(-90.0f);
Mat4 translation_matrix = (Mat4)translate(vec3(3.0f, 0.0f, 0.0f));

// create translation matrix for the second (parent) material ball
Mat4 translation_matrix_clone = (Mat4)translate(vec3(0.5f, 0.0f, 1.0f));

// create new transformation matrices for the material balls
Mat4 transform = translation_matrix * rotation_matrix * material_ball_child->getTransform();
Mat4 transform_clone = translation_matrix_clone * material_ball_parent->getTransform();

// set the transformation matrices to the material balls
material_ball_child->setTransform(transform);
material_ball_parent->setTransform(transform_clone);

return 1;
}

int AppWorldLogic::update() {
// render world origin
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(1.0f,0.0f,0.1f), vec4(1.0f,0.0f,0.0f,1.0f));
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,1.0f,0.1f), vec4(0.0f,1.0f,0.0f,1.0f));
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,0.0f,1.1f), vec4(0.0f,0.0f,1.0f,1.0f));

return 1;
}

int AppWorldLogic::shutdown() {
// clear smart pointers
material_ball_child.clear();
material_ball_parent.clear();

return 1;
}`````` If you comment the following line:如果您注释以下行：

``````// make the one node the child of another you get a different result:您会得到不同的结果：   Parent-child nodes父子节点 Nodes are independent节点是独立的 When nodes are independent, they have different local and world transformation matrices. In case of parent-child nodes, the child's local transformation matrix remains the same after moving, but the world transformation matrix is changed (you can check that by using the debug profiler).当节点独立时，它们具有不同的本地和世界转换矩阵。对于父子节点，子对象的局部变换矩阵在移动后保持不变，但是世界变换矩阵已更改（可以使用调试概要分析器进行检查）。