This page has been translated automatically.
Видеоуроки
Интерфейс
Основы
Продвинутый уровень
Подсказки и советы
Основы
Программирование на C#
Рендеринг
Профессиональный уровень (SIM)
Принципы работы
Свойства (properties)
Компонентная Система
Рендер
Физика
Браузер SDK 2
Лицензирование и типы лицензий
Дополнения (Add-Ons)
Демонстрационные проекты
API Samples
Редактор UnigineEditor
Обзор интерфейса
Работа с ассетами
Контроль версий
Настройки и предпочтения
Работа с проектами
Настройка параметров ноды
Setting Up Materials
Настройка свойств
Освещение
Sandworm
Использование инструментов редактора для конкретных задач
Расширение функционала редактора
Встроенные объекты
Ноды (Nodes)
Объекты (Objects)
Эффекты
Декали
Источники света
Geodetics
World-ноды
Звуковые объекты
Объекты поиска пути
Player-ноды
Программирование
Основы
Настройка среды разработки
Примеры использования
C++
C#
UnigineScript
Унифицированный язык шейдеров UUSL (Unified UNIGINE Shader Language)
Плагины
Форматы файлов
Материалы и шейдеры
Rebuilding the Engine Tools
Интерфейс пользователя (GUI)
Двойная точность координат
Справочник API
Animations-Related Classes
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
VR-Related Classes
Работа с контентом
Оптимизация контента
Материалы
Визуальный редактор материалов
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Учебные материалы

Матричные преобразования

Многие вычисления в UNIGINE выполняются с использованием матриц. Собственно, матричные преобразования - одна из основных концепций 3D-движков. Эта статья содержит объяснение матричных преобразований с примерами использования.

Смотрите также#

Трансформации#

Проще говоря, матрица в трехмерной графике - это массив чисел, упорядоченных по строкам и столбцам:

Обычно используются матрицы 4x4 . Такой размер матриц (4х4) обусловлен состоянием трансляции в трехмерном пространстве. Когда вы помещаете новый узел в мир, он имеет мировую матрицу трансформации мира 4x4, которая определяет его положение в мире.

В UNIGINE матрицы являются основными по столбцам (ориентированными по столбцам). Следовательно, первый столбец матрицы преобразования представляет вектор X локальной системы координат ( v1 ), второй представляет вектор Y ( v2 ), третий представляет вектор Z ( v3 ), а четвертый представляет вектор перемещения t . Первые три столбца показывают направления локальных осей координат ( поворот ) и масштаб начала координат. Последний столбец содержит перемещение локального начала координат относительно мирового.

Единичная матрица#

Начало координат имеет следующую матрицу:

Эта матрица называется единичной матрицей , матрицей с единицами на главной диагонали и нулями в другом месте. Если матрица умножается на единичную матрицу, это ничего не изменит: результирующая матрица будет такой же, как и до умножения.

Если локальное начало координат имеет единичную матрицу, это означает, что местное начало координат и мировое начало координат совпадают .

Вращение#

Чтобы изменить ориентацию локального начала координат, следует изменить первые три столбца матрицы.

Чтобы повернуть начало координат по разным осям, вы должны использовать соответствующие матрицы:

В приведенных выше матрицах α - это угол поворота вдоль оси.

Следующая матрица показывает поворот локального начала координат по оси Y на 45 градусов:

Перемещение#

Последний столбец матрицы преобразования показывает положение локального начала координат в мире относительно начала координат. Следующая матрица показывает перемещение начала координат. Вектор перемещения t равен (3, 0, 2) .

Масштабирование#

Длина вектора показывает масштабный коэффициент по оси.

Чтобы вычислить длину вектора (также известную как величина ), вы должны найти квадратный корень из суммы квадратов компонентов вектора. Формула следующая:

Vector Length
|vector length| = √(x² + y² + z²)

Следующая матрица масштабирует местное начало координат до 2 единиц по всем осям.

Накопление преобразований#

Порядок преобразования матриц в коде очень важен.

Если вы хотите реализовать последовательность кумулирующих преобразований, порядок преобразований в коде должен быть следующим:

Transformation order
TransformedVector = TransformationMatrixN * ... * TransformationMatrix2 * TransformationMatrix1 * Vector

Преобразования применяются одно за другим, начиная с TransformationMatrix1 и заканчивая TransformationMatrixN.

пример#

Этот пример показывает разницу между двумя порядками преобразования матриц.

В приведенном ниже примере кода получается объект материального шара. В первом случае за вращением следует перемещение, а во втором случае за перемещением следует поворот.

В файле AppWorldLogic.h определите указатель для узла material_ball.

Исходный код (C++)
// 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.
Исходный код (C++)
// 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;
}

Чтобы изменить порядок, достаточно изменить строку накопления преобразований:

Исходный код (C++)
Mat4 transform = rotation_matrix * translation_matrix * material_ball->getTransform();

Результат будет другим. На фотографиях ниже показана разница (камера расположена в том же месте).

Порядок: вращение и перемещение
Порядок: перемещение и вращение

На изображениях выше показано положение сеток относительно начала мира.

Матричная иерархия#

Еще одно важное понятие - матричная иерархия. Когда узел добавляется в мир как дочерний по отношению к другому узлу, он имеет матрицу преобразования, связанную с родительским узлом. Вот почему класс Node различает функции getTransform(), setTransform() и getWorldTransform(), setWorldTransform(), которые возвращают локальную и мировую матрицы преобразования соответственно.

Примечание
Если у добавленного узла нет родителя, этот узел использует мировую матрицу трансформации .

В чем причина использования матричной иерархии? Чтобы переместить узел относительно другого узла. И когда вы перемещаете родительский узел, дочерние узлы также будут перемещены.

Родительское происхождение совпадает с мировым происхождением
Родительский источник был перемещен, и дочерний источник также был перемещен

Рисунки выше показывают суть матричной иерархии. Когда начало координат родителя (ноды) перемещается, начало координат дочерней ноды также будет перемещено, и локальная матрица преобразования дочернего элемента не будет изменена. Но матрица трансформации мира дочерней ноды будет изменена. Если вам нужна матрица преобразования мира дочернего элемента, связанная с началом координат мира, вы должны использовать функции getWorldTransform(), setWorldTransform(); в случае, когда вам нужна локальная матрица преобразования дочернего элемента, связанного с родительским, вы должны использовать функции getTransform(), setTransform().

пример#

Следующий пример показывает, насколько важна иерархия матриц.

В этом примере мы получаем узел и клонируем его. Затем мы меняем матрицы преобразования этих узлов. Рассмотрим два случая:

  1. Два узла независимы.
  2. Один узел является дочерним для другого.

В файле AppWorldLogic.h определите интеллектуальные указатели дочерних и родительских узлов material_ball.

Исходный код (C++)
// AppWorldLogic.h

/* ... */

class AppWorldLogic : public Unigine::WorldLogic {
	
public:
	/* .. */
private:
	Unigine::NodePtr material_ball_child;
	Unigine::NodePtr material_ball_parent;
};

В AppWorldLogic.cpp реализуйте следующий код:

Исходный код (C++)
// 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;
}

Если закомментировать следующую строку:

Исходный код (C++)
// make the one node the child of another
material_ball_parent->addChild(material_ball_child);

вы получите другой результат:

Родительско-дочерние узлы
Узлы независимы

Когда узлы независимы, они имеют разные локальные и мировые матрицы преобразования. В случае узлов родитель-потомок локальная матрица преобразования дочернего элемента остается прежней после перемещения, но матрица преобразования мира изменяется (вы можете проверить это с помощью профилировщика отладки).

Информация, представленная на данной странице, актуальна для версии UNIGINE 2.20 SDK.

Последнее обновление: 26.07.2024
Build: ()