Component vs Matrix Math

Component-Wise Versus Matrix Multiplication

Between two vectors, or a scalar and anything, * multiplies component by component. When a matrix meets a vector or matrix it becomes linear algebra: m * v treats v as a column vector, v * m as a row vector, and m1 * m2 is the (non-commutative) matrix product. matrixCompMult(), dot() and cross() cover the rest:

The five meanings of multiplication in GLSLJavaScript
const m = 'mat2 m = mat2(1.0, 2.0, 3.0, 4.0);   // columns (1, 2) and (3, 4)\n';
show(m + 'vec2 v = vec2(1.0, 1.0); result = vec4(m * v, v * m);');
show(m + 'result = vec4((m * m)[0], matrixCompMult(m, m)[0]);');
show('vec3 a = vec3(1.0, 2.0, 3.0); result = vec4(a * a, dot(a, a));');
show('result = vec4(cross(vec3(1.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0)), 0.0);');
Output
4, 6, 3, 7
7, 10, 1, 4
1, 4, 9, 14
0, 0, 1, 0

m * v sums the columns weighted by v; v * m dots v with each column. Vertex shaders later in this chapter compute uProjection * uView * uModel * position, read right to left: the model matrix applies first.

A book outline transformed by v * s, m * v and v * m to show what GLSL's * meansHTMLLive
<!doctype html>
<style>
  body { margin: 0; font: 12px system-ui, sans-serif; background: #f7f4ee; color: #333; }
  canvas { display: block; width: 100%; max-width: 600px; }
  .names { display: flex; max-width: 600px; text-align: center; font-family: monospace; }
  .names div { flex: 1; padding: 4px 0; }
</style>
<canvas id="view" width="600" height="170"></canvas>
<div class="names"><div>v (original)</div><div>v * vec2(1.4, 0.6)</div><div>m * v</div><div>v * m</div></div>
<script>
const gl = document.getElementById('view').getContext('webgl2');
const vs = `#version 300 es
in vec2 aPos;
uniform int uMode; uniform float uAngle;
void main() {
  float c = cos(uAngle), s = sin(uAngle);
  mat2 m = mat2(c, s, -s, c);             // columns (c, s) and (-s, c): a rotation
  vec2 v = aPos;
  if (uMode == 1) v = v * vec2(1.4, 0.6); // vector * vector: component by component
  if (uMode == 2) v = m * v;              // column vector: rotates counter-clockwise
  if (uMode == 3) v = v * m;              // row vector: same as transpose(m) * v, the other way
  gl_Position = vec4(v * 0.8, 0.0, 1.0);
}`;
const fs = `#version 300 es
precision mediump float;
uniform vec3 uColor; out vec4 outColor;
void main() { outColor = vec4(uColor, 1.0); }`;
const program = gl.createProgram();
for (const [type, src] of [[gl.VERTEX_SHADER, vs], [gl.FRAGMENT_SHADER, fs]]) {
  const s = gl.createShader(type);
  gl.shaderSource(s, src); gl.compileShader(s);
  if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) throw new Error(gl.getShaderInfoLog(s));
  gl.attachShader(program, s);
}
gl.linkProgram(program);
gl.useProgram(program);

// A book cover outline as line segments, with a title bar and an arrow so orientation shows.
const lines = [
  -0.5, -0.8, 0.5, -0.8,   0.5, -0.8, 0.5, 0.8,   0.5, 0.8, -0.5, 0.8,   -0.5, 0.8, -0.5, -0.8,
  -0.38, -0.8, -0.38, 0.8,                                  // spine
  -0.25, 0.5, 0.35, 0.5,   -0.25, 0.4, 0.2, 0.4,            // title lines
  0.0, -0.5, 0.0, 0.15,    0.0, 0.15, -0.12, 0.0,   0.0, 0.15, 0.12, 0.0,   // up arrow
];
gl.bindBuffer(gl.ARRAY_BUFFER, gl.createBuffer());
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(lines), gl.STATIC_DRAW);
const aPos = gl.getAttribLocation(program, 'aPos');
gl.enableVertexAttribArray(aPos);
gl.vertexAttribPointer(aPos, 2, gl.FLOAT, false, 0, 0);
const uMode = gl.getUniformLocation(program, 'uMode');
const uAngle = gl.getUniformLocation(program, 'uAngle');
const uColor = gl.getUniformLocation(program, 'uColor');
const colors = [[0.3, 0.3, 0.3], [0.25, 0.49, 0.23], [0.12, 0.37, 0.55], [0.71, 0.27, 0.18]];

function frame(ms) {
  gl.viewport(0, 0, 600, 170);
  gl.clearColor(0.97, 0.96, 0.93, 1);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.uniform1f(uAngle, 0.6 + 0.4 * Math.sin(ms * 0.001));
  for (let mode = 0; mode < 4; mode++) {
    gl.viewport(mode * 150 + 5, 0, 140, 170);
    gl.uniform1i(uMode, 0); gl.uniform3f(uColor, 0.85, 0.82, 0.76);   // faint original
    gl.drawArrays(gl.LINES, 0, lines.length / 2);
    gl.uniform1i(uMode, mode); gl.uniform3fv(uColor, colors[mode]);
    gl.drawArrays(gl.LINES, 0, lines.length / 2);
  }
  requestAnimationFrame(frame);
}
requestAnimationFrame(frame);
</script>