My shader runs only once, is there a way to make it run multiple times?

Godot Version

4.2

Question

I wanted to use shaders to compute Conway’s Game of Life, and I implemented a shader (not a compute shader). However, the shader only applied once, and then it just stopped. :frowning_with_open_mouth:
Is there a way to make it run based on the output image of the shader? I have already checked out the official compute shader tutorial, but there seems to be lacking examples about dealing with images. Thanks for your help in advance.

shader_type canvas_item;

void vertex() {
	// Called for every vertex the material is visible on.
}

int cell(ivec2 pos, sampler2D t) {
	ivec2 s = textureSize(t, 0);
	pos = (pos + s) % s;
	return (texelFetch(t, pos, 0).r > 0.5) ? 1 : 0;
}

void fragment() {
	// Called for every pixel the material is visible on.
	ivec2 scaledUV = ivec2(UV * vec2(textureSize(TEXTURE, 0)));
	int live = 0;
	
	for(int dx = -1; dx <= 1; dx++) {
		for(int dy = -1; dy <= 1; dy++) {
			live += cell(scaledUV + ivec2(dx, dy), TEXTURE);
		}
	}
	
	COLOR = vec4(vec3(float(live)), 1.0);
}

I’m not sure what the best way is, but I got it working like this.

  • Make a SubViewport node and set its clear mode to never.
  • Make a ColorRect node or something that fills the SubViewport.
  • Apply a shader, that reads the screen texture, to the ColorRect. (uniform sampler2D screen : hint_screen_texture;)
  • Display the contents of the SubViewport by using a SubViewportContainer or ViewportTexture.

Can you share the project or give me screenshots? I’m quite new to Godot. I can’t understand that. :confused: Thanks for your help! :smiley:

I can give you a more detailed description. I made a test project in which a simple blur shader is applied to the same image each frame.

The node structure is like this:

Node2D
    SubViewportContainer
        SubViewport
            ColorRect
            Sprite2D

SubViewport has a property called render_target_clear_mode, which is set to never. This means that the screen won’t be cleared before the next frame is drawn.

For the ColorRect I set its anchors_preset to Full Rect, so that it fills the whole SubViewport.

Then I added a simple blur shader to the ColorRect. This shader reads the image on the screen and makes a blurry version of it by taking the average of 5 pixels.

shader_type canvas_item;

uniform sampler2D screen : hint_screen_texture;

void fragment() {
	vec3 col = texture(screen, SCREEN_UV).rgb;
	col += texture(screen, vec2(SCREEN_UV.x - SCREEN_PIXEL_SIZE.x, SCREEN_UV.y)).rgb;
	col += texture(screen, vec2(SCREEN_UV.x + SCREEN_PIXEL_SIZE.x, SCREEN_UV.y)).rgb;
	col += texture(screen, vec2(SCREEN_UV.x, SCREEN_UV.y - SCREEN_PIXEL_SIZE.y)).rgb;
	col += texture(screen, vec2(SCREEN_UV.x, SCREEN_UV.y + SCREEN_PIXEL_SIZE.y)).rgb;
	col *= 0.2;
	COLOR = vec4(col, 1.0);
}

Now if I add a texture to the Sprite2D node, the image will be sharp, but when I hide the Sprite2D, the image will persist and get blurrier over time.

For Game of Life you just have to change the shader and maybe figure out a different way to set the initial state.

Cool, it’s working! Thank you so much. :grinning:

From my understanding, you used hint_screen_texture to access the already-rendered image on the screen and apply the shader to the result. Is that correct? Additionally, I can’t quite understand the reason why the texture only starts to blur when the Sprite2D is hidden. Shouldn’t the texture start to blur only when it’s visible on the screen? :thinking: Again, thanks for your kind help. I appreciate everything you have done.

Sorry for the late reply. Your understanding of hint_screen_texture is correct. And the Sprite2D kind of gets blurry even when it’s visible, but you can’t see it, because a new sharp image is drawn on top of the ColorRect every frame. Once the sprite is hidden, you can see the blur effect happening uninterrupted.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.