Qual è la logica dei buffer di binding in webgl?

A volte mi trovo a dover lottare tra la dichiarazione dei buffer (con createBuffer / bindBuffer / bufferdata) in ordine diverso e il rebinding in altre parti del codice, solitamente nel ciclo di disegno.

Se non faccio il rebind del buffer dei vertici prima di disegnare gli array, la console si lamenta di un tentativo di accedere ai vertici fuori intervallo. Il mio sospetto è che l’ultimo object associato viene passato al puntatore e quindi ai drawarray, ma quando cambio l’ordine all’inizio del codice, nulla cambia. Ciò che funziona efficacemente è la riconnessione del buffer nel ciclo di disegno. Quindi, non riesco davvero a capire la logica dietro a ciò. Quando hai bisogno di ricollegarti? Perché hai bisogno di ricollegarti? A cosa si riferisce attributo0?

Non so se questo aiuterà. Come alcune persone hanno detto, GL / WebGL ha un sacco di stato interno. Tutte le funzioni che chiami impostano lo stato. Quando è tutto configurato si chiama drawArrays o drawElements e tutto ciò viene utilizzato per disegnare le cose

Questo è stato spiegato altrove su SO, ma il binding di un buffer è solo l’impostazione di 1 delle 2 variabili globali all’interno di WebGL. Dopodiché ti riferisci al buffer dal suo punto di collegamento.

Puoi pensare in questo modo

 gl = function() { // internal WebGL state let lastError; let arrayBuffer = null; let 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 }, ... ], } ... // 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; arrayBuffer = buffer; break; case gl.ELEMENT_ARRAY_BUFFER; vertexArray.elementArrayBuffer = buffer; break; default: lastError = gl.INVALID_ENUM; break; } }; ... }(); 

Dopo che le altre funzioni WebGL fanno riferimento a quelle. Ad esempio gl.bufferData potrebbe fare qualcosa di simile

  // implementation of gl.bufferData // Notice you don't pass in a buffer. You pass in a bindPoint. // The function gets the buffer one of its internal variable you set by // previously calling gl.bindBuffer this.bufferData = function(bindPoint, data, usage) { // lookup the buffer from the bindPoint var buffer; switch (bindPoint) { case gl.ARRAY_BUFFER; buffer = arrayBuffer; break; case gl.ELEMENT_ARRAY_BUFFER; buffer = vertexArray.elemenArrayBuffer; break; default: lastError = gl.INVALID_ENUM; break; } // copy data into buffer buffer.copyData(data); // just making this up buffer.setUsage(usage); // just making this up }; 

Separato da quei bindpoint c’è il numero di attributi. Gli attributi sono anche stati globali per impostazione predefinita. Definiscono come estrarre i dati dai buffer per fornire al vertex shader. Chiamando gl.getAttribLocation(someProgram, "nameOfAttribute") ti dice quale attributo verrà gl.getAttribLocation(someProgram, "nameOfAttribute") dal vertex shader per estrarre i dati da un buffer.

Quindi, ci sono 4 funzioni che usi per configurare come un attributo otterrà i dati da un buffer. gl.enableVertexAttribArray , gl.disableVertexAttribArray , gl.vertexAttribPointer e gl.vertexAttrib?? .

Sono implementati in modo efficace qualcosa come questo

 this.enableVertexAttribArray = function(location) { const attribute = vertexArray.attributes[location]; attribute.enabled = true; // true means get data from attribute.buffer }; this.disableVertexAttribArray = function(location) { const attribute = vertexArray.attributes[location]; attribute.enabled = false; // false means get data from attribute.value }; this.vertexAttribPointer = function(location, size, type, normalized, stride, offset) { const attribute = 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 = arrayBuffer; }; this.vertexAttrib4f = function(location, x, y, z, w) { const attribute = vertexArray.attributes[location]; attribute.value[0] = x; attribute.value[1] = y; attribute.value[2] = z; attribute.value[3] = w; }; 

Ora, quando chiamate gl.drawArrays o gl.drawElements il sistema sa come si desidera estrarre i dati dai buffer creati per fornire il vertex shader. Vedi qui per come funziona .

Poiché gli attributi sono stati globali, ciò significa che ogni volta che chiami drawElements o drawArrays la configurazione degli attributi è la modalità con cui verranno utilizzati. Se imposti gli attributi # 1 e # 2 sui buffer che hanno ciascuno 3 vertici ma chiedi di disegnare 6 vertici con gl.drawArrays otterrai un errore. Allo stesso modo se si crea un indice buffer che si associa al gl.ELEMENT_ARRAY_BUFFER bindpoint e quel buffer ha un indice che è> 2, si otterrà quell’indice index out of range errore index out of range . Se i tuoi buffer hanno solo 3 vertici, gli unici indici validi sono 0 , 1 e 2 .

Normalmente, ogni volta che disegni qualcosa di diverso, ricollega tutti gli attributi necessari per disegnare quella cosa. Disegnare un cubo che ha posizioni e normali? Associare il buffer con i dati di posizione, impostare l’attributo utilizzato per le posizioni, associare il buffer con i dati normali, impostare l’attributo utilizzato per le normali, ora disegnare. Successivamente si disegna una sfera con posizioni, colors vertici e coordinate della trama. Associare il buffer che contiene i dati di posizione, impostare l’attributo utilizzato per le posizioni. Associare il buffer che contiene i dati dei colors dei vertici, impostare l’attributo utilizzato per i colors dei vertici. Associare il buffer che contiene le coordinate della trama, impostare l’attributo utilizzato per le coordinate della trama.

L’unica volta in cui non ricolleghi i buffer è se disegni la stessa cosa più di una volta. Ad esempio disegnando 10 cubi. Dovresti ricolbind i buffer, quindi impostare le uniformi per un cubo, disegnarlo, impostare le uniformi per il cubo successivo, disegnarlo, ripetere.

Dovrei anche aggiungere che c’è un’estensione [ OES_vertex_array_object ] che è anche una funzionalità di WebGL 2.0. Un object array Vertex è lo stato globale sopra denominato vertexArray che include l’ elementArrayBuffer e tutti gli attributi.

Chiamare gl.createVertexArray crea uno nuovo. La chiamata a gl.bindVertexArray imposta gli attributes globali in modo che puntino a quello nel verticeArray associato.

Chiamare gl.bindVertexArray sarebbe quindi

  this.bindVertexArray = function(vao) { vertexArray = vao ? vao : defaultVertexArray; } 

Questo ha il vantaggio di consentire la configurazione di tutti gli attributi e buffer in fase di avvio e quindi al momento del disegno solo 1 chiamata WebGL imposterà tutti i buffer e gli attributi.