Матричные преобразования
Многие вычисления в UNIGINE выполняются с использованием матриц. Собственно, матричные преобразования - одна из основных концепций 3D-движков. Эта статья содержит объяснение матричных преобразований с примерами использования.
Смотрите также#
- Статья о типах данных UNIGINE mat4 и dmat4 .
- Описание матричных математических функций .
Трансформации#
Проще говоря, матрица в трехмерной графике - это массив чисел, упорядоченных по строкам и столбцам:
Обычно используются матрицы 4x4 . Такой размер матриц (4х4) обусловлен состоянием трансляции в трехмерном пространстве. Когда вы помещаете новый узел в мир, он имеет мировую матрицу трансформации мира 4x4, которая определяет его положение в мире.
В UNIGINE матрицы являются основными по столбцам (ориентированными по столбцам). Следовательно, первый столбец матрицы преобразования представляет вектор X локальной системы координат ( v1 ), второй представляет вектор Y ( v2 ), третий представляет вектор Z ( v3 ), а четвертый представляет вектор перемещения t . Первые три столбца показывают направления локальных осей координат ( поворот ) и масштаб начала координат. Последний столбец содержит перемещение локального начала координат относительно мирового.
Единичная матрица#
Начало координат имеет следующую матрицу:
Эта матрица называется единичной матрицей , матрицей с единицами на главной диагонали и нулями в другом месте. Если матрица умножается на единичную матрицу, это ничего не изменит: результирующая матрица будет такой же, как и до умножения.
Если локальное начало координат имеет единичную матрицу, это означает, что местное начало координат и мировое начало координат совпадают .
Вращение#
Чтобы изменить ориентацию локального начала координат, следует изменить первые три столбца матрицы.
Чтобы повернуть начало координат по разным осям, вы должны использовать соответствующие матрицы:
В приведенных выше матрицах α - это угол поворота вдоль оси.
Следующая матрица показывает поворот локального начала координат по оси Y на 45 градусов:
![]() |
![]() |
Перемещение#
Последний столбец матрицы преобразования показывает положение локального начала координат в мире относительно начала координат. Следующая матрица показывает перемещение начала координат. Вектор перемещения t равен (3, 0, 2) .
![]() |
![]() |
Масштабирование#
Длина вектора показывает масштабный коэффициент по оси.
Чтобы вычислить длину вектора (также известную как величина ), вы должны найти квадратный корень из суммы квадратов компонентов вектора. Формула следующая:
|vector length| = √(x² + y² + z²)
Следующая матрица масштабирует местное начало координат до 2 единиц по всем осям.
![]() |
![]() |
Накопление преобразований#
Порядок преобразования матриц в коде очень важен.
Если вы хотите реализовать последовательность кумулирующих преобразований, порядок преобразований в коде должен быть следующим:
TransformedVector = TransformationMatrixN * ... * TransformationMatrix2 * TransformationMatrix1 * Vector
Преобразования применяются одно за другим, начиная с TransformationMatrix1 и заканчивая TransformationMatrixN.
пример#
Этот пример показывает разницу между двумя порядками преобразования матриц.
В приведенном ниже примере кода получается объект материального шара. В первом случае за вращением следует перемещение, а во втором случае за перемещением следует поворот.
В файле AppWorldLogic.h определите указатель для узла material_ball.
// AppWorldLogic.h
/* ... */
class AppWorldLogic : public Unigine::WorldLogic {
public:
/* .. */
private:
Unigine::NodePtr material_ball;
};
В файле AppWorldLogic.cpp выполните следующие действия:
- Включите заголовки UnigineEditor.h, UnigineVisualizer.h, UnigineConsole.h.
- Используйте директивы using namespace Unigine и using namespace Unigine::Math: имена пространств имен Unigine и Unigine::Math будут вставлены в глобальное пространство имен.
- Включите визуализатор, передав команду show_visualizer 1 функции run() класса Console.
- Получите material ball из редактора.
- Создайте новые матрицы поворота и перемещения.
- Рассчитайте новую матрицу преобразования и примените ее к material ball.
- Визуализируйте происхождение мира с помощью метода renderVector() класса Visualizer.
// 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_red);
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,1.0f,0.1f), vec4_green);
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,0.0f,1.1f), vec4_blue);
return 1;
}
Чтобы изменить порядок, достаточно изменить строку накопления преобразований:
Mat4 transform = rotation_matrix * translation_matrix * material_ball->getTransform();
Результат будет другим. На фотографиях ниже показана разница (камера расположена в том же месте).
![]() |
![]() |
Порядок: вращение и перемещение
|
Порядок: перемещение и вращение
|
На изображениях выше показано положение сеток относительно начала мира.
Матричная иерархия#
Еще одно важное понятие - матричная иерархия. Когда узел добавляется в мир как дочерний по отношению к другому узлу, он имеет матрицу преобразования, связанную с родительским узлом. Вот почему класс Node различает функции getTransform(), setTransform() и getWorldTransform(), setWorldTransform(), которые возвращают локальную и мировую матрицы преобразования соответственно.
В чем причина использования матричной иерархии? Чтобы переместить узел относительно другого узла. И когда вы перемещаете родительский узел, дочерние узлы также будут перемещены.
![]() |
![]() |
Родительское происхождение совпадает с мировым происхождением
|
Родительский источник был перемещен, и дочерний источник также был перемещен
|
Рисунки выше показывают суть матричной иерархии. Когда начало координат родителя (ноды) перемещается, начало координат дочерней ноды также будет перемещено, и локальная матрица преобразования дочернего элемента не будет изменена. Но матрица трансформации мира дочерней ноды будет изменена. Если вам нужна матрица преобразования мира дочернего элемента, связанная с началом координат мира, вы должны использовать функции getWorldTransform(), setWorldTransform(); в случае, когда вам нужна локальная матрица преобразования дочернего элемента, связанного с родительским, вы должны использовать функции getTransform(), setTransform().
пример#
Следующий пример показывает, насколько важна иерархия матриц.
В этом примере мы получаем узел и клонируем его. Затем мы меняем матрицы преобразования этих узлов. Рассмотрим два случая:
- Два узла независимы.
- Один узел является дочерним для другого.
В файле AppWorldLogic.h определите интеллектуальные указатели дочерних и родительских узлов material_ball.
// AppWorldLogic.h
/* ... */
class AppWorldLogic : public Unigine::WorldLogic {
public:
/* .. */
private:
Unigine::NodePtr material_ball_child;
Unigine::NodePtr material_ball_parent;
};
В 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
material_ball_parent->addChild(material_ball_child);
// 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_red);
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,1.0f,0.1f), vec4_green);
Visualizer::renderVector(Vec3(0.0f,0.0f,0.1f), Vec3(0.0f,0.0f,1.1f), vec4_blue);
return 1;
}
int AppWorldLogic::shutdown() {
// clear smart pointers
material_ball_child.clear();
material_ball_parent.clear();
return 1;
}
Если закомментировать следующую строку:
// make the one node the child of another
material_ball_parent->addChild(material_ball_child);
вы получите другой результат:
![]() |
![]() |
Родительско-дочерние узлы
|
Узлы независимы
|
Когда узлы независимы, они имеют разные локальные и мировые матрицы преобразования. В случае узлов родитель-потомок локальная матрица преобразования дочернего элемента остается прежней после перемещения, но матрица преобразования мира изменяется (вы можете проверить это с помощью профилировщика отладки).
Информация, представленная на данной странице, актуальна для версии UNIGINE 2.20 SDK.