From looking at some different examples in the wild, it seems that uploading data to a buffer, for use as a uniform buffer, does the following sequence:
What is the purpose of step 3?
You don’t need to do it in that order.
Simplest example:
'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>
If you’d like to see a complex example you can dig through 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
Then later, using the block spec, you can create a typedarray with premade views into that array for all the uniforms by calling twgl.createUniformBlockInfo
const ubi = twgl.createUniformBlockInfo(...)
You could set the uniform values in the typedarray through the views directly using
ubi.uniforms.nameOfUniform.set(newValue)
but that would be brittle since blocks may get optimized out while debugging so instead you can use the less brittle
twgl.setBlockUniforms(ubi, {nameOfUniform: newValue});
When you actually want the data in the typedarray to get uploaded to the GPU you call
twgl.setUniformBlock(...);
Which both binds the uniform block to its assigned binding AND uploads the data to the GPU.
If you just want to bind an existing block (no need to upload new data) then
twgl.bindUniformBlock(gl, programInfo, ubi);
The pattern though is as you see in the example
bindBufferRange already binds the buffer so we can just use that binding to upload the data.
Test (non 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>
The example above shows bindBufferRange
does 2 things.
UNIFORM_BUFFER
bind pointWe know it worked because the result is purple. If it didn’t work it would either be red or blue
From the OpenGL ES 3.0 spec section 2.10.1.1 in relation to bindBufferRange
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