Импортер FBX SDK и система координат LH в OpenGL
Мы разрабатываем средство визуализации с использованием OpenGL. Одной из функций является возможность импорта моделей FBX. Модуль импорта использует FBX SDK (2017). Теперь, как мы все знаем,OpenGL является правосторонней системой координат. Это прямое направление от положительного к отрицательному, правое - правое, а вектор вверх - вверх. В нашем приложении требование иметь положительный вектор вперед, аналогично DirectX. На уровне приложения мы устанавливаем это, масштабируя Z матрицы проекции с -1. используя glm math:
glm::mat4 persp = glm::perspectiveLH (glm::radians (fov), width / height, mNearPlane, mFarPlane);
Что так же, как делать это:
glm::mat4 persp = glm::perspective (glm::radians (fov), width / height, mNearPlane, mFarPlane);
persp = glm::scale (persp , glm::vec3 (1.0f, 1.0f, -1.0f));
Пока все хорошо. Самое смешное, когда мы импортируем модели FBX.
При использовании
FbxAxisSystem::OpenGL.ConvertScene(pSceneFbx);
Затем преобразуем вершины с помощью глобального преобразования узла, которое вычисляется так:
FbxSystemUnit fbxUnit = node->GetScene()->GetGlobalSettings().GetSystemUnit();
FbxMatrix globalTransform = node->EvaluateGlobalTransform();
glm::dvec4 c0 = glm::make_vec4((double*)globalTransform.GetColumn(0).Buffer());
glm::dvec4 c1 = glm::make_vec4((double*)globalTransform.GetColumn(1).Buffer());
glm::dvec4 c2 = glm::make_vec4((double*)globalTransform.GetColumn(2).Buffer());
glm::dvec4 c3 = glm::make_vec4((double*)globalTransform.GetColumn(3).Buffer());
Геометрические грани инвертированы. (Порядок намотки по умолчанию против часовой стрелки в OpenGL)
При использовании конвертера DirectX:
FbxAxisSystem::DirectX.ConvertScene(pSceneFbx);
Модель как перевернутая, так и перевернутая вверх дном.
OpenGL преобразован:
DirectX преобразован:
То, что мы нашли, что решает эту проблему, это отрицание Z столбца 3 в этой матрице. И также вращение его на 180 градусов вокруг оси Z. В противном случае передняя часть модели будет расположена сзади (да, звучит сложно, но это имеет смысл, когда сравнение различий между системами координат OpenGL и DirectX.
Итак, вся матрица "преобразования" теперь выглядит так:
FbxSystemUnit fbxUnit = node->GetScene()->GetGlobalSettings().GetSystemUnit();
FbxMatrix globalTransform = node->EvaluateGlobalTransform();
glm::dvec4 c0 = glm::make_vec4((double*)globalTransform.GetColumn(0).Buffer());
glm::dvec4 c1 = glm::make_vec4((double*)globalTransform.GetColumn(1).Buffer());
glm::dvec4 c2 = glm::make_vec4((double*)globalTransform.GetColumn(2).Buffer());
glm::dvec4 c3 = glm::make_vec4((double*)globalTransform.GetColumn(3).Buffer());
glm::mat4 mat =
glm::mat4(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, -1, 0,//flip z to get correct mesh direction (CCW)
0, 0, 0, 1);
//in this case the model faces look right dir ,but the model
//itself needs to be rotated because the camera looks at it from the
//wrong direction.
mat = glm::rotate(mat, glm::radians(180.0f), glm::vec3(0.0f, 0.0f, 1.0f));
glm::mat4 convertMatr = glm::mat4(c0, c1, c2, c3) *mat;
Затем преобразование вершин модели FBX с помощью этой матрицы дает желаемый результат:
Кстати, откуда мы знаем, что результат желателен? Мы сравниваем его с игровым движком Unity3D.
Теперь, это первый раз, когда я должен выполнить такой хак. Это похоже на очень неприятный хак. Особенно, когда речь идет о сетчатых сетках, нам нужно преобразовать с помощью матрицы преобразования также матрицы костей, матрицы поз, а что нет...
В этом случае возникает вопрос, когда нам нужно сохранить порядок намотки CCW и иметь позитивный форвард в OpenGL. Это единственный способ получить правильные геометрические преобразования?
(Источник 3d-модели - 3DsMax, экспортируется с Y-up.)
1 ответ
В большинстве программ, которые я делал в OpenGL, использовалась левосторонняя система, а не правосторонняя система DirectX. Что нужно понимать в OpenGL по сравнению с DirectX, так это в том, что в OpenGL нет встроенного объекта камеры. Вы должны создать / поставить свою собственную камеру, и, таким образом, вы можете буквально установить свою систему координат как RHC или LHC, в зависимости от того, как вы настроили свои сохраненные матрицы, в большинстве случаев настройками по умолчанию являются LHC. Другое важное различие между DirectX и OpenGL заключается в том, что, когда у вас есть сцена и камера в 3D-пространстве с DirectX, когда вы перемещаете камеру или поворачиваете ее; это именно камера, которая на самом деле движется, где, как в OpenGL, это не камера, которая движется, потому что она зафиксирована, и это вся сцена, которая движется относительно камеры. Таким образом, самый простой способ проиллюстрировать преобразование - это понять или узнать операции множественных матричных вычислений и узнать порядок ваших матриц MVP (модель - вид - проекция). Преобразование из RHC в LHC должно быть выполнено и предварительно рассчитано после открытия и загрузки в файл модели, а затем сохранено в матрицу модели. Затем, когда у вас есть подходящая матрица модели, остальные расчеты MVP должны быть точными.
Это были бы основные шаги
- Откройте файл модели, извлеките необходимые или требуемые данные и загрузите содержимое в специальные пользовательские структуры данных приложения
- При необходимости конвертируйте из одной системы координат в другую и сохраняйте обратно в пользовательские структуры.
- Используйте пользовательские структуры для создания ваших MVP и отправки их на графическую карту с помощью пакетного процесса, используя нужные шейдеры
- Рендеринг объектов на экран для каждого кадра буфера.
Вот несколько очень хороших прочтений при преобразовании между одной системой координат:
Самое простое, что можно сделать при преобразовании из RHC в LHC и наоборот, если X направлен влево и вправо, а Y - вверх и вниз, и все, что вам нужно сделать, это отменить каждую координату z, но это хорошо только для перевода и не вращения. Теперь, если в каком-либо приложении есть система RHC, и вы используете систему LHC, но в этом приложении сохранены данные модели, где, скажем, их вектор Up равен Z, а вектор входа и выхода - Y, тогда вам сначала нужно поменять местами каждый Y с Z, затем вы нужно отменить новый Z после перестановки, и снова это хорошо только с переводами, а не с поворотами.
Вот основная формула для конверсий:
Пусть M0 - заданная матрица преобразования 4x4.
Пусть M1 - дополнительная матрица аффинного преобразования 4x4, которая отображается из левой системы координат, которую вы хотите, в правую систему координат, которая у вас есть в настоящее время.
Тогда полное преобразование = inv(M1) * M0*M1
который может быть найден здесь: mathworks: mathlab * Математика или формула - это то же самое преобразование в любом направлении.