javascriptwebgl

I make a data texture, very basic, but it does not display correctly


I think it must be an obvious bug in my code, but I can't find it. So I create a 4x2 texture and fill it with a green and red pixel grid.

the expected result is this image, a grid of 4x2 pixels alternating red and green:

enter image description here

But, no, here is the result (which you can observe by running the code below).

enter image description here

There must be a small bug, something obvious, but I don't see what...

"use strict";

let canvas = document.getElementById("canvas");
let gl = canvas.getContext("webgl");

let width = 4;
let height = 2;

gl.canvas.width = width;
gl.canvas.height = height;

gl.viewport(0, 0, width, height);


let vertex = `
      attribute vec2 a_position;
      attribute vec2 a_texCoord;

      varying vec2 v_texCoord;

      void main() {

        gl_Position = vec4(a_position, 0, 1);
        v_texCoord = a_texCoord;
      }
    `;

let fragment = `
      precision mediump float;

      uniform sampler2D u_image;

      varying vec2 v_texCoord;

      void main() {
        gl_FragColor = texture2D(u_image, v_texCoord);
      }
    `;

let shader = gl.createProgram();
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(vertexShader, vertex);
gl.shaderSource(fragmentShader, fragment);
gl.compileShader(vertexShader);
gl.compileShader(fragmentShader);
gl.attachShader(shader, vertexShader);
gl.attachShader(shader, fragmentShader);
gl.linkProgram(shader);

let positionLocation = gl.getAttribLocation(shader,"a_position");
let texcoordLocation = gl.getAttribLocation(shader,"a_texCoord");

let positionBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
  -1,-1,
  -1,+1,
  +1,-1,
  +1,-1,
  -1,+1,
  +1,+1
 ]),gl.STATIC_DRAW);
 
let texcoordBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
    0.0, 0.0,
    1.0, 0.0,
    0.0, 1.0,
    0.0, 1.0,
    1.0, 0.0,
    1.0, 1.0,
]), gl.STATIC_DRAW);


let   texture= gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, texture);
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.NEAREST);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);

let r=[255,0,0,255];
let g=[0,255,0,255];
gl.texImage2D(
     gl.TEXTURE_2D,
     0,        // mip level
     gl.RGBA,  // internal format
     4,        // width
     2,        // height
     0,        // border
     gl.RGBA,  // data format
     gl.UNSIGNED_BYTE,   // data type
     new Uint8Array([    // data
        ...r, ...g, ...r, ...g,
        ...g, ...r, ...g, ...r,
     ]));

gl.useProgram(shader);

gl.enableVertexAttribArray(positionLocation);
gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  let size = 2;
  let type = gl.FLOAT;
  let normalize = false;
  let stride = 0;
  let offset = 0;
  gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);

  gl.enableVertexAttribArray(texcoordLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
  size = 2;
  type = gl.FLOAT;
  normalize = false;
  stride = 0;
  offset = 0;
  gl.vertexAttribPointer(texcoordLocation, size, type, normalize, stride, offset);

gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, texture);
let utexture = gl.getUniformLocation(shader, 'u_image');

gl.uniform1i(utexture, 0);
gl.drawArrays(gl.TRIANGLES, 0, 6);
canvas {
  width: 160px;
  height: 80px;
  image-rendering: pixelated;
}
<canvas id="canvas"></canvas>


Solution

  • The texture coordinates are wrong. Change them to:

    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0.0, 1.0,
        0.0, 0.0,
        1.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,
    ]), gl.STATIC_DRAW);
    

    "use strict";
    
    let canvas = document.getElementById("canvas");
    let gl = canvas.getContext("webgl");
    
    let width = 4;
    let height = 2;
    
    gl.canvas.width = width;
    gl.canvas.height = height;
    
    gl.viewport(0, 0, width, height);
    
    
    let vertex = `
          attribute vec2 a_position;
          attribute vec2 a_texCoord;
    
          varying vec2 v_texCoord;
    
          void main() {
    
            gl_Position = vec4(a_position, 0, 1);
            v_texCoord = a_texCoord;
          }
        `;
    
    let fragment = `
          precision mediump float;
    
          uniform sampler2D u_image;
    
          varying vec2 v_texCoord;
    
          void main() {
            gl_FragColor = texture2D(u_image, v_texCoord);
          }
        `;
    
    let shader = gl.createProgram();
    const vertexShader = gl.createShader(gl.VERTEX_SHADER);
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
    gl.shaderSource(vertexShader, vertex);
    gl.shaderSource(fragmentShader, fragment);
    gl.compileShader(vertexShader);
    gl.compileShader(fragmentShader);
    gl.attachShader(shader, vertexShader);
    gl.attachShader(shader, fragmentShader);
    gl.linkProgram(shader);
    
    let positionLocation = gl.getAttribLocation(shader,"a_position");
    let texcoordLocation = gl.getAttribLocation(shader,"a_texCoord");
    
    let positionBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
      -1,-1,
      -1,+1,
      +1,-1,
      +1,-1,
      -1,+1,
      +1,+1
     ]),gl.STATIC_DRAW);
     
    let texcoordBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([
        0.0, 1.0,
        0.0, 0.0,
        1.0, 1.0,
        1.0, 1.0,
        0.0, 0.0,
        1.0, 0.0,
    ]), gl.STATIC_DRAW);
    
    
    let   texture= gl.createTexture();
    gl.bindTexture(gl.TEXTURE_2D, texture);
    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.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    
    let r=[255,0,0,255];
    let g=[0,255,0,255];
    gl.texImage2D(
         gl.TEXTURE_2D,
         0,        // mip level
         gl.RGBA,  // internal format
         4,        // width
         2,        // height
         0,        // border
         gl.RGBA,  // data format
         gl.UNSIGNED_BYTE,   // data type
         new Uint8Array([    // data
            ...r, ...g, ...r, ...g,
            ...g, ...r, ...g, ...r,
         ]));
    
    gl.useProgram(shader);
    
    gl.enableVertexAttribArray(positionLocation);
    gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
      let size = 2;
      let type = gl.FLOAT;
      let normalize = false;
      let stride = 0;
      let offset = 0;
      gl.vertexAttribPointer(positionLocation, size, type, normalize, stride, offset);
    
      gl.enableVertexAttribArray(texcoordLocation);
      gl.bindBuffer(gl.ARRAY_BUFFER, texcoordBuffer);
      size = 2;
      type = gl.FLOAT;
      normalize = false;
      stride = 0;
      offset = 0;
      gl.vertexAttribPointer(texcoordLocation, size, type, normalize, stride, offset);
    
    gl.activeTexture(gl.TEXTURE0);
    gl.bindTexture(gl.TEXTURE_2D, texture);
    let utexture = gl.getUniformLocation(shader, 'u_image');
    
    gl.uniform1i(utexture, 0);
    gl.drawArrays(gl.TRIANGLES, 0, 6);
    canvas {
      width: 160px;
      height: 80px;
      image-rendering: pixelated;
    }
    <canvas id="canvas"></canvas>