mat4 attrib的步幅与偏移量

问题描述

我正在阅读 this answer on SO,其中有人提取了 mat4 属性

在设置顶属性数组时,我注意到一件事:

gl.vertexAttribPointer(row3Location,floatsPerRow,gl.FLOAT,false,bytesPerMatrix,row3Offset);

我知道提供的 mat4 占用 4 个属性槽,但为什么我们将 bytesPerMatrix 作为步幅而不是 bytesPerRow 之类的东西传递?每个属性槽不应该从其偏移量中提取 16 个字节而不是 64 个字节吗?

这就是我想象的 16 个字节的步长和 16 的倍数的偏移量。

0000111122223333444455556666777788889999AAAABBBBCCCCddddEEEEFFFF
^---------------
                ^---------------
                                ^---------------
                                                ^---------------

这就是我想象的 64 个字节的步长和 16 的倍数的偏移量。

0000111122223333444455556666777788889999AAAABBBBCCCCddddEEEEFFFF
^---------------------------------------------------------------
                ^---------------------------------------------------------------
                                ^---------------------------------------------------------------
                                                ^---------------------------------------------------------------
^ considerable overlap when pulling attributes for matrix

所以,显然我的步幅和偏移心智模型是错误的。这实际上是如何工作的?当这个属性一次只拉一个 vec4 的等价物时,为什么 stride 需要是整个矩阵的大小?

解决方法

stride 是要跳过多少字节才能到达该属性的下一个值。对于 mat3,有 3 个属性,矩阵的每一行一个。假设您将矩阵彼此线性地放在缓冲区中,每个属性的数据是:

|        Matrix0        |        Matrix1        |        Matrix2        | ...
| row0  | row1  | row2  | row0  | row1  | row2  | row0  | row1  | row2  | ...
| x,y,z | x,z | ...

所以第一个属性需要 row0 的数据,对于每个矩阵。从第一个矩阵中的 row0 到第二个矩阵中的 row0 是 bytesPerMatrix

|        Matrix0        |        Matrix1        |        Matrix2        | ...
| row0  | row1  | row2  | row0  | row1  | row2  | row0  | row1  | row2  | ...
| x,z | ...
| --- bytesPerMatrix -->|
                        | --- bytesPerMatrix -->|
                                                | --- bytesPerMatrix -->|

stride 是要跳过多少字节才能到达下一个值,而不是要读取多少字节。读取多少字节由属性的大小和类型参数定义,如

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);

以上是因为 size = 3 和 type = FLOAT 它将读取 12 个字节。

过程是

  1. 从缓冲区中的 offset 个字节开始
  2. 从缓冲区中读取 12 个字节并将它们应用到属性值
  3. stride 添加到 offset
  4. 转到 2

无论您要求它处理多少个顶点。

注意:假设您实际上将数据一个接一个地放入缓冲区。你不必那样做。您可以将所有 row0 并排放置,然后是所有 row1,然后是所有 row2。或者您甚至可以将所有 row0 放在与 row1s 和 row2s 不同的缓冲区中。我认为我从未见过任何人这样做,但只是指出顶部描述的方式并没有一成不变。这只是最常见的方式。