The stride is how many bytes to skip to get to the next value for that attribute. For a mat3 there are 3 attributes, one for each row of the matrix. The data for each attribute, assuming you put your matrices in a buffer linearly next to each other, is:
| Matrix0 | Matrix1 | Matrix2 | ...
| row0 | row1 | row2 | row0 | row1 | row2 | row0 | row1 | row2 | ...
| x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | ...
so the first attribute wants data for row0, for each matrix. To get from row0 in the first matrix to row0 in the second matrix is bytesPerMatrix
| Matrix0 | Matrix1 | Matrix2 | ...
| row0 | row1 | row2 | row0 | row1 | row2 | row0 | row1 | row2 | ...
| x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | x,y,z | ...
| --- bytesPerMatrix -->|
| --- bytesPerMatrix -->|
| --- bytesPerMatrix -->|
stride is how many bytes to skip to get to the next value, not how many bytes to read. How many bytes to read is defined by the size and type parameters of the attribute as in
const size = 3;
const type = gl.FLOAT;
const normalize = false;
const stride = bytesPerMatrix;
const offset = row * bytesPerRow
gl.vertexAttribPointer(location, size, type, normalize, stride, offset);
So above because size
= 3 and type
= FLOAT
it will read 12 bytes.
The process is
- start at
offset
bytes in the buffer
- read 12 bytes from the buffer and apply them to the attribute's value
- add
stride
to offset
- goto 2
for however many vertices you asked it to process.
note: that assumes you actually put your data in the buffer one matrix after another. You don't have to do that. You could put all row0s next to each other, followed by all row1s, followed by all row2s. Or you could even put all row0s in a different buffer from row1s and row2s. I don't think I've ever seen anyone do that but just pointing it out that the way described at the top is not set it stone. It's just the most common way.