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

时间:2019-06-26 18:37:57

标签: webgl webgl2

从野外看一些不同的示例,似乎将数据上传到缓冲区(用作统一缓冲区)的过程如下:

  1. bindBuffer()
  2. bufferData()
  3. bindBuffer()-具有null,即“取消绑定”
  4. bindBufferRange()

第3步的目的是什么?

1 个答案:

答案 0 :(得分:0)

您不需要按此顺序进行。

最简单的例子:

'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。创建程序时,它将查询有关统一缓冲区的所有数据。有多少人,他们的名字是什么,他们使用哪些制服,这些制服的类型是什么。当您致电look inside并看到createUniformBlockSpecFromProgram中的信息已创建时,便会发生这种情况

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

,创建一个类型化的数组,其中包含对所有制服的该数组的预制视图。
twgl.createProgramInfo

您可以直接使用视图通过视图在typedarray中设置统一值

const ubi = twgl.createUniformBlockInfo(...)

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

ubi.uniforms.nameOfUniform.set(newValue)

当您实际上希望将typedarray中的数据上传到您调用的GPU时

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

既将统一块绑定到其分配的绑定,又将数据上传到GPU。

如果您只想绑定现有块(无需上传新数据),则

twgl.setUniformBlock(...);

虽然模式如您在示例中所见

  1. bindBufferRange
  2. bufferData

bindBufferRange已经绑定了缓冲区,因此我们可以使用该绑定来上传数据。

测试(非twgl)

twgl.bindUniformBlock(gl, programInfo, ubi);
'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>做2件事。

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

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

根据OpenGL ES 3.0规范第2.10.1.1节中关于UNIFORM_BUFFER

  

每个目标也代表缓冲对象绑定点的索引数组,   作为单个通用绑定点,可被其他缓冲区对象操作功能使用