How GLSL outerProduct Calculates Matrices

The outerProduct function in the OpenGL Shading Language (GLSL) creates a matrix from two vectors by computing their outer, or tensor, product. This article explains the underlying mathematical mechanics of the function, how vector dimensions determine the resulting matrix size in GLSL, and how the calculation maps directly to GLSL's column-major matrix structure.

Mathematical Principle

The outer product between a column vector \(\mathbf{c}\) of length \(M\) and a row vector \(\mathbf{r}\) of length \(N\) yields an \(M \times N\) matrix (\(M\) rows, \(N\) columns). Every element \(A_{ij}\) at row \(i\) and column \(j\) in the resulting matrix is calculated by multiplying the \(i\)-th component of the first vector by the \(j\)-th component of the second vector:

\[A_{ij} = c_i \cdot r_j\]

Unlike the dot product (dot()), which multiplies two vectors of the same dimension to produce a single scalar value, the outer product expands two vectors into a full transformation matrix.

GLSL Dimensional Mapping

GLSL matrices follow a mat<columns>x<rows> naming convention, and matrices are stored in column-major order. The outerProduct function signature reflects this relationship:

matNxM outerProduct(vecM c, vecN r);
  • The first parameter c defines the number of rows (\(M\)).
  • The second parameter r defines the number of columns (\(N\)).
  • The return type is a matrix with \(N\) columns and \(M\) rows (matNxM).

For example, passing a vec3 and a vec2 yields a mat2x3 (two columns, three rows):

mat2x3 outerProduct(vec3 c, vec2 r);

How the Elements Are Populated

Because GLSL treats matrices as collections of column vectors (matrix[column][row]), each column \(j\) in the result is simply the entire first vector c scaled by the single scalar component r[j]:

result[j] = c * r[j];

Accessing the element at column \(j\) and row \(i\) corresponds to:

result[j][i] = c[i] * r[j];

Practical Example

Consider the following operation in GLSL:

vec3 c = vec3(1.0, 2.0, 3.0);
vec2 r = vec2(4.0, 5.0);

mat2x3 result = outerProduct(c, r);

The function computes each column sequentially:

  • Column 0 (result[0]): Vector c scaled by r.x (\(4.0\)) \[\begin{bmatrix} 1.0 \times 4.0 \\ 2.0 \times 4.0 \\ 3.0 \times 4.0 \end{bmatrix} = \begin{bmatrix} 4.0 \\ 8.0 \\ 12.0 \end{bmatrix}\]

  • Column 1 (result[1]): Vector c scaled by r.y (\(5.0\)) \[\begin{bmatrix} 1.0 \times 5.0 \\ 2.0 \times 5.0 \\ 3.0 \times 5.0 \end{bmatrix} = \begin{bmatrix} 5.0 \\ 10.0 \\ 15.0 \end{bmatrix}\]

The resulting mat2x3 layout represents the combined \(3 \times 2\) matrix:

\[\begin{bmatrix} 4.0 & 5.0 \\ 8.0 & 10.0 \\ 12.0 & 15.0 \end{bmatrix}\]

This operation is commonly used in shaders for computing covariance matrices, directional lighting tensors, coordinate-space projections, and physical simulation constraints.