WebGL FAQ: Texture Pixels

How to extract pixel data from a texture for use in the javascript portion of a WebGL application.

Textures in WebGL use images as the source for their texture data. This is very useful for texturing because it lets you use any image quickly and easily as a source for a texture in a 3D scene.

Images are loaded asynchronously in the web browser. This means that you specify what image to load in your javascript and specify a function to be called once the image has finished loading.

In order to access the pixel data from a texture we need to copy the data out of the texture after the image has been loaded.

We can do this by having our onLoad function render the texture into a Framebuffer object and then extracting the pixels from the Framebuffer into an Unsigned Integer byte array.

Then we can use this array anywhere in our program we want to access the texture data. The following code implements such a texture extraction.

var Gpixels; // this global variable will hold the Unsigned 8 bit array of RGBA pixels.
// Initialize our texture data and prepare it for rendering
var exTexture;  // this texture is used by WebGL for passing pixel data to the GPU.

//this function initializes the basic texture handle with an image file.
function initTextures()
{
    exTexture = gl.createTexture();
    exTexture.image = Gimage;
    exTexture.image.onload = function() {
      handleLoadedTexture(exTexture)
    }

    exTexture.image.src = "box.png";
  }

// this function is called by the interpreter once the image data has been loaded
// it is used to finalize texture preparation for the GPU
// and is where we will extract our pixel data for use in other parts of the code.
function handleLoadedTexture(texture)
{
    // configure the texture handle for use by the GPU
    // has nothing to do with the pixel unpacking
    gl.bindTexture(gl.TEXTURE_2D, texture);
    gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
    gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture.image);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST);
    gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
    
    // alocate the array for holding the RGBA pixel data
    var width = texture.image.width;
    var height = texture.image.height;
    Gpixels = new Uint8Array(4 * width * height);
    
    // here we use a framebuffer as an offscreen render object
    // draw the texture into it and then copy the pixel values into a local array.
    var framebuffer = gl.createFramebuffer();
    gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer);
    gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, texture, 0);
    if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) == gl.FRAMEBUFFER_COMPLETE) {
         gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, Gpixels);
    }
    
    // unbind this framebuffer so its memory can be reclaimed.
    gl.bindFramebuffer(gl.FRAMEBUFFER, null);

}