How Does GLSL Evaluate Matrix-Vector Multiplication?

In the OpenGL Shading Language (GLSL), matrix-vector multiplication is evaluated using standard linear algebra rules, with the mathematical behavior determined by whether the vector appears on the right or left side of the operator. GLSL treats vectors on the right (M * v) as column vectors and vectors on the left (v * M) as row vectors. Internally, matrices are structured around column vectors, meaning evaluation translates directly into either linear combinations of matrix columns or dot products against matrix rows and columns.

Matrix Layout and Indexing in GLSL

Before evaluating arithmetic operations, it is necessary to understand how matrices are indexed in GLSL. GLSL matrices use column-major notation:

Consider a standard \(4 \times 4\) matrix \(M\):

\[M = \begin{bmatrix} m_{00} & m_{10} & m_{20} & m_{30} \\ m_{01} & m_{11} & m_{21} & m_{31} \\ m_{02} & m_{12} & m_{22} & m_{32} \\ m_{03} & m_{13} & m_{23} & m_{33} \end{bmatrix} = \begin{bmatrix} \mathbf{c}_0 & \mathbf{c}_1 & \mathbf{c}_2 & \mathbf{c}_3 \end{bmatrix}\]

Here, each \(\mathbf{c}_j\) corresponds directly to the vector stored at M[j].

Matrix Times Vector: M * v

When multiplying a matrix by a vector on the right, GLSL treats the vector \(\mathbf{v}\) as a column vector (\(4 \times 1\)). The operation produces a new column vector \(\mathbf{u}\):

\[\mathbf{u} = M \mathbf{v}\]

Component-Wise Evaluation

Mathematically, each row of the matrix computes a standard dot product with the vector \(\mathbf{v}\):

\[u_0 = m_{00} v_x + m_{10} v_y + m_{20} v_z + m_{30} v_w\]

\[u_1 = m_{01} v_x + m_{11} v_y + m_{21} v_z + m_{31} v_w\]

\[u_2 = m_{02} v_x + m_{12} v_y + m_{22} v_z + m_{32} v_w\]

\[u_3 = m_{03} v_x + m_{13} v_y + m_{23} v_z + m_{33} v_w\]

Linear Combination Interpretation

Because GLSL internally organizes matrices by columns, the operation can also be evaluated as a linear combination of the matrix’s column vectors scaled by the components of \(\mathbf{v}\):

\[\mathbf{u} = v_x \mathbf{c}_0 + v_y \mathbf{c}_1 + v_z \mathbf{c}_2 + v_w \mathbf{c}_3\]

In GLSL code, evaluating vec4 u = M * v; is mathematically equivalent to:

vec4 u = v.x * M[0] + v.y * M[1] + v.z * M[2] + v.w * M[3];

Vector Times Matrix: v * M

When the vector is placed on the left side of the operator, GLSL treats the vector as a row vector (\(1 \times 4\)):

\[\mathbf{u}^T = \mathbf{v}^T M\]

Component-Wise Evaluation

In this form, each component of the resulting row vector is evaluated as the dot product between the input vector \(\mathbf{v}\) and the corresponding column vector of \(M\):

\[u_0 = \mathbf{v} \cdot \mathbf{c}_0 = v_x m_{00} + v_y m_{01} + v_z m_{02} + v_w m_{03}\]

\[u_1 = \mathbf{v} \cdot \mathbf{c}_1 = v_x m_{10} + v_y m_{11} + v_z m_{12} + v_w m_{13}\]

\[u_2 = \mathbf{v} \cdot \mathbf{c}_2 = v_x m_{20} + v_y m_{21} + v_z m_{22} + v_w m_{23}\]

\[u_3 = \mathbf{v} \cdot \mathbf{c}_3 = v_x m_{30} + v_y m_{31} + v_z m_{32} + v_w m_{33}\]

In GLSL code, vec4 u = v * M; is mathematically equivalent to:

vec4 u = vec4(dot(v, M[0]), dot(v, M[1]), dot(v, M[2]), dot(v, M[3]));

Dimension Matching for Non-Square Matrices

For non-square matrices (matCxR), matrix-vector multiplications enforce strict dimension rules: