如何将gl.drawElements"find"对应的顶点数组缓冲?

How gl.drawElements "find" the corresponding vertices array buffer?

提前谢谢你。

我是 Webgl 的新手,我不太了解 drawElements 方法和我要绘制的当前顶点缓冲区之间的 link。 我大致了解 drawArray 方法发生了什么(例如创建缓冲区,将其绑定到上下文,用数据填充它,指向相应的属性,绘制它)。但是当我尝试对索引数组和较少的顶点数据执行相同操作时,我遇到了这种类型的错误:

[.Offscreen-For-WebGL-0x7fae2b940800]GL ERROR :GL_INVALID_OPERATION : glDrawElements: bound to target 0x8893 : no buffer

也许我的代码提示可以帮助你。

    const cube = new Program(renderer.gl, vertex3d, fragment); // my webgl program
    const cubeData = new Cube(); // Only array of vertices/indices
    const cubeVertexPosition = new ArrayBuffer(renderer.gl, cubeData.vertices, 'STATIC_DRAW'); // ARRAY_BUFFER
    const cubeVertexIndices = new IndexBuffer(renderer.gl, renderer.gl.UNSIGNED_SHORT, cubeData.indices, 'STATIC_DRAW'); // ELEMENT_ARRAY_BUFFER
cubeVertexPosition.attribute('aPosition', 3, 'FLOAT', false); // define attribute corresponding in vertex shader
    cubeVertexPosition.attributePointer(cube); // enableVertexAttribArray + vertexAttribPointer
    [...]
    cubeVertexIndices.draw('TRIANGLES', 0, 36); // drawElements with gl.UNSIGNED_SHORT type

我用 drawArray 绘制成功了:)

([...] 只是制服的矩阵变换);

也许你有一个快速提示可以帮助我理解这个黑魔法,

非常感谢!

drawArray 仅使用一个或多个 ARRAY_BUFFER 从顶点按它们在缓冲区中的顺序绘制的位置,从 first 参数到 count 参数。

drawElements 使用一个或多个 ARRAY_BUFFER AND 一个 ELEMENT_ARRAY_BUFFER 包含指向要绘制的 ARRAY_BUFFER 顶点的索引.在 drawElements 中,count 参数指定要在 ELEMENT_ARRAY_BUFFER 中读取的索引计数,而 offset 指定开始读取 [=22= 的字节偏移量](通常 FirstIndex*sizeof(type) 其中 type 可以是 UNSIGNED_BYTE(1 个字节)、UNSIGNED_SHORT(2 个字节)或 UNSIGNED_INT(4 个字节)。

ELEMENT_ARRAY_BUFFER:

[0][1][2][1][2][0][1][2][3][3][1][2][3][4][5][...

ARRAY_BUFFER:

|   0   |   1    |    2   |    3   |    4   | ...
[x][y][z][x][y][z][x][y][z][x][y][z][x][y][z][...

要正常工作,offset + count*sizeof(type) 不应大于 ELEMENT_ARRAY_BUFFER 字节大小。此外,ELEMENT_ARRAY_BUFFER 中的元素索引应小于 ARRAY_BUFFER.

中包含的顶点数

drawArray一样,drawElements将当前绑定的缓冲区(配置了属性指针)作为数据源。 drawElements 的不同之处在于,您必须使用 ELEMENT_ARRAY_BUFFER 目标指定一个附加元素(索引)缓冲区,例如:

gl.bindBuffer(gl.ARRAY_BUFFER, myVerticesA);
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, myIndicesA);
// configure attributes pointers here
gl.drawElements(gl.TRIANGLES, 12345, gl.UNSIGNED_SHORT, 0);

"how" drawElements 将根据存储在 ELEMENT_ARRAY_BUFFER 中的索引获取 ARRAY_BUFFER 缓冲区中的属性,具体取决于您如何配置属性指针。

假设以下顶点缓冲区具有交错的位置、法线和纹理坐标:

|    p0    ||    n0    ||  t0  ||    p1    ||    n1    ||  t1  |
[px][py][pz][nx][ny][nz][tu][tv][px][py][pz][nx][ny][nz][tu][tv][...

我们定义属性指针如下:

let stride = 8*4;  // 8*float (8 * 4 bytes)
let offp = 0;   // positions at beginning
let offn = 3*4; // normals after 3*float position.
let offt = 6*4; // tex coords after 3*float position + 3*float normal 
gl.vertexAttribPointer(0, 3, gl.FLOAT, false, stride, offp);
gl.vertexAttribPointer(1, 3, gl.FLOAT, false, stride, offn);
gl.vertexAttribPointer(2, 2, gl.FLOAT, false, stride, offt);

使用元素(索引)缓冲区,GL 将根据存储在 ELEMENT_ARRAY_BUFFER 缓冲区中的索引简单地移动指针位置:

 // pseudo-code
 for(let i = start_elem; i < start_elem+count_elem; i++) {

   let index = ELEMENT_ARRAY_BUFFER[i];

   attrib[0] = ARRAY_BUFFER[(index*stride)+offp];
   attrib[1] = ARRAY_BUFFER[(index*stride)+offn];
   attrib[2] = ARRAY_BUFFER[(index*stride)+offt];

 }

您发布的代码不是 WebGL。您正在使用一些从代码中可以清楚地看出的库。 ProgramIndexBufferArrayBuffer 之类的内容都是您正在使用的某些库的一部分。该图书馆如何做事取决于该图书馆。

一般来说,WebGL 有着色器,一个顶点着色器,它的工作是将 gl_Position 设置为每个顶点的剪辑 space 坐标和一个片段着色器,它的工作是设置 [=16] =] 到每个像素的颜色。

顶点着色器通常从属性中获取有关位置的数据。属性通常从缓冲区获取数据。您通过首先使用 gl.bindBuffer(gl.ARRAY_BUFFER, someBuffer) 将缓冲区绑定到 ARRAY_BUFFER 绑定点然后调用 gl.vertexAttribPointer 告诉 WebGL 您希望如何从中获取数据来告诉属性从哪个缓冲区获取数据缓冲区(数据是什么类型,每个顶点有多少个值,顶点之间要跳过多少字节,缓冲区从多远开始)。 gl.vertexAttribPointer 保存给定属性的所有信息以及对绑定到 ARRAY_BUFFER 绑定点的当前缓冲区的引用,因此您可以在那里自由绑定不同的缓冲区以设置另一个属性。

当您调用 gl.drawArrays 时,数据将从您指定的缓冲区中提取到着色器的属性中,着色器的每次迭代都有一组值

至于 gl.drawElements 它需要一个缓冲区,绑定到 ELEMENT_ARRAY_BUFFER 并且当你 cll gl.drawElements 你告诉它缓冲区中的数据类型(gl.UNSIGNED_BYTEgl.UNSIGNED_SHORT)。然后它使用该缓冲区的值从属性缓冲区中提取值。

gl.drawElementsgl.drawArrays 完全相同,如果您在缓冲区中放置一个简单的递增值。例子

 const offset  = 0;
 const numVerts = 100;

 // process 100 vertices from the buffers pointed to by the attributes
 // in order 0 to 99
 gl.drawArrays(gl.POINTS, offset, numVerts)

实际上与

相同
 // fill a buffer with numbers 0 to 99 (0 to numVerts)
 const numVerts = 100;
 const indexData = new Uint16Array(numVerts);
 for (let i = 0; i < numVerts; ++i) {
   indexData[i] = i;
 }
 const indexBuffer = gl.createBuffer();
 gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indexBuffer);
 gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, indexData, gl.STATIC_DRAW);

 // process 100 vertices from the buffers pointed to by the attributes
 // in order 0 to 99
 const offset  = 0;
 gl.drawElements(gl.POINTS, numVerts, gl.UNSIGNED_SHORT, offset);

但当然,因为在第二种情况下您提供了 indexData,所以它不必是连续的。

我建议阅读其他一些 webgl tutorials