Search code examples
metalmetalkit

Rotation, Translation and Default Camera Location


I am just playing around the template setup in MTKView; and, I have been trying to understand the followings:

  1. Default location of the camera.

  2. The default location when creating primitives using MDLMesh and MTKMesh.

  3. Why does a rotation involve also a translation.

Relevant code:

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));

The preceding code is from the _update method by the template; evidently, it is trying to rotate the model instead of the camera. But what baffles me is the fact that it requires also a translation. I have read claims such as "because it always rotates at (0, 0, 0)". But why (0, 0, 0), if the object is placed somewhere else? Also, it appears to me that the camera is looking at the positive z-axis (question 1) instead of the usual negative z-axis because if I change:

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));

to:

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));

nothing will be displayed on the screen because it appears that the object is behind the camera, which means that the camera is looking at the positive z-axis.

If I set matrix_from_translation(0.0f, 0.0f, 0.0f) (all zeros), the object simply rotate not on the y-axis (question 3) as I expected.

I have tried to find out where the MDLMesh and MTKMesh is placed by default (question 2), but I could not find a property that logs its position. The following is, also by the template, how the primitive is created:

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];

Without knowing its location generated by the above method, it hinders my understanding of how the rotation and translation work and the default location the camera in Metal.

Thanks.


Solution

  • I think the order in which the matrices are written in the code somewhat obfuscates the intent, so I've boiled down what's actually happening into the following pseudocode to make it easier to explain.

    I've replaced that last matrix with the one from the template, since your modification just has the effect of doubling the rotation about the Y axis.

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

    Since the matrix is multiplied on the left of the vector in the shader, we're going to read the matrices from right to left to determine their cumulative effect.

    First, we rotate the cube around the axis (1, 1, 1), which passes diagonally through the origin. Then, we rotate about the cube about the Y axis. These rotations combine to form a sort of "tumble" animation. Then, we translate the cube by 5 units along the +Z axis (which, as you observe, goes into the screen since we're regarding our world as left-handed). Finally, we apply our camera transformation, which is hard-coded to be the identity matrix. We could have used an additional positive translation along +Z as the camera matrix to move the cube even further from the camera, or a negative value to move the cube closer.

    To answer your questions:

    1. There is no default location for the camera, other than the origin (0, 0, 0) if you want to think of it like that. You "position" the camera in the world by multiplying the vertex positions by the inverse of the transformation that represents how the camera is placed in the world.

    2. Model I/O builds meshes that are "centered" around the origin, to the extent this makes sense for the shape being generated. Cylinders, ellipsoids, and boxes are actually centered around the origin, while cones are constructed with their apex at the origin and their axis extending along -Y.

    3. The rotation doesn't really involve the translation as much as it's combined with it. The reason for the translation is that we need to position the cube away from the camera; otherwise we'd be inside it when drawing.

    One final note on order of operations: If we applied the translation before the rotation, it would cause the box to "orbit" around the camera, since as you note, rotations are always relative to the origin of the current frame of reference.