Phil
Phil

Reputation: 50516

How to implement Shadertoy buffers in WebGL Javascript when trying to convert to JS?

I'm attempting to convert a Shadertoy to Javascript and WebGL so that it can run independently from Shadertoy. Shadertoy has the concept of a buffer, in this example it re-cycles the buffer and improves the output image. It does this on the Buf A tab.

https://www.shadertoy.com/view/MdyGDW

It does this by writing its output to buffer A which is bound to iChannel0 and then it reads from the same iChannel0 on each draw cycle. How can I implement this concept of buffers in WebGL Javascript fragment shaders, WebGL uses the GLSL language. Specifically in this case it is able to write to the buffer and then read from the same buffer on the next render cycle.

Upvotes: 11

Views: 4682

Answers (1)

Kirill Dmitrenko
Kirill Dmitrenko

Reputation: 3649

Shadertoy uses technique called rendering to texture. Suppose gl is our WebGL context. First we need to create a texture first pass will draw to:

// desired size of the texture
const W = 800, H = 600;
const textureA = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, textureA);
// allocate texture data.
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, W, H, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
// may be change texture parameters (at least magnification and
// minification filters since we won't render mip levels.

Then we create framebuffer object so we can draw to our texture:

const framebufferA = gl.createFramebuffer();
gl.bindFramebuffer(gl.FRAMEBUFFER, framebufferA);
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textureA, 0);

Now we can draw:

gl.bindBuffer(gl.FRAMEBUFFER, framebufferA);
gl.viewport(0, 0, W, H);

// draw first pass, the one which supposed to write data for the channel 0
// it'll use fragment shader for bufferA

gl.bindFramebuffer(gl.FRAMEBUFFER, null);
gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);

// pass textureA as channel 0
gl.activeTexture(gl.TEXTURE0);
gl.bindTexture(gl.TEXTURE_2D, textureA);
gl.uniform1i(channel0Uniform, 0);

// draw second pass, the one with uses channel 0

There're a lot of materials about rendering to texture, for example here or here.

Upvotes: 14

Related Questions