为什么在调用 bindBufferRange() 之前取消绑定 gl.UNIFORM_BUFFER?

Why unbind gl.UNIFORM_BUFFER before calling bindBufferRange()?

通过查看一些实际的不同示例,似乎将数据上传到缓冲区以用作统一缓冲区会执行以下顺序:

  1. bindBuffer()
  2. 缓冲区数据()
  3. bindBuffer() - 为 null,即 "unbinding"
  4. bindBufferRange()

第 3 步的目的是什么?

你不需要按那个顺序做。

最简单的例子:

'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>

如果你想看一个复杂的例子,你可以深入研究 this example. It queries all the data about uniform buffers when the program is created. How many there are, what their names are, which uniforms they use, what the types of those uniforms are. This happens when you call twgl.createProgramInfo which you can look inside and see that info is created in createUniformBlockSpecFromProgram

然后,稍后,使用块规范,您可以通过调用 twgl.createUniformBlockInfo

为所有制服创建一个带有预制视图的类型数组
const ubi = twgl.createUniformBlockInfo(...)

您可以直接使用

通过视图在类型数组中设置统一值
ubi.uniforms.nameOfUniform.set(newValue)

但这会很脆弱,因为在调试时块可能会被优化掉,所以您可以使用不那么脆弱的

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

当您真正希望将类型数组中的数据上传到您调用的 GPU 时

twgl.setUniformBlock(...);

它将统一块绑定到其分配的绑定并将数据上传到 GPU。

如果你只是想绑定一个现有的块(不需要上传新数据)那么

twgl.bindUniformBlock(gl, programInfo, ubi);

模式如您在示例中看到的那样

  1. bindBufferRange
  2. 缓冲区数据

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 做了两件事。

  1. 它将缓冲区绑定到 UNIFORM_BUFFER 绑定点
  2. 它将缓冲区的一部分绑定到统一缓冲区索引。

我们知道它有效,因为结果是紫色的。如果它不起作用,它将是红色或蓝色

来自与 bindBufferRange

相关的 OpenGL ES 3.0 规范第 2.10.1.1 节

Each target represents an indexed array of buffer object binding points, as well as a single general binding point that can be used by other buffer object manipulation functions