Multiple textures in WebGL
The above image is the combination of two images: a red-green checkerboard, and an image of me. This is made with WebGL, where the fragment shader combines two textures:
precision mediump float;
uniform sampler2D checkerboardTexture;
uniform sampler2D jimTexture;
void main(void) {
vec2 texCoord = vec2(gl_FragCoord.x/200.0, gl_FragCoord.y/200.0);
gl_FragColor = (texture2D(checkerboardTexture, texCoord) + texture2D(jimTexture, texCoord)) * 0.5;
gl_FragColor.a = 1.0;
}
To use this fragment shader,
we must tell it which textures to use for each sampler2D
.
The sampler2D
is actually an int
which indexes into a “texture units” array.
We use texture units 0 and 1:
const checkerboardTextureLoc = gl.getUniformLocation(prog, "checkerboardTexture");
gl.uniform1i(checkerboardTextureLoc, 0);
const jimTextureLoc = gl.getUniformLocation(prog, "jimTexture");
gl.uniform1i(jimTextureLoc, 1);
To use these texture units 0
and 1
,
we must create new textures and bind them to these indices:
const checkerboardTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, checkerboardTexture);
const jimTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0+1);
gl.bindTexture(gl.TEXTURE_2D, jimTexture);
The objects checkerboardTexture
and jimTexture
are WebGLTexture
objects.
We created them, but they don’t have any image data associated with them yet.
For that, we use texImage2D
:
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, jimImg);
The object jimImg
is an Image
,
which is loaded from a URL:
const jimImg = new Image();
jimImg.onload = function() {
const jimTexture = gl.createTexture();
gl.activeTexture(gl.TEXTURE0+1);
gl.bindTexture(gl.TEXTURE_2D, jimTexture);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, jimImg);
gl.generateMipmap(gl.TEXTURE_2D);
gl.drawArrays(gl.TRIANGLE_FAN, 0, 4);
};
jimImg.src = '/assets/jim_512.jpg';
There are many levels of indirection here!
We have an image at /assets/jim_512.jpg
,
which is loaded into a JS Image
,
which is copied to a WebGLTexture
,
which is bound to the texture unit 1
,
the index of which is copied to the fragment shader’s sampler2D
,
which is accessed by the shader with texture2D
.
Note the oddity in these lines:
gl.activeTexture(gl.TEXTURE0+1);
What is gl.TEXTURE0
?
On my machine, it’s 33984
.
These identifiers go up to gl.TEXTURE31
,
which is 34015
.
We use these identifiers in gl.activeTexture
,
but we do not use them in the shader uniforms!
gl.activeTexture(0); // WRONG!
gl.activeTexture(gl.TEXTURE0); // right!
gl.uniform1i(checkerboardTextureLoc, gl.TEXTURE0); // WRONG!
gl.uniform1i(checkerboardTextureLoc, 0); // right!
Tagged . All content copyright James Fisher 2017. This post is not associated with my employer.