title: Bind multiple Uniform Buffer Objects
body: <p>With WebGL 2 we now can play with Uniform Buffer Objects.</p>
  
  <p>They look like a great idea, not having to attach common uniforms to every single program (like projection and view matrices that are common to every object being rendered).</p>
  
  <p>I created an helper class which I call every time I want to bind a uniform buffer object.</p>
  
  <pre><code>class UniformBuffer {
      constructor(gl, data, boundLocation = 0) {
          this.boundLocation = boundLocation;
  
          this.data = new Float32Array(data);
  
          this.buffer = gl.createBuffer();
          gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
          gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
          gl.bindBuffer(gl.UNIFORM_BUFFER, null);
          gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
      }
  
      update(gl, data, offset = 0) {
          this.data.set(data, offset);
  
          gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
          gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
          gl.bindBuffer(gl.UNIFORM_BUFFER, null);
          gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
      }
  };
  </code></pre>
  
  <p>The idea if to create the uniform buffers like this</p>
  
  <pre><code>const perScene = new UniformBuffer(gl, [
      ...vec4.create(),
      ...vec4.create(),
  ], 0); // and bind it to bind location 0?
  
  const perObject = new UniformBuffer(gl, [
      ...vec4.create(),
  ], 1); // and bind it to bind location 1?
  </code></pre>
  
  <p>In my render loop, I then update the "perScene" uniforms by calling</p>
  
  <pre><code>perScene.update(gl, [
      ...vec4.fromValues(1, 0, 0, 1),
  ], 4); // giving an offset to update only the 2nd color.
  </code></pre>
  
  <p>Then I'll look through all the objects in the scene and my idea is to update the perObject uniform buffer like this</p>
  
  <pre><code>for (let i = 0; i &lt; objects.length; i++) {
      perObject.update(gl, [
         ...vec4.fromValues(0, 0, 1, 1),
      ]);
  } 
  </code></pre>
  
  <p>I'm talking about <code>vec4</code> just to make the example easier, but the idea is to have matrices (projection and view) on the perScene, and (object and normal matrices) on the perObject.</p>
  
  <p>In my shader I have them declared as</p>
  
  <pre><code>uniform perScene {
      vec4 color1;
      vec4 color2;
  };
  
  
  uniform perModel {
      vec4 color3;
  };
  </code></pre>
  
  <p>I have a working snippet here</p>
  
  <p><div class="snippet" data-lang="js" data-hide="false" data-console="true" data-babel="false">
  <div class="snippet-code">
  <pre class="snippet-code-js lang-js prettyprint-override"><code>class UniformBuffer {
      constructor(gl, data, boundLocation = 0) {
          this.boundLocation = boundLocation;
  
          this.data = new Float32Array(data);
  
          this.buffer = gl.createBuffer();
          gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
          gl.bufferData(gl.UNIFORM_BUFFER, this.data, gl.DYNAMIC_DRAW);
          gl.bindBuffer(gl.UNIFORM_BUFFER, null);
          gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
      }
  
      update(gl, data, offset = 0) {
          this.data.set(data, offset);
  
          gl.bindBuffer(gl.UNIFORM_BUFFER, this.buffer);
          gl.bufferSubData(gl.UNIFORM_BUFFER, 0, this.data, 0, null);
          gl.bindBuffer(gl.UNIFORM_BUFFER, null);
          gl.bindBufferBase(gl.UNIFORM_BUFFER, this.boundLocation, this.buffer);
      }
  };
  
  const vertex = `#version 300 es
  
  uniform perScene {
   vec4 color1;
      vec4 color2;
  };
  
  uniform perModel {
   vec4 color3;
  };
  
  in vec3 a_position;
  out vec3 v_color;
  
  void main() {
   gl_Position = vec4(a_position, 1.0);
   v_color = color1.rgb + color2.rgb; // WORKS
      // v_color = color1.rgb + color2.rgb + color3.rgb; // DOESNT WORK
  }
  `;
  
  const fragment = `#version 300 es
  precision highp float;
  precision highp int;
  
  in vec3 v_color;
  out vec4 outColor;
  
  void main() {
   outColor = vec4(v_color, 1.0);
  }
  `;
  
  const geometry = {
      positions: [-0.5, -0.5, 0, -0.5, 0.5, 0, 0.5, -0.5, 0, 0.5, 0.5, 0],
      indices: [0, 2, 1, 1, 2, 3],
  };
  
  const renderList = [];
  
  // STEP 1 (create canvas)
  var canvas = document.getElementById("canvas");
  var gl = canvas.getContext("webgl2");
  if (!gl) {
      console.log('no webgl2 buddy');
  }
  
  // STEP 2 (create program)
  const v = gl.createShader(gl.VERTEX_SHADER);
  gl.shaderSource(v, vertex);
  gl.compileShader(v);
  
  const f = gl.createShader(gl.FRAGMENT_SHADER);
  gl.shaderSource(f, fragment);
  gl.compileShader(f);
  
  const program = gl.createProgram();
  gl.attachShader(program, v);
  gl.attachShader(program, f);
  gl.linkProgram(program);
  
  // STEP 3 (create VAO)
  const positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
  const colorUniformLocation = gl.getUniformLocation(program, 'color');
  
  const positionsBuffer = gl.createBuffer();
  const indicesBuffer = gl.createBuffer();
  
  const vao = gl.createVertexArray();
  gl.bindVertexArray(vao);
  
  // position &amp; indices
  gl.enableVertexAttribArray(positionAttributeLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, positionsBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(geometry.positions), gl.STATIC_DRAW);
  gl.vertexAttribPointer(positionAttributeLocation, 3, gl.FLOAT, false, 0, 0);
  gl.bindBuffer(gl.ARRAY_BUFFER, null);
  
  gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, indicesBuffer);
  gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(geometry.indices), gl.STATIC_DRAW);
  
  // STEP 4 (create UBO)
  
  // bound to location 0
  const perScene = new UniformBuffer(gl, [
      ...vec4.create(), // color 1
      ...vec4.create(), // color 2
  ], 0);
  
  // bound to location 1 ?
  const perModel = new UniformBuffer(gl, [
      ...vec4.create(), // color 3
  ], 3);
  
  // STEP 5 (add instances)
  for (let i = 0; i &lt; 1; i++) {
      renderList.push({
          id: i,
          vao: vao,
          program: program,
          color: [0, 1, 1],
      });
  }
  
  // STEP 6 (draw)
  gl.clearColor(0, 0, 0, 0);
  
  gl.enable(gl.DEPTH_TEST);
  
  gl.viewport(0, 0, canvas.width, canvas.height);
  
  perScene.update(gl, [
      ...vec4.fromValues(1, 0, 0, 1),
      ...vec4.fromValues(0, 1, 0, 1),
  ]);
  
  for (let i = 0; i &lt; renderList.length; i++) {
      const current = renderList[i];
      gl.useProgram(current.program);
      gl.bindVertexArray(current.vao);
  
      // update perObject
      perModel.update(gl, [
          ...vec4.fromValues(0, 0, 1, 1),
      ]);
  
      gl.drawElements(gl.TRIANGLES, geometry.indices.length, gl.UNSIGNED_SHORT, 0);
  
      // unbind
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
  }
  
  console.log('compiled!');</code></pre>
  <pre class="snippet-code-css lang-css prettyprint-override"><code>canvas {
      background-color: black;
  }</code></pre>
  <pre class="snippet-code-html lang-html prettyprint-override"><code>&lt;canvas id="canvas"&gt;&lt;/canvas&gt;
  &lt;script src="https://cdnjs.cloudflare.com/ajax/libs/gl-matrix/2.3.2/gl-matrix-min.js"&gt;&lt;/script&gt;</code></pre>
  </div>
  </div>
  </p>
  
  <p>Shouldn't I be seeing a white square since all colours added up result in a <code>vec4(1.0, 1.0, 1.0, 1.0)</code>? (jsfiddle line 41)</p>
  
  <p>What am I doing wrong?
  Thanks</p>