title: WebGL: Rendering two objects with separate textures
body: <p>I am currently working on a simple project in WebGL that requires rendering multiple 2D objects with a simple image textured on. The actual project generates a random number of objects, usually between 5 and 9, sets vertex data for them around the canvas to separate them, and is supposed to render, however it will only render one at a time (usually the last but I can change around gl.activeTexture to show other objects in the array). I tried to use a <a href="https://stackoverflow.com/questions/19592850/how-to-bind-an-array-of-textures-to-a-webgl-shader-uniform">question</a> on here about texture arrays in the shader, but to no avail, so I ended up creating a very simple test program that just tries to load two objects and textures, one on the left of the canvas and the other on the right.</p>
  
  <p>From here I tried to do everything completely separate, even giving each object their own shaders, programs, buffers and everything, and subsequently binding everything in the draw call for each before calling gl.drawElements for each. This still doesn't show me the correct result, only the second texture appears, however it did lead me to discover what I believe to be happening. By commenting out the bindings and draw call for the second one, the first texture shows up, however it appears at the location of the second texture, not where it's vertices should be placing it. So, I assume what is happening in this program (and my project code) is that it is in fact drawing both, but for some reason applying the vertices of the last drawn one to all of them, thus stacking them and only showing the top (or last drawn one).</p>
  
  <p>I have also tried a mishmash of tweaks to the below code, using only one program, using the same indices, texture coordinates, there are some commented out lines from trying to make calls in different orders as well. Anything commented out doesn't mean I necessarily think it is wrong or right, just from various things I've aimlessly tried at this point.</p>
  
  <p>I have worked with OpenGL a little and had little to no trouble drawing multiple objects with their own textures, and I know that WebGL works differently than OpenGL in some ways including textures, but I do not see where I am creating the issue. I'm sure it is something very simple, and any guidance would be greatly appreciated. </p>
  
  <p>I apologize for the long block of code, it's pretty much just straight typing everything out that I believe to be needed without trying to take any shortcuts. The initShaders call is from the WebGL js files I'm using from my textbook and isn't something I've written, and the loadImage call just simply loads an <code>&lt;img&gt;</code> from the html code. There are no issues with the images being loaded correctly as far as I can tell. I only included the first vertex and fragment shader because the other two are the same save for the id. </p>
  
  <pre><code>&lt;script id="vertex-shader1" type="x-shader/x-vertex"&gt;
          attribute vec4 vPosition;
          attribute vec2 vTexCoord;
  
          varying vec2 fTexCoord;
  
          void main() {
              fTexCoord = vTexCoord;
              gl_Position = vPosition;
          }
  
      &lt;/script&gt;
  
      &lt;script id="fragment-shader1" type="x-shader/x-fragment"&gt;
          precision mediump float;
  
          varying vec2 fTexCoord;
  
          uniform sampler2D texture;
  
          void main() {
              gl_FragColor = texture2D(texture, fTexCoord);
          }
      &lt;/script&gt;
  </code></pre>
  
  <pre><code>"use-strict"
  
  var gl;
  
  var images = [];
  
  var program1;
  var program2;
  var texture1;
  var texture2;
  
  var vBuff1;
  var vBuff2;
  var iBuff1;
  var iBuff2;
  var tBuff1;
  var tBuff2;
  
  var vPos1;
  var vPos2;
  var fTexCoord1;
  var fTexCoord2;
  
  var sampler1;
  var sampler2;
  
  var vertices1 = [
      vec4(-0.8, 0.1, 0.0, 1.0),
      vec4(-0.8, 0.3, 0.0, 1.0),
      vec4(-0.6, 0.3, 0.0, 1.0),
      vec4(-0.6, 0.1, 0.0, 1.0)
  ];
  
  var vertices2 = [
      vec4(0.1, 0.1, 0.0, 1.0),
      vec4(0.1, 0.3, 0.0, 1.0),
      vec4(0.3, 0.3, 0.0, 1.0),
      vec4(0.3, 0.1, 0.0, 1.0)
  ];
  
  var indices1 = [
      0, 1, 2,
      0, 2, 3
  ];
  
  var indices2 = [
      0, 1, 2,
      0, 2, 3
  ];
  
  var tcs1 = [
      vec2(0, 0),
      vec2(0, 1),
      vec2(1, 1),
      vec2(1, 0)
  ];
  
  var tcs2 = [
      vec2(0, 0),
      vec2(0, 1),
      vec2(1, 1),
      vec2(1, 0)
  ];
  
  window.onload = function init() {
      var canvas = document.getElementById("gl-canvas");
      gl = WebGLUtils.setupWebGL(canvas);
      if (!gl) { alert("WebGL isn't available"); }
  
      gl.viewport(0, 0, canvas.width, canvas.height);
      gl.clearColor(0.0, 0.0, 0.0, 1.0);
  
      loadImages();
  
      program1 = initShaders(gl, "vertex-shader1", "fragment-shader1");
      gl.useProgram(program1);
  
      vBuff1 = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
      gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices1), gl.STATIC_DRAW);
  
      vPos1 = gl.getAttribLocation(program1, "vPosition");
      gl.vertexAttribPointer(vPos1, 4, gl.FLOAT, false, 0, 0);
      //gl.enableVertexAttribArray(vPos1);
  
      iBuff1 = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices1), gl.STATIC_DRAW);
  
      tBuff1 = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, tBuff1);
      gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs1), gl.STATIC_DRAW);
  
      fTexCoord1 = gl.getAttribLocation(program1, "vTexCoord");
      gl.vertexAttribPointer(fTexCoord1, 2, gl.FLOAT, false, 0, 0);
      //gl.enableVertexAttribArray(fTexCoord1);
  
      sampler1 = gl.getUniformLocation(program1, "texture");
  
      texture1 = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture1);
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[0]);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  
      gl.bindTexture(gl.TEXTURE_2D, null);
  
  ///////////////////////////////////////////////////////////////////////////////////////
  /*
      program2 = initShaders(gl, "vertex-shader2", "fragment-shader2");
      gl.useProgram(program2);
  */
      vBuff2 = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, vBuff2);
      gl.bufferData(gl.ARRAY_BUFFER, flatten(vertices2), gl.STATIC_DRAW);
  
      vPos2 = gl.getAttribLocation(program1, "vPosition");
      gl.vertexAttribPointer(vPos2, 4, gl.FLOAT, false, 0, 0);
      //gl.enableVertexAttribArray(vPos2);
  
      iBuff2 = gl.createBuffer();
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);
      gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint8Array(indices2), gl.STATIC_DRAW);
  
      tBuff2 = gl.createBuffer();
      gl.bindBuffer(gl.ARRAY_BUFFER, tBuff2);
      gl.bufferData(gl.ARRAY_BUFFER, flatten(tcs2), gl.STATIC_DRAW);
  
      fTexCoord2 = gl.getAttribLocation(program1, "vTexCoord");
      gl.vertexAttribPointer(fTexCoord2, 2, gl.FLOAT, false, 0, 0);
      //gl.enableVertexAttribArray(fTexCoord2);
  
      sampler2 = gl.getUniformLocation(program1, "texture");
  
      texture2 = gl.createTexture();
      gl.bindTexture(gl.TEXTURE_2D, texture2);
      gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
      gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, images[1]);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
      gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
  
      gl.bindTexture(gl.TEXTURE_2D, null);
  
      render();
  };
  
  function render() {
      gl.clear(gl.COLOR_BUFFER_BIT);
  
      gl.useProgram(program1);
  
      gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
      gl.enableVertexAttribArray(vPos1);
      gl.enableVertexAttribArray(fTexCoord1);
  
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture1);
  
      gl.uniform1i(sampler1, 0);
  
  //  gl.bindBuffer(gl.ARRAY_BUFFER, vBuff1);
  //  gl.enableVertexAttribArray(vPos1);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff1);
      gl.drawElements(gl.TRIANGLES, indices1.length, gl.UNSIGNED_BYTE, 0);
  
      //gl.bindTexture(gl.TEXTURE_2D, null);
  
  //  gl.useProgram(program2);
  
      gl.bindBuffer(gl.ARRAY_BUFFER,vBuff2);
      gl.enableVertexAttribArray(vPos2);
      gl.enableVertexAttribArray(fTexCoord2);
  
      gl.activeTexture(gl.TEXTURE0);
      gl.bindTexture(gl.TEXTURE_2D, texture2);
  
      gl.uniform1i(sampler2, 0);
  
  //  gl.bindBuffer(gl.ARRAY_BUFFER, vBuff2);
  //  gl.enableVertexAttribArray(vPos2);
      gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, iBuff2);
  
      gl.drawElements(gl.TRIANGLES, indices2.length, gl.UNSIGNED_BYTE, 0);
  
      requestAnimFrame(render);
  }
  </code></pre>