Learn more about Russian war crimes in Ukraine.

Textures in WebGL shaders

Above, you should see a square displaying red and green in a checkerboard pattern. It is heavily blurred, because it shows a 2x2 checkerboard scaled up to 200x200 pixels. This image is created using WebGL. The 2x2 checkerboard is a tiny WebGL “texture”, created in JavaScript, then uploaded to the graphics driver. The fragment shader then accesses the texture, indexing into it with the position of the fragment. The texture is configured with its magnification filter set to gl.LINEAR, which causes the blurring.

Here is our fragment shader. The function texture2D samples from a texture. That is, it takes a texture and a coordinate, and gives you the color of the texture at that coordinate. Note its second parameter treats the texture as entirely within the unit square! We therefore map our 200x200 clip space down to the unit square.

uniform sampler2D smplr;
void main(void) {
  gl_FragColor = texture2D(smplr, vec2(gl_FragCoord.x/200.0, gl_FragCoord.y/200.0));

In JavaScript, we create the texture from a Uint8Array of RGBA pixels:

gl.activeTexture(gl.TEXTURE0);        // We only have one texture in this program
var tex = gl.createTexture();         
gl.bindTexture(gl.TEXTURE_2D, tex);   // TEXTURE0 is now `tex`
gl.texImage2D(                        // upload a checkerboard pattern to `tex`
  0,                // level
  gl.RGBA,          // internal format
  2,                // width
  2,                // height
  0,                // border
  gl.RGBA,          // format
  gl.UNSIGNED_BYTE, // type
  new Uint8Array([
    255, 0, 0, 255,
    0, 255, 0, 255,
    0, 255, 0, 255,
    255, 0, 0, 255,

If you try to use this texture, WebGL will complain that the texture is unrenderable! It’s badly documented, but you need to set some parameters for the texture:

gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
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);

Before we draw something with our shader program prog, we need to set its smplr uniform variable to refer to this uploaded texture:

const samplerLoc = gl.getUniformLocation(prog, "smplr");
gl.uniform1i(samplerLoc, 0); // Note 0, not gl.TEXTURE0

Then, when we draw stuff, the fragment shader will consult our texture, and draw its checkerboard pattern.

What can computers do? What are the limits of mathematics? And just how busy can a busy beaver be? This year, I’m writing Busy Beavers, a unique interactive book on computability theory. You and I will take a practical and modern approach to answering these questions — or at least learning why some questions are unanswerable!

It’s only $19, and you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

After months of secret toil, I and Andrew Carr released Everyday Data Science, a unique interactive online course! You’ll make the perfect glass of lemonade using Thompson sampling. You’ll lose weight with differential equations. And you might just qualify for the Olympics with a bit of statistics!

It’s $29, but you can get 50% off if you find the discount code ... Not quite. Hackers use the console!

More by Jim

Tagged . All content copyright James Fisher 2017. This post is not associated with my employer. Found an error? Edit this page.