Read Time:3 Minute, 33 Second

Sometimes, when writting pixel shader, you are facing this problem of wanting redraw on top on what has been driven from the last GPU drawcall. In short, let’s assume we have a simple sphere moving on the screen, and we want to move it without removing the result of the last frame… Common approach is to use this idea of a framebuffer, where you render stuff to a texture, and then access this texture to render on the screen. In this article, I want to demonstrate how you could use the same technique by resampling the latest value, and then accumulate was has been rendered to the texture to cummulate to screen.

But in short: let’s just write a ShaderToy solution where we can draw to a texture without and load this texture to screen, and this texture will be print on top of the previous one.

As you can see from this GIF, we now have a circle being render to a texture, and we see the previous step of rendering to the final image. But the shader does not reset and get a state of what was previously drawned.

Setup and algos:

Here is what we want to do:

• Render a circle at a position(x,y)
• Change the position by a certain amount

In short, what we are doing in this simple algo is to simply move the circle position, and render it for every pixel of the current frame being rendered to screen.

I will not cover how to render a circle in a pixel shader. But let’s simly change the position of the circle and see the result.

Now that we have a moving sphere, let’s render that into a buffer, and then load that buffer from the main program.

The main image code goes as:

You can see that wee sample a texture being set as the buffferA

Let’s take a look to the BufferA code for that moving circle:

BufferA:

Note that I removed all the previous frame result and we simply have a moving sphere. We update the position of the sphere by altering the length of the UV, as showned on line 7.

Here is the code:

BufferA:
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 color = vec3(0.0f);
vec2 uv = fragCoord.xy / iResolution.xy;
vec3 previous = texture(iChannel0, uv).rgb;

float d = length(uv - vec2(iTime * 0.15));
float r = 0.03;
float c = smoothstep(r, r-0.001,d);
color = vec3(c);

color = color/*+previous*/;
fragColor = vec4(color,1.0);
}

MainImage:
void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec2 uv = fragCoord.xy / iResolution.xy;
fragColor = texture(iChannel0, uv);
}

Add the previous rendered texture to the main:

We now have a “texture” sampled into the main image function, so we can now start to handle the accumulation of the previous rendered state of this texture and shoot it onto the main image.

Remember that our main goal is to draw the circle every frame, store the value of this and then shoot append this texture to the main render. Our circle is now moving, but all the previously rendered computation are also cleared.

Remember that what we want to do is to draw the circle but not clear the main frame.

So to do this:

• Get the previous rendered texture
• Add the previous to the current.
• Then sample and render to screen

Here is the code:

void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
vec3 color = vec3(0.0f);
vec2 uv = fragCoord.xy / iResolution.xy;
vec3 previous = texture(iChannel0, uv).rgb;

float d = length(uv - vec2(iTime * 0.15));
float r = 0.03;
float c = smoothstep(r, r-0.001,d);
color = vec3(c);

color = color+previous;
fragColor = vec4(color,1.0);
}

So we now have our circle being rendered, and accumulated to our main image ðŸ˜€

Note: While not being super exciting on the final image, we actually build a nice shader behavior! We can now render stuff to a texture and reload the previous stage of the render to add it to the main image. We used the + operator from current and previous, just to make it easy to grasps the concept.

Here is the code and demo:

Happy
0 %
0 %
Excited
0 %
Sleepy
0 %
Angry
0 %
Surprise
0 %

5 Star
0%
4 Star
0%
3 Star
0%
2 Star
0%
1 Star
0%