【问题标题】:How to convert mesh defined in a right-handed XYZ coordinate system to a left-handed XZY one如何将右手 XYZ 坐标系中定义的网格转换为左手 XZY 坐标系
【发布时间】:2017-02-05 12:19:03
【问题描述】:

我需要一种方法来传递到这些坐标系,但我在数学上苦苦挣扎。

基本上我有一个“为 OpenGL”创建的网格,所以右手 XYZ 坐标系,我想将它加载到使用左手 XZY 坐标系的不同环境(虚幻引擎 4)中。

现在,以这个网格为例,在它的原始状态:

如果加载它使所有顶点保持原样,我会得到:

如果你仔细看,你会发现它是“镜像的”,蜘蛛的左腿与右腿互换了。此外,在图像中不是很清晰,但深度不够,我可以看到蜘蛛的“内部”(如果你注意到的话,眼睛会因此而丢失)。

现在,如果我用 -Y 交换每个顶点的 Y 坐标,我基本上会得到与原来相同的结果:

现在,我使用的网格格式具有对象层次结构,这意味着网格的一个子集可能有另一个子集作为父子集,如果是这样,它具有相对于与其关联的父级的位置和旋转矩阵。这意味着加载网格时会出现更多问题,因为旋转矩阵和位置也在错误的坐标系中定义。 为了应用这些更改,我创建了顶点,然后由这些顶点组成的对象,为其分配其父对象,然后应用旋转矩阵和位置。

以这个网格为例,用这种格式定义;组成它的所有各个部分都按照我描述的方式分层:

如果我按原样加载它,顶点、相对旋转矩阵和位置在错误的坐标系中,我会得到:

哪种看起来一样,只是它是沿轴“镜像”的。请注意图像最右侧的黄色物体(不知道如何称呼它):它位于原始图像中位置的相反侧。 此外,如果您查看文本“BROKK”(这不是纹理,它是由顶点组成的),它也会被反转。

现在,如果我应用与蜘蛛相同的技巧,将 Y 切换为 -Y,我会得到:

现在看起来没有镜像,但是机器的“手臂”全错了,可能是因为我将相对旋转矩阵和位置留在了它们的原始坐标系中?我试着惹他们,但我得到了各种奇怪的事情,没有任何解决办法。

我环顾四周,在坐标系之间切换我发现了不同的想法。我尝试在下面应用这个矩阵来切换轴并反转轴“向前”,但结果是疯狂的错误:

(1 0 0)
(0 0 1)
(0 -1 0)

我很无奈,你有什么想法吗?

编辑:感谢 Ripi2 的建议(仅将符号反转到 Z 坐标),我有点接近理想的解决方案。这是结果:

即使它是颠倒的(我稍后可以简单地旋转它),它也不再镜像了。尽管如此,在那张图片中,我只是反转了网格顶点和孩子位置的 Z 坐标。在图片中,除了手臂之外的所有部分都处于正确的方向,因为它们的旋转矩阵是单位矩阵。在手臂中,虽然旋转矩阵不是恒等式,但由于我将其保留在原始坐标系中,因此看起来完全错误。

我需要某种矩阵来乘以每个旋转矩阵,但我不知道是什么!

【问题讨论】:

  • 你试过只改变 Z 符号吗?
  • 感谢您的建议,我用结果编辑了原帖

标签: opengl matrix mesh coordinate-systems


【解决方案1】:

矩阵从X+ right, Y+ up, Z- deep 转换为Y+ right, Z+ up, X+ deep

 0  1  0  0
 0  0  1  0
-1  0  0  0
 0  0  0  1 

和它的逆

0  0 -1  0
1  0  0  0
0  1  0  0
0  0  0  1

没有矩阵码可能是这样的:

x2 = -z1
y2 = x1
z2 = y1

编辑

你得到错误的结果?也许您有不同的系统更改。例如,如果只改变 Y 符号就足够了,这就是矩阵:

1  0  0  0
0 -1  0  0
0  0  1  0
0  0  0  1

记住矩阵计算顺序Result = Transform * Origin

如果模型由层次结构中的多个对象组成,则过程如下:

  1. 每个模型都使用自己的矩阵进行平移和旋转,或者 与组成 ORDERED 两个转换的那个。它是 假设如果数据来自文件,则轴系统是相同的 为所有人。
  2. 构建完整的世界后,您可以使用 一个视图矩阵和一个带有另一个矩阵的投影。在这一点上,您需要应用轴系变换矩阵。

让我们在您的模型中使用手臂末端。它有自己的矩阵 T1(例如,由旋转和平移组成,T1 = T·R)。当它被链接到手臂的其余部分(有它的 T2 矩阵)时,它也必须得到主手臂变换。所以应用于 arm-end 的结果矩阵是 T2·T1(注意顺序,不是 T1·T2)。如果整个手臂链接到其他部分,则所需的手臂末端矩阵为 T3·T2·T1。

现在应用视图矩阵 (V)、轴变化 (A) 和投影 (P)。臂端的合成矩阵为 P·A·V·T3·T2·T1。

也许在新的轴系中定义投影变得很困难。如果只是改变一些符号和交换轴的问题,最好在投影后应用:A·P·V·T3·T2·T1

【讨论】:

  • 再次感谢 Ripi2!这种工作,现在网格围绕 Y 轴移动了 90°,并面向 Y+。但是碎片的旋转矩阵呢?我尝试将您给我的矩阵应用于相对旋转矩阵,但结果一团糟……我做错了
  • 在纸上画出两个轴系。请参阅我显示的代码,其第一行表示“当我发送 X 时,我得到 -Z” 与矩阵形式相比,手动进行一些矩阵计算。使用一个或相反的,一个有效的。如果您有不同的系统,请尝试自己推断。
  • 我一整天都在尝试将其形象化,但我无处可去,而且我越来越困惑。我尝试了一堆矩阵,但也许我在核心上做错了什么。我正在做的是:1)将每个顶点转换为另一个坐标系,就像你说的那样 2)使用这些顶点创建对象 3)获取该对象的旋转矩阵并应用你的旋转矩阵,如下所示: newRotationMatrix = oldRotationMatrix * matrixConverter,其中 matrixConverter 是您的矩阵。这种方法正确吗?因为我整天都在用不同的矩阵做这个,但什么都没有
  • 不,这不是正确的顺序。请参阅我的答案的编辑。
  • 我想我不明白。我无法更改视图或投影矩阵。我唯一能做的就是加载模型,对其应用转换,以使其在当前视图/投影设置下看起来“正常”。考虑到这一点,我应该什么时候应用轴系变换?
【解决方案2】:

这是我找到的解决方案。我需要将一个矩阵应用于每个顶点,然后将相同的矩阵应用于每个分层组件具有的旋转平移矩阵。矩阵就是这个:

1 0 0
0 0 1
0 1 0

在代码中(记住,我使用的是虚幻引擎),它是这样的:

// Iterating over all vertices of the mesh
for(int i = 0; i < verticesNumber; i++)
{
    FVector4 vertex;

    // Getting the coordinates in the original mesh coordinate system.
    // Here the method GetX(), GetY(), GetZ() retrieve the coordinates from the mesh file somehow
    vertex.X = GetX();
    vertex.Y = GetY();
    vertex.Z = GetZ();

    // Creating the matrix that will transform these coordinates in the new coordinate system
    FMatrix mat = FMatrix::Identity;

    mat.M[0][0] = 1, mat.M[0][1] = 0, mat.M[0][2] = 0;
    mat.M[1][0] = 0, mat.M[1][1] = 0, mat.M[1][2] = 1;
    mat.M[2][0] = 0, mat.M[2][1] = 1, mat.M[2][2] = 0;

    // Applying the matrix
    vertex = mat.TransformFVector4(vertex);

    vertices.Add(FVector(vertex.X, vertex.Y, vertex.Z));
}

/* Then I create the indices, normals, and so on. */

// Now I get from the file the roto-translation matrix for the current object and apply the same matrix like this (note that I multiply before and after; this was mainly what I was missing):
rotationTranslationMatrix = mat * rotationTranslationMatrix * mat;

// Then I set the new matrix, relative to the parent of the current object
FVector position;
position.X = rotationTranslationMatrix.M[0][3];
position.Y = rotationTranslationMatrix.M[1][3];
position.Z = rotationTranslationMatrix.M[2][3];
mesh->SetRelativeLocationAndRotation(position, rotationTranslationMatrix.Rotator());

此代码会针对构成网格的每个部分执行。对于我用作示例的模型,最终结果是这样的:

请注意,它沿 X 轴移动了 90 度。我不知道如何避免这种情况,但我需要的是在创建后旋转网格就足够了,像这样:

FRotator rotation;
rotation.Yaw = 0.0f;
rotation.Pitch = 0.0f;
rotation.Roll = -90.0f;

mMesh->SetWorldRotation(rotation);

最终结果:

再次感谢 Ripi2 的帮助!

【讨论】:

  • 使用第一个矩阵 (m) 在处理所有顶点之前交换 Y、Z。 m·T·m 进行两次交换,第一次由于交换已经交换的 Y、Z 而恢复顶点。你能避免吗?矩阵在 X 轴上旋转 -90 与 m 非常相似,只是“底部”1 变为 -1
  • “第一个顶点由于交换已经交换了 Y,Z”是什么意思?实际上,我尝试切换底部 1 的符号,但它弄乱了所有东西的旋转,不确定它是否正确
  • res = m * v= (0,1,1,0) * (x,y) 产生 (y,x)。您首先将“m”应用于每个向量,然后在 mtm(最右边的 m)中应用。我不知道 UE4 轴,所以我不知道在这里应用的正确矩阵。
【解决方案3】:

要制作从opengl到虚幻坐标系的矩阵,您需要:

  1. 将Ygl替换为Zu;
  2. 将 Xgl 替换为 Yu;
  3. 将 Zgl 替换为 -Xu;

FMatrix Mgl2u = FMatrix::Identity;

Mgl2u.M[0][0] = 0; Mgl2u.M[0][1] = 1; Mgl2u.M[0][2] = 0; Mgl2u.M[1][0] = 0; Mgl2u.M[1][1] = 0; Mgl2u.M[1][2] = 1; Mgl2u.M[2][0] = -1; Mgl2u.M[2][1] = 0; Mgl2u.M[2][2] = 0;

【讨论】:

    猜你喜欢
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 2019-11-23
    • 2014-01-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-01-05
    相关资源
    最近更新 更多