I am working with image processing in WebGL and have come across a problem I can't seem to solve. I haven't worked much with GL and I have a feeling I am misunderstanding something fundamental and simple.
My goal is to simulate layers. Initially I setup each layer using different contexts and then compiled each into a final rendered canvas but creating a texture from the canvas each render was slow. Next I used a Uint8Array
for each layer which contains the image data and that was much faster but lead to the problem I have now.
The problem is when I add more than 8 layers. If I have 8 layers and add another the first layer seems to be emptied - the layer becomes blank. I tried changing from Uint8Array
to using a texture for each layer but the problem still persists.
I can provide some code snippets if needed but there is a lot of code across multiple files and it's a bit of a mess as I've been trying to solve this. The layers are compiled using two framebuffers to draw back and forth to and a shader handles the math.
Eight layers, rendering properly:
Edit: Here is some code and I forgot to mention I am using the helper library twgl.
First I setup the layer texture:
this.renderTexture = twgl.createTexture(gl, {
width: this.width,
height: this.height,
target: gl.TEXTURE_2D,
min: gl.NEAREST,
max: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE
});
I draw something (like an image) to framebuffer and then update the appropriate layer's texture:
gl.readPixels(0, 0, this.width, this.height, gl.RGBA, gl.UNSIGNED_BYTE, this.buf8);
twgl.setTextureFromArray(gl, this.renderTexture, this.buf8, {
width: this.width,
height: this.height
});
And last I render each layer into one:
// create two buffers to ping pong
var blendedFramebufferInfo = [
twgl.createFramebufferInfo(gl, [{
src: $this.layers.length === 1 ? null : bottomLayer.buf8,
width: gl.canvas.width,
height: gl.canvas.height,
target: gl.TEXTURE_2D,
format: gl.RGBA,
min: gl.NEAREST,
max: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE
}], gl.canvas.width, gl.canvas.height),
twgl.createFramebufferInfo(gl, [{
width: gl.canvas.width,
height: gl.canvas.height,
target: gl.TEXTURE_2D,
format: gl.RGBA,
min: gl.NEAREST,
max: gl.NEAREST,
wrap: gl.CLAMP_TO_EDGE
}], gl.canvas.width, gl.canvas.height)
];
var layers = [...],
activeBuffer = 1;
while (layer = layers.shift()) {
// bind to either buffer or canvas
twgl.bindFramebufferInfo(gl, layers.length ? blendedFramebufferInfo[activeBuffer] : null);
// set proper program
var programInfo = $this.setBlendProgram(layer.blendMode());
// set uniforms
twgl.setUniforms(programInfo, {
u_dst: blendedFramebufferInfo[activeBuffer ? 0 : 1].attachments[0],
u_src: layer.renderTexture
});
// draw
gl.drawArrays(gl.TRIANGLES, 0, 6);
// draw to other texture next time around
activeBuffer = activeBuffer ? 0 : 1;
}