Зачем отменять привязку gl.UNIFORM_BUFFER перед вызовом bindBufferRange ()? - PullRequest
0 голосов
/ 26 июня 2019

Из нескольких примеров в дикой природе видно, что загрузка данных в буфер для использования в качестве единого буфера выполняется в следующей последовательности:

  1. bindBuffer ()
  2. bufferData ()
  3. bindBuffer () - со значением NULL, то есть «unbinding»
  4. bindBufferRange ()

Какова цель шага 3?

1 Ответ

0 голосов
/ 27 июня 2019

Вам не нужно делать это в таком порядке.

Простейший пример:

'use strict';

const vs = `#version 300 es
void main() {
  gl_PointSize = 128.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision mediump float;

uniform Color {
  vec4 u_color;
};

out vec4 outColor;

void main() {
  outColor = u_color;
}
`;

const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need webgl2');
const program = twgl.createProgram(gl, [vs, fs]);

const color = new Float32Array([1, 0.5, 0.7, 1]);
const buffer = gl.createBuffer();

// there's only 1 so I believe it's safe to guess index 0
const uniformBlockIndex = 0;
const uniformBlockBinding = 0;
gl.uniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);


// at render time
gl.useProgram(program);

// for each block
{
  const uniformBlockBufferOffset = 0;
  const uniformBlockBufferOffsetByteLength = 16;  // 4 floats
  gl.bindBufferRange(gl.UNIFORM_BUFFER, uniformBlockBinding, buffer, uniformBlockBufferOffset, uniformBlockBufferOffsetByteLength);

  // set the data
  gl.bufferData(gl.UNIFORM_BUFFER, color, gl.DYNAMIC_DRAW);
}


gl.drawArrays(gl.POINTS, 0, 1);
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

Если вы хотите увидеть сложный пример, вы можете просмотреть в этом примере . Он запрашивает все данные о единообразных буферах при создании программы. Сколько их, как их зовут, какую форму они используют, какие типы этих форм. Это происходит, когда вы звоните twgl.createProgramInfo, который вы можете заглянуть внутрь и увидеть, что информация создается в createUniformBlockSpecFromProgram

Затем, используя спецификацию блока, вы можете создать типизированный массив с предварительно подготовленными представлениями в этом массиве для всех униформ, вызвав twgl.createUniformBlockInfo

const ubi = twgl.createUniformBlockInfo(...)

Вы можете установить единообразные значения в типизированном массиве через представления напрямую, используя

ubi.uniforms.nameOfUniform.set(newValue)

но это было бы хрупко, так как блоки могут быть оптимизированы во время отладки, поэтому вы можете использовать менее хрупкие

twgl.setBlockUniforms(ubi, {nameOfUniform: newValue});

Когда вы действительно хотите, чтобы данные в набранном массиве загружались в графический процессор, который вы называете

twgl.setUniformBlock(...);

Который связывает унифицированный блок с назначенной привязкой И загружает данные в графический процессор.

Если вы просто хотите связать существующий блок (нет необходимости загружать новые данные), тогда

twgl.bindUniformBlock(gl, programInfo, ubi);

Хотя образец такой, как вы видите в примере

  1. bindBufferRange
  2. bufferData

bindBufferRange уже связывает буфер, поэтому мы можем просто использовать эту привязку для загрузки данных.

Тест (без twgl)

'use strict';

const vs = `#version 300 es
void main() {
  gl_PointSize = 128.0;
  gl_Position = vec4(0, 0, 0, 1);
}
`;
const fs = `#version 300 es
precision mediump float;

uniform Color1 {
  vec4 u_color1;
};
uniform Color2 {
  vec4 u_color2;
};

out vec4 outColor;

void main() {
  outColor = u_color1 + u_color2;
}
`;

const gl = document.querySelector('canvas').getContext('webgl2');
if (!gl) alert('need webgl2');
const program = twgl.createProgram(gl, [vs, fs]);

const color1 = new Float32Array([1, 0, 0, 1]);
const buffer1 = gl.createBuffer();
const color2 = new Float32Array([0, 0, 1, 1]);
const buffer2 = gl.createBuffer();

// there's only 2 and they are the same format so we don't really
// care which is which to see the results.
const uniformBlockIndex = 0;
const uniformBlockBinding = 0;
gl.uniformBlockBinding(program, uniformBlockIndex, uniformBlockBinding);
gl.uniformBlockBinding(program, uniformBlockIndex + 1, uniformBlockBinding + 1);


// at render time
gl.useProgram(program);

{
  const uniformBlockBufferOffset = 0;
  const uniformBlockBufferOffsetByteLength = 16;  // 4 floats
  gl.bindBufferRange(gl.UNIFORM_BUFFER, uniformBlockBinding, buffer1, uniformBlockBufferOffset, uniformBlockBufferOffsetByteLength);

  // set the data
  gl.bufferData(gl.UNIFORM_BUFFER, color1, gl.DYNAMIC_DRAW);
  
  
  gl.bindBufferRange(gl.UNIFORM_BUFFER, uniformBlockBinding + 1, buffer2, uniformBlockBufferOffset, uniformBlockBufferOffsetByteLength);

  // set the data
  gl.bufferData(gl.UNIFORM_BUFFER, color2, gl.DYNAMIC_DRAW);
}


gl.drawArrays(gl.POINTS, 0, 1);
<canvas></canvas>
<script src="https://twgljs.org/dist/4.x/twgl-full.min.js"></script>

В приведенном выше примере показано, что bindBufferRange делает 2 вещи.

  1. связывает буфер с UNIFORM_BUFFER точкой привязки
  2. связывает часть буфера с единым индексом буфера.

Мы знаем, что это сработало, потому что результат фиолетовый. Если бы он не работал, он был бы красным или синим

Из раздела 2.10.1.1 спецификации OpenGL ES 3.0 относительно bindBufferRange

Каждая цель представляет индексированный массив точек привязки буферного объекта, в качестве единой общей точки привязки, которая может использоваться другими функциями манипулирования буферными объектами

...