WebGL - wait for texture to load
Asked Answered
G

3

16

How do I test if the WebGLTexture object is 'complete' ?

Currently I get this message: [WebGLRenderingContext]RENDER WARNING: texture bound to texture unit 0 is not renderable. It maybe non-power-of-2 and have incompatible texture filtering or is not 'texture complete'

I get this warning because the render-loop is trying to use the texture before its image has finished loading, so how to fix that?

Grade answered 1/11, 2013 at 7:3 Comment(1)
Thanks for asking this, I've been living with these console messages in my experiments for a while. :-)Paramecium
E
27

The easiest way to fix that is to make a 1x1 texture at creation time.

var tex = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, tex);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, 1, 1, 0, gl.RGBA, gl.UNSIGNED_BYTE,
              new Uint8Array([255, 0, 0, 255])); // red

Then when the image loads you can replace the 1x1 pixel texture with the image. No flags needed and your scene will render with the color of your choice until the image has loaded.

var img = new Image();
img.src = "http://someplace/someimage.jpg";
img.onload = function() {
   gl.bindTexture(gl.TEXTURE_2D, tex);
   gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img);

   // then either generate mips if the image uses power-of-2 dimensions or 
   // set the filtering correctly for non-power-of-2 images.
   setupTextureFilteringAndMips(img.width, img.height);
}

Just for the sake of saving people the trouble of running into the next problem they are most likely going to run into, WebGL requires mips or it requires filtering that doesn't require mips. On top of that it requires textures with dimensions that are a power of 2 (ie, 1, 2, 4, 8, ..., 256, 512, etc) to use mips. So, when loading an image you'll most likely want to setup the filtering to handle this correctly.

function isPowerOf2(value) {
  return (value & (value - 1)) == 0;
};

function setupTextureFilteringAndMips(width, height) {
  if (isPowerOf2(width) && isPowerOf2(height) {
    // the dimensions are power of 2 so generate mips and turn on 
    // tri-linear filtering.
    gl.generateMipmap(gl.TEXTURE_2D);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR_MIPMAP_LINEAR);
  } else {
    // at least one of the dimensions is not a power of 2 so set the filtering
    // so WebGL will render it.
    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);
  }
}
Edwin answered 3/11, 2013 at 1:29 Comment(3)
This is a very nice trick, get rid of warnings without adding a test/boolean. Question remains that what happens if for some reason the texture is not considered 'complete' for some reason. Is returning from texImage2D sufficient condition to have a 'complete' texture?Grade
My bad. The value of the 1 pixel one pixel texture should be [255, 0, 0, 255] for red. Fixed it above. As for texture complete I'll update the answerEdwin
What a feat! That's stopped days of searching... But - I wanted to use transparent black ([0,0,0,0]), which is not working, probably because I am still getting "WebGL: INVALID_ENUM: texImage2D: invalid texture type" in Chrome 31.0. I'm sure I passed gl.TEXTURE_2D - still looking for the causeTennies
M
1

To fix that issue, use some boolean value to tell if the image has loaded.

var loaded = false,
    texture,
    img = new Image();

img.onload = function() {
    texture = gl.createTexture();
    // . . . 
    loaded = true;
};
img.src = "path/myimage.jpg";

// render-loop
function render() {
    if(loaded) {
        // use texture
    }
    else {
        // not loaded yet
    }
}
Meave answered 1/11, 2013 at 20:43 Comment(1)
Managing my own boolean would just tell me if the set of APIs, such as texImage2D, have been called. But it won't tell if it succeeded in creating a 'complete' texture? Tracking the booleans separately can be a pain, so instead I modified this to store the boolean inside the WebGL texture object -> texture.isLoaded=false;Grade
D
0

I had this issue when attempting to deploy my HTML5/JS app to an Android phone using Cordova.

At first, I thought my issue was that my spritesheet/texture atlas was too large for the mobile GPU to load. So I batch shrank all the images using ImageMagick's mogrify (mogrify -resize 256x256 *.png), but was still having issues. This step was still necessary tho (as my 8000x8000 .png was too much for phones).

Then I used console.log(navigator.userAgent) to check my browser version and saw that the Chromium used was older than my browser. So I re-installed the Crosswalk plugin and everything is rendering fine.

cordova plugin rm cordova-plugin-crosswalk-webview
cordova plugin add cordova-plugin-crosswalk-webview
Datcha answered 11/9, 2016 at 15:50 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.