什么是OpenGL中的顶点阵列? WebGL2?

时间:2018-05-09 13:51:31

标签: opengl-es webgl webgl2

我一直在使用WebGL1一段时间,但现在我正在学习更多关于WebGL2的内容,我对Vertex Array实际做的事情感到困惑。例如,在下面的example中,我可以删除对它们的所有引用(例如创建,绑定,删除),并且示例继续有效。

1 个答案:

答案 0 :(得分:3)

这已在别处解释,但您可以认为WebGL1和WebGL2都有一个顶点数组。它只是WebGL1默认只有一个,因为WebGL2可以创建多个顶点数组(尽管99.9%的所有WebGL1实现都支持它们作为扩展)

顶点数组是所有属性状态加上ELEMENT_ARRAY_BUFFER绑定的集合。

您可以像这样想到WebGL状态

function WebGLRenderingContext() {
   // internal WebGL state
   this.lastError: gl.NONE,
   this.arrayBuffer = null;
   this.vertexArray = {
     elementArrayBuffer: null,
     attributes: [
       { enabled: false, type: gl.FLOAT, size: 3, normalized: false, 
         stride: 0, offset: 0, value: [0, 0, 0, 1], buffer: null },
       { enabled: false, type: gl.FLOAT, size: 3, normalized: false, 
         stride: 0, offset: 0, value: [0, 0, 0, 1], buffer: null },
       { enabled: false, type: gl.FLOAT, size: 3, normalized: false, 
         stride: 0, offset: 0, value: [0, 0, 0, 1], buffer: null },
       { enabled: false, type: gl.FLOAT, size: 3, normalized: false, 
         stride: 0, offset: 0, value: [0, 0, 0, 1], buffer: null },
       { enabled: false, type: gl.FLOAT, size: 3, normalized: false, 
         stride: 0, offset: 0, value: [0, 0, 0, 1], buffer: null },
       ...
     ],
   }
   ...

你可以认为gl.bindBuffer像这样实施了

   // Implementation of gl.bindBuffer. 
   // note this function is doing nothing but setting 2 internal variables.
   this.bindBuffer = function(bindPoint, buffer) {
     switch(bindPoint) {
       case gl.ARRAY_BUFFER;
         this.arrayBuffer = buffer;
         break;
       case gl.ELEMENT_ARRAY_BUFFER;
         this.vertexArray.elementArrayBuffer = buffer;
         break;
       default:
         this.lastError = gl.INVALID_ENUM;
         break;
     }
   };

您可以在上面看到,使用gl.bindBuffer调用gl.ELEMENT_ARRAY_BUFFER会设置当前elementArray的{​​{1}}部分

您还可以看到vertexArray有许多属性。它们定义了如何从缓冲区中提取数据以提供给顶点着色器。调用vertexArray会告诉您顶点着色器将查看哪个属性以从缓冲区中获取数据。

您可以使用4个函数来配置属性从缓冲区获取数据的方式。 gl.getAttribLocation(someProgram, "nameOfAttribute")gl.enableVertexAttribArraygl.disableVertexAttribArraygl.vertexAttribPointer

他们有效地实施了类似的东西

gl.vertexAttrib??

现在,当您调用this.enableVertexAttribArray = function(location) { const attribute = this.vertexArray.attributes[location]; attribute.enabled = true; // true means get data from attribute.buffer }; this.disableVertexAttribArray = function(location) { const attribute = this.vertexArray.attributes[location]; attribute.enabled = false; // false means get data from attribute.value }; this.vertexAttribPointer = function(location, size, type, normalized, stride, offset) { const attribute = this.vertexArray.attributes[location]; attribute.size = size; // num values to pull from buffer per vertex shader iteration attribute.type = type; // type of values to pull from buffer attribute.normalized = normalized; // whether or not to normalize attribute.stride = stride; // number of bytes to advance for each iteration of the vertex shader. 0 = compute from type, size attribute.offset = offset; // where to start in buffer. // IMPORTANT!!! Associates whatever buffer is currently *bound* to // "arrayBuffer" to this attribute attribute.buffer = this.arrayBuffer; }; this.vertexAttrib4f = function(location, x, y, z, w) { const attribute = this.vertexArray.attributes[location]; attribute.value[0] = x; attribute.value[1] = y; attribute.value[2] = z; attribute.value[3] = w; }; gl.drawArrays时,系统会知道您希望如何从您为顶点着色器提供的缓冲区中提取数据。 See here for how that works

然后有3个函数可以管理连接到gl.drawElements的所有状态。它们是this.vertexArraygl.createVertexArraygl.bindVertexArray。在WebGL1中,它们可以在OES_vertex_array_object扩展名上稍微重命名。在WebGL2上,它们仅在默认情况下可用  这也是WebGL 2.0的一个特性。

调用gl.deleteVertexArray创建新的顶点数组。调用gl.createVertexArray设置gl.bindVertexArray以指向您传递的那个。您可以想象它是这样实现的

this.vertexArray

好处应该是显而易见的。在您要绘制的每件事物之前,您需要设置所有属性。设置每个属性要求每个属性至少调用一次。更常见的是每个属性3次调用。一次调用 this.bindVertexArray = function(vao) { this.vertexArray = vao ? vao : defaultVertexArray; } 将缓冲区绑定到gl.bindBuffer,然后调用ARRAY_BUFFER,然后将该缓冲区绑定到特定属性,并设置如何将数据拉出并调用{{1}开启从缓冲区获取属性的数据。

对于具有9个调用的位置,法线和纹理坐标的典型模型,如果您正在使用索引并且需要将缓冲区绑定到gl.vertexAttribPointer,则更多。

对于顶点数组,所有这些调用都在初始化时发生。您为要绘制的每个事物创建一个顶点数组,然后设置该事物的属性。在抽奖时,只需拨打一次gl.enableVertexAttribArray即可设置所有属性和ELEMENT_ARRAY_BUFFER

如果您只想使用顶点数组,可以在WebGL1中使用this polyfill。如果扩展存在,它使用内置的或者模拟它。当然,仿真速度较慢,但​​任何需要仿真的GPU可能已经太慢了。

请注意,如果您要查找样本,可以将https://webglfundamentals.org上的相应示例与https://webgl2fundamentals.org进行比较。 WebGL2站点在任何地方都使用顶点数组。您将在绘制之前的WebGL1示例中注意到,对于每个顶点数据,绑定该数据的缓冲区,然后设置该数据的属性。在WebGL2示例中,它发生在初始时而不是绘制时。在抽奖时,所有这一切都是调用gl.bindVertexArray

关于顶点数组要注意的另一件事是它们通常需要更多的组织。如果您要使用不同的着色器程序多次绘制同一个对象,那么一个着色器程序可能会对同一数据使用不同的属性。换句话说,没有额外的组织,shaderprogram1可能会使用属性3作为位置,而shaderprogram2可能将2作为位置。在这种情况下,相同的顶点数组不适用于同一数据的两个程序。

解决方案是手动分配位置。您可以在WebGL2中的着色器本身中执行此操作。您也可以在链接WebGL1和WebGL2中每个着色器程序的着色器之前调用ELEMENT_ARRAY_BUFFER来完成此操作。我倾向于认为使用gl.bindVertexArray比使用GLSL更好,因为它更多D.R.Y.