fitzmode
fitzmode

Reputation: 1066

HTML 2d canvas as texture on webgl canvas

I'm trying to add a canvas with context 2d as a texture to a webgl canvas based on this example here. The texture appears to be blank on the webgl canvas but the image is loaded when I append the texture canvas to theDOM on it's own. What may I be missing here?

const canvas = document.createElement("canvas");
    const textureCanvas = document.createElement('canvas');
    textureCanvas.width = 200
    textureCanvas.height = 200
    const ctx = textureCanvas.getContext('2d')
    const img = new Image();
    img.src = "https://i.imgur.com/uIEexIc.jpg";
    img.crossOrigin = 'Anonymous'

    img.onload = (i) => {
      
      ctx.drawImage(img, 0,0, 200, 200)
    }

   
    const gl = canvas.getContext("webgl");
    const canvasTexture = gl.createTexture();
    
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);

    gl.bindTexture(gl.TEXTURE_2D, canvasTexture);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, textureCanvas); 
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_NEAREST);
    gl.generateMipmap(gl.TEXTURE_2D);

    gl.bindTexture(gl.TEXTURE_2D, null);

Upvotes: 0

Views: 2470

Answers (2)

Anton
Anton

Reputation: 2703

I've tried to investigate the problem.

It seems, that forcing the texture canvas to have dimensions as power of 2 fixes the texture visibility.

You can try to replace this block in your code:

img.onload = (i) => {
    ctx.drawImage(img, 0,0, 200, 200)
}

to this block:

img.onload = (ev) => {
  const w = getPowerOfTwo(img.width);
  const h = getPowerOfTwo(img.height);
  textureCanvas.width = w
  textureCanvas.height = h
  ctx.drawImage(img, 0, 0, w, h);
  handleLoadedTexture(textureCanvas);
}

Here I force textureCanvas to have width and height to be power or 2. The function is here:

function getPowerOfTwo(value, pow) {
  var pow = pow || 1;
  while (pow < value) {
    pow *= 2;
  }
  return pow;
}

Also notice the code handleLoadedTexture(textureCanvas);. Here I redraw (update) the texture with new loaded image.

All other code you can test here: jsfiddle. To be sure, that texture is served from canvas, I've added 'Hello World' text above the image (see handleLoadedTexture).

Upvotes: 0

user128511
user128511

Reputation:

A canvas can not be used as a "live" texture.

In other words, when your code calls gl.texImage2D(....., textureCanvas) what happens is the contents of the canvas at that moment in time is copied to the texture just once.

In your code above the flow is this

  1. create a canvas
  2. create an image
  3. set the src and setup an onload function
  4. create a webgl texture
  5. copy the blank canvas to the texture via texImage2D
  6. sometime later the image finishes loading and the onload function is called
  7. the image is drawn into the canvas.

You need to wait for the image to be drawn into the canvas before you call gl.texImage2D

Upvotes: 2

Related Questions