You seem to be mixing two questions, and I think the webpage on scratchapixel is trying to explain. From the information on the page you are linking to, everything looks pretty clear, but getting this type of material right in your mind is difficult. You have a theory (what you do in math with pen and paper) and what you do with your implementation (C ++). These are two different problems.
Math: you can use two records, both columns and rows. As mentioned by GraphicsMuncher on this web page, as well as with the main row vector on paper, you need to write the multiplication of the vector matrix vM, where v is the row vector (1x4) and M is your 4x4 matrix, why, since you can only mathematically write [1x4 ] * [4x4], not vice versa. Similarly, if you use a column, then the vector must be written vertically or in the [4x1] notation (4 rows, 1 column). Thus, matrix multiplication can only be written: [4x4] [4x1]. The matrix is placed before the vector: Mv. The first record is called post-multiplication, and the second (Mv) is called pre-multiplication (the matrix in front). Now, as GraphicsMuncher mentioned, if you need to convert a vector (or point), then you need to pay attention to the order of multiplication when you write them to ON PAPER. If you want to translate something with the matrix T, and then rotate it with R, and then scale it with S, then in the main world of the column you need to write v '= S * R * T * v. In the line of the important world, you need to write v '= v * T * R * S.
This is for theory.
Computer: then the moment comes when you decide to implement this in C ++. It’s good that C ++ does not impose anything on you. You can match the values of your matrix coefficients in memory the way you want, and you can write code to multiply the matrix by another matrix the way you want. Just as you access coefficients for vector-matrix multiplication, you are completely dependent.
You need to clearly distinguish how you compare your coefficients in memory and what conventions you should use from a mathematical point of view to represent your vectors. These are two independent problems. For example, in your case, you are likely to declare your matrix class as an array of 16 contiguous floats. It's great. If the coefficients m14, m24, m34 are part of the translation of the matrix (Tx, Ty, Tz), so you assume that your “agreement” has a large row value, even if you are invited to use the agreement with the OpenGL matrix, which, as they say, is the main . Here, your confusion arises from the fact that the mapping of coefficients in memory is different from the mental representation that you make from the matrix of "columns". You encode a “row”, but you were said to use (mathematically) a “column”, hence your difficulty in understanding whether you are doing everything right or wrong.
It is important to see the matrix as a representation of the coordinate system defined by the three axes, and translation. Where and how you store this data in memory is completely up to you. Assuming that the three vectors representing the three axes of the coordinate system are called AX (x, y, z), AY (x, y, z), AZ (x, y, z), and the translation vector is denoted by (Tx, Ty, Tz ), then mathematically, if you use the column vector that you have (latex is not supported, I think):
AXx AYx AZx Tx M = AXy AYy AZy Ty AXz AYz AZz Tz 0 0 0 1
The axes of the coordinate system are written vertically. Now if you use row-major:
AXx AXy AXz 0 M = AYx AYy AYz 0 AZx AZy AZz 0 Tx Ty Tz 1
The axes of the coordinate system are recorded horizontally. So the problem now, when it comes to the world of computers, is how to store these coefficients in memory. You also can:
float m[16] = { AXx, AXy, AXz, 0, AYx, AYy, AYz, 0, AZx, AZy, AZz, 0, Tx, Ty, Tz, 1};
Does it tell you which agreement you are using? No. You can also write:
float m[16] = { AXx, AXy, AXz, Tx, AYx, AYy, AYz, Ty, AZx, AZy, AZz, Tz, 0, 0, 0, 1};
or
float m[16] = { AXx, AYx, AZx, Tx, AXy, AYy, AZy, Ty, AXz, AYz, AZz, Tz, 0, 0, 0, 1};
which does not give you a specific indication of which “mathematical” convention you are using. You simply store 16 coefficients in memory in different ways, and that’s great if you know what it is so that you can later access it. Now keep in mind that a vector multiplied by a matrix should give you the same vector, regardless of whether you use mathematical notation of a row or column. Thus, it is important that you multiply the coordinates (x, y, z) of your vector by the correct coefficients from the matrix, which requires knowing how “you” decided to store the matrix coefficient in memory:
Vector3 vecMatMult (Vector3 v, float AXx, float AXy, float AXz, float Tx, float AYx, float AYy, float AYz, float Ty, float AZz, float AZy, float AZz, float Tz) { return Vector3( vx * AXx + vy * AYx + vz * AZx + Tx, vx * AXy + vy * AYy + vz * AZy + Ty, vx * AXz + vy * AYz + vz * AZz + Tz }
EDIT: The code above was wrong, now it is fixed.
I wrote this function to emphasize the fact that no matter what convention you use, the result of multiplying the vector matrix is simply multiplying and adding between the coordinates of the vector input and the coordinates of the coordinate axis of the AX, AY and AZ systems (regardless of the notation you use and regardless of how they are stored in memory).
If you use:
float m[16] = { AXx, AXy, AXz, 0, AYx, AYy, AYz, 0, AZx, AZy, AZz, 0, Tx, Ty, Tz, 1};
You need to call:
vecMatMult(v, m[0], m[1], m[2], m[12], m[4], m[5], m[6], m[13], ...
If you use:
float m[16] = { AXx, AYx, AZx, Tx, AXy, AYy, AZy, Ty, AXz, AYz, AZz, Tz, 0, 0, 0, 1};
You need to call:
vecMatMult(v, m[0], m[4], m[8], m[3], m[1], m[5], m[9], m[10], ...
Does this tell you which agreement you are using? No. You just need to name the correct coefficients in the right places when you do vec * mat multiplication. And that’s all that is in it, as disgusting as it may seem.
Now everything is a little different when it comes to multiplying mat * mat. You can assume that the order in which you multiply the matrices does not match. So R * S * T does not match T * S * R. The order really matters. Now, if you use "row major", then mathematically you need to write (using your notation):
mt11 = ml11 * mr11 + ml12 * mr21 + m13 * m31 + ml14 * mr41
where ml is the left matrix and mr is the right side: mt = ml * mr. However, please note that I did not use the brackets [] for access indices, because I do not want to suggest that we refer to the elements stored in the 1D array here. We are just talking about matrix coefficients. If you want to write this in C ++, it all depends on how you saved your coefficients in memory, as described above.
Hope this helps.