Search code examples
metal

Is there a Metal library function to create a simd rotation matrix?


There seems to be at least half a dozen matrix libraries in the Apple system. One of them is the simd library with types that work the same in CPU and GPU code.

import simd
let mat = float3x3(...)
let vec = float3(...)
mat * vec

I'm having trouble finding documentation for it. Unlike most things it does not show up in Xcode's documentation browser. I know that a different library (GLKit) has matrix types that have functions for building rotation matrixes. For example,

GLKMatrix3MakeXRotation(radians)
GLKMatrix3RotateY(mat, radians)

Are there similar functions for the simd matrix types?


Solution

  • There are not currently utility functions for creating such matrices in simd.framework, Metal, or MetalKit. However, you can use GLKit's matrix functions and convert the resulting GLKMatrix4s into float4x4s before, for example, copying them into a Metal buffer for use in a shader.

    A GLKMatrix4 is just a union containing an array of 16 floats, stored in column-major order.

    Therefore, we can write an extension on float4x4 that allows initializing a simd matrix with a GLKit matrix:

    extension float4x4 {
        init(matrix: GLKMatrix4) {
            self.init(columns: (float4(x: matrix.m00, y: matrix.m01, z: matrix.m02, w: matrix.m03),
                                float4(x: matrix.m10, y: matrix.m11, z: matrix.m12, w: matrix.m13),
                                float4(x: matrix.m20, y: matrix.m21, z: matrix.m22, w: matrix.m23),
                                float4(x: matrix.m30, y: matrix.m31, z: matrix.m32, w: matrix.m33)))
        }
    }
    

    I verified that the resulting matrix matched my expectations by creating a GLKit matrix that represents a 45-degree rotation counterclockwise about the +Z axis, and ensuring that it does, in fact, rotate the unit vector <1, 0, 0> onto the unit vector <sqrt(2)/2, sqrt(2)/2, 0>:

    let rotation = GLKMatrix4MakeZRotation(.pi / 4)
    let simdRotation = float4x4(matrix: rotation)
    let v = float4(1, 0, 0, 0)
    let vp = simdRotation * v
    print("\(vp)")
    
    
    > float4(0.707107, 0.707107, 0.0, 0.0)
    

    Note that I'm abiding the convention here that matrix-vector multiplication treats the vector as a column vector and places the matrix on the left, which is the most common convention in current use.

    There is one caveat you should be aware of with respect to GLKit and Metal's clip space. You can read about the issue, and how to correct for it, here.