【问题标题】:Rotation, Translation and Default Camera Location旋转、平移和默认相机位置
【发布时间】:2017-03-06 19:15:13
【问题描述】:

我只是在玩MTKView 中的模板设置;而且,我一直在尝试理解以下内容:

  1. 摄像头的默认位置。

  2. 使用MDLMeshMTKMesh 创建图元时的默认位置。

  3. 为什么旋转还涉及平移。

相关代码:

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));
matrix_float4x4 base_mv = matrix_multiply(_viewMatrix, base_model);
matrix_float4x4 modelViewMatrix = matrix_multiply(base_mv, matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

上述代码来自模板的_update方法;显然,它正在尝试旋转模型而不是相机。但令我困惑的是,它还需要翻译。我读过诸如“因为 it 总是以(0, 0, 0) 旋转”之类的声明。但是为什么(0, 0, 0),如果对象被放置在其他地方呢?此外,在我看来,相机正在查看正 z 轴(问题 1)而不是通常的负 z 轴,因为如果我改变:

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, 5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

到:

matrix_float4x4 base_model = matrix_multiply(matrix_from_translation(0.0f, 0.0f, -5.0f), matrix_from_rotation(_rotation, 0.0f, 1.0f, 0.0f));

屏幕上不会显示任何内容,因为对象似乎在相机后面,这意味着相机正在查看正 z 轴。

如果我设置matrix_from_translation(0.0f, 0.0f, 0.0f)(全为零),则对象不会像我预期的那样在 y 轴上旋转(问题 3)。

我试图找出MDLMeshMTKMesh 默认放置的位置(问题2),但我找不到记录其位置的属性。以下也是通过模板创建基元的方式:

MDLMesh *mdl = [MDLMesh newBoxWithDimensions:(vector_float3){2,2,2} segments:(vector_uint3){1,1,1}
                geometryType:MDLGeometryTypeTriangles inwardNormals:NO
                allocator:[[MTKMeshBufferAllocator alloc] initWithDevice: _device]];

_boxMesh = [[MTKMesh alloc] initWithMesh:mdl device:_device error:nil];

在不知道上述方法生成的位置的情况下,它妨碍了我对旋转和平移如何工作以及相机在 Metal 中的默认位置的理解。

谢谢。

【问题讨论】:

  • 有一些非常棒的 3D 数学书籍,我推荐 Dunn 的 3D Math Primer for Graphics and Game Development 和 Essential Mathematics for Games and Interactive Applications。您遇到的问题称为模型视图转换和外观。那应该可以帮助您进行谷歌搜索。还有矩阵乘法。 @warrenm 提供了一个很好的答案。

标签: metal metalkit


【解决方案1】:

我认为矩阵在代码中的写入顺序有些混淆了意图,因此我将实际发生的事情归结为以下伪代码以便于解释。

我已经用模板中的那个矩阵替换了最后一个矩阵,因为您的修改只会使绕 Y 轴的旋转加倍。

modelViewMatrix = identity *
                  translate(0, 0, 5) *
                  rotate(angle, axis(0, 1, 0)) *
                  rotate(angle, axis(1, 1, 1))

由于矩阵在着色器中向量的左侧相乘,我们将从右到左读取矩阵以确定它们的累积效果。

首先,我们围绕轴 (1, 1, 1) 旋转立方体,该轴对角线通过原点。然后,我们围绕 Y 轴围绕立方体旋转。这些旋转结合起来形成一种“翻滚”动画。然后,我们将立方体沿 +Z 轴平移 5 个单位(正如您所见,由于我们认为我们的世界是左撇子,所以它进入屏幕)。最后,我们应用我们的相机变换,它被硬编码为单位矩阵。我们可以使用沿 +Z 的额外正平移作为相机矩阵,以将立方体移离相机更远,或者使用负值将立方体移动得更近。

回答您的问题:

  1. 如果你想这样想的话,除了原点 (0, 0, 0) 之外,没有相机的默认位置。通过将顶点位置乘以表示相机在世界中放置方式的变换的倒数,您可以在世界中“定位”相机。

  2. 模型 I/O 构建围绕原点“居中”的网格,在某种程度上这对生成的形状有意义。圆柱体、椭球体和长方体实际上以原点为中心,而圆锥体的顶点位于原点,轴沿 -Y 延伸。

  3. 旋转并不真正涉及平移,因为它与它结合。平移的原因是我们需要将立方体放置在远离相机的位置;否则我们在绘图时会在里面。

关于操作顺序的最后一点说明:如果我们在旋转之前应用平移,它将导致框围绕相机“环绕”,因为正如您所注意到的,旋转总是相对于当前参照系的原点。

【讨论】:

  • 嗨,沃伦;感谢您的反馈。关于乘法的顺序:我认为只有当乘法发生在着色器(?)中时,顺序才重要;因此,我是从左到右阅读的。我的理由是,既然我们多次调用matrix_multiply,系统怎么知道这些调用之间的顺序?如果它只有一个matrix_multiply,那么我会理解它正在以正确的顺序进行乘法运算。 (续)
  • 关于摄像头位置:我们可以说(0, 0, 0) 就在摄像头所在的屏幕中间以及I/O 网格的中心位置吗?最后,我的困惑是,在 GL 中,当围绕其轴对对象应用旋转时,我们通常需要这样做:glTranslate(.., .., some negative number)glRotate(.., .., ..),然后是glTranslate(.., .., a positive number of the negative number from the preceding glTranslate);你会知道为什么我们需要在 GL 中这样做,而不是像我们这里的示例中那样在 Metal 中这样做吗?谢谢。
  • @Unheilig 乘法的顺序很重要,因为矩阵的乘法不是可交换的。 A(B)!= B(A)。您可以通过双向乘以 2x2 平凡矩阵来轻松验证这一点。矩阵乘法是关联的,所以你可以调用 C = A(B) 和 F = D(E),如果你做了 A(B(D(E))),C(F) 将等于。在应用程序代码中预乘矩阵模型视图变换,然后将每个顶点乘以着色器中的结果是很常见的。您需要根据预乘的顺序在着色器中正确地乘以它。
  • @Unheilig 您所描述的是如何在任意参考系内围绕任意轴旋转。你不需要在这里做 T^-1(R)T 技术的原因是所有的旋转都是相对于世界坐标系的原点发生的,所以 T 和 T^-1 都是同一性的。跨度>
  • 对 :-) 现在我看到了不同之处。非常感谢,特别是您的耐心。