首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >OpenGL & WebGL2中的顶点数组是什么?

OpenGL & WebGL2中的顶点数组是什么?
EN

Stack Overflow用户
提问于 2018-05-09 13:51:30
回答 1查看 2.8K关注 0票数 4

我使用WebGL1已经有一段时间了,但是现在我对WebGL2有了更多的了解,我搞不懂Vertex Array到底在做什么。例如,在下面的示例中,我可以删除对它们的所有引用(例如创建、绑定、删除),并且这个示例继续工作。

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2018-05-09 16:06:15

这一点在其他地方已经解释过了,但是您可以考虑WebGL1和WebGL2都有一个顶点数组。它只是默认情况下只有一个WebGL1,作为WebGL2,您可以创建多个顶点数组(尽管99.9%的WebGL1实现支持它们作为扩展)

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

你可以这样想WebGL状态

代码语言:javascript
复制
class WebGLRenderingContext {
  constructor() {
   // 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看作是这样实现的

代码语言:javascript
复制
   // 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.ELEMENT_ARRAY_BUFFER调用gl.ELEMENT_ARRAY_BUFFER设置当前vertexArrayelementArray部分

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

有4个函数用于配置属性将如何从缓冲区获取数据。gl.enableVertexAttribArraygl.disableVertexAttribArraygl.vertexAttribPointergl.vertexAttrib??

它们实际上是这样实现的

代码语言:javascript
复制
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.drawArraysgl.drawElements时,系统知道如何从为提供顶点着色器而制作的缓冲区中提取数据。请看这里的工作原理。

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

调用gl.createVertexArray生成新的顶点数组。调用gl.bindVertexArraythis.vertexArray设置为指向传入的对象。你可以想象它是这样实现的

代码语言:javascript
复制
 this.bindVertexArray = function(vao) {
   this.vertexArray = vao ? vao : defaultVertexArray;
 }    

好处应该是显而易见的。在您想要绘制的每件东西之前,您需要设置所有属性。设置每个属性要求每个使用的属性至少调用一次。更常见的是,每个属性调用3次。一次调用gl.bindBuffer将缓冲区绑定到ARRAY_BUFFER,一次调用gl.vertexAttribPointer将缓冲区绑定到特定属性并设置如何提取数据,另一次调用gl.enableVertexAttribArray打开从属性缓冲区获取数据。

对于具有位置、法线和纹理坐标的典型模型,这是9次调用,如果您使用索引并需要将缓冲区绑定到ELEMENT_ARRAY_BUFFER,则需要多调用1次。

对于顶点数组,所有这些调用都发生在init时间。为要绘制的每一件事物创建一个顶点数组,然后设置该事物的属性。在抽签时,它只需调用一次gl.bindVertexArray就可以设置所有属性和ELEMENT_ARRAY_BUFFER

如果您希望始终使用顶点数组,则可以在这种聚酰亚胺中使用WebGL1。如果存在扩展,或者模仿扩展,则使用内置的扩展。当然,仿真要慢一些,但是任何需要仿真的GPU都可能已经太慢了。

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

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

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

票数 8
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/50255115

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档