Game Physics Cookbook
上QQ阅读APP看书,第一时间看更新

Multiplication

Like a vector, there are many ways to multiply a matrix. In this chapter we will cover multiplying matrices by a scalar or by another matrix. Scalar multiplication is component wise. Given a Multiplication matrix M and a scalar s, scalar multiplication is defined as follows:

Multiplication

We can also multiply a matrix by another matrix. Two matrices, A and B, can be multiplied together only if the number of columns in A matches the number of rows in B. That is, two matrices can only be multiplied together if their inner dimensions match.

When multiplying two matrices together, the dimension of the resulting matrix will match the outer dimensions of the matrices being multiplied. If A is an Multiplication matrix and B is an Multiplication matrix, the product of AB will be an Multiplication matrix. We can find each element of the matrix AB with the following formula:

Multiplication

This operation concatenates the transformations represented by the two matrices into one matrix. Matrix multiplication is not cumulative. Multiplication. However, matrix multiplication is associative, meaning Multiplication.

Getting ready

Just as with the Transpose operation, we're going to write a generic matrix multiplication function that works on arrays representing matrices of any size. Then, we're going to call this generic matrix multiply function from operator overrides for mat2, mat3, and mat4.

How to do it…

Follow these steps to implement scalar multiplication for two, three and four dimensional square matrices:

  1. We're going to start with scalar multiplication. First, add the declaration for scalar multiplication to matrices.h.:
    mat2 operator*(const mat2& matrix, float scalar);
    mat3 operator*(const mat3& matrix, float scalar);
    mat4 operator*(const mat4& matrix, float scalar);
  2. Next, add the implementation for the scalar multiplication functions to matrices.cpp:
    mat2 operator*(const mat2& matrix, float scalar) {
        mat2 result;
        for (int i = 0; i < 4; ++i) {
            result.asArray[i] = matrix.asArray[i] * scalar;
        }
        return result;
    }
    
    mat3 operator*(const mat3& matrix, float scalar) {
        mat3 result;
        for (int i = 0; i < 9; ++i) {
            result.asArray[i] = matrix.asArray[i] * scalar;
        }
        return result;
    }
    
    mat4 operator*(const mat4& matrix, float scalar) 
        mat4 result;
        for (int i = 0; i < 16; ++i) {
            result.asArray[i] = matrix.asArray[i] * scalar;
        }
        return result;
    }
  3. Now it's time to implement matrix-matrix multiplication. First, add the declaration for the generic matrix Multiply function and the overridden matrix multiplication operators to matrices.h. The generic Multiply function returns a Boolean value because the operation can fail. Matrix multiplication fails if the inner dimensions of the matrices being multiplied are not the same:
    bool Multiply(float* out, const float* matA, int aRows, 
       int aCols, const float* matB, int bRows, int bCols);
    mat2 operator*(const mat2& matA, const mat2& matB);
    mat3 operator*(const mat3& matA, const mat3& matB);
    mat4 operator*(const mat4& matA, const mat4& matB);
  4. Implement the generic Multiply function in matrices.cpp:
    bool Multiply(float* out, const float* matA, int aRows, 
       int aCols, const float* matB, int bRows, int bCols) {
       if (aCols != bRows) { 
          return false; 
       }
       for (int i = 0; i < aRows; ++i) {
          for (int j = 0; j < bCols; ++j) {
             out[bCols * i + j] = 0.0f;
             for (int k = 0; k < bRows; ++k) {
                int a = aCols * i + k;
                int b = bCols * k + j;
                out[bCols * i + j] += matA[a] * matB[b];
             }
          }
       }
       return true;
    }
  5. Implement the overridden matrix multiplication operators in matrices.cpp. These operators are going to call the generic Multiply function with the proper arguments:
    mat2 operator*(const mat2& matA, const mat2& matB) {
        mat2 res;
        Multiply(res.asArray, matA.asArray, 
           2, 2, matB.asArray, 2, 2);
        return res;
    }
    
    mat3 operator*(const mat3& matA, const mat3& matB) {
        mat3 res;
        Multiply(res.asArray, matA.asArray, 
           3, 3, matB.asArray, 3, 3);
        return res;
    }
    
    mat4 operator*(const mat4& matA, const mat4& matB) {
        mat4 res;
        Multiply(res.asArray, matA.asArray, 
           4, 4, matB.asArray, 4, 4);
        return res;
    }

How it works…

It may not be obvious from the preceding code but, when multiplying matrices A and B, each element i, j of the result is the dot product of row i from matrix A and column j from matrix B:

How it works…

This figure demonstrates finding element 3,2 when multiplying matrices A and B. To find element 3,2 we take the dot product of row 3 from matrix A and column 2 from matrix B.

This is why the inner dimensions of the two matrices being multiplied together must match, so we take the dot product of vectors that have the same size.