Shader over several text gui nodes (SOLVED)

Hi! I have a gui that contains 4 dialog options stacked on top of each other. Each dialog option is a separate gui object. Like so:

I want to run a shader over all of the dialog options. For example:

What’s the right way to set this up? A shader on the containing box doesn’t seem to affect the text. Do I need to add a backbuffer to the render script, or can it be done by just enabling a material before rending text?

Thank you so much!

Possible solution:

  1. Create new material and set it for text nodes you want to have such effect
  2. Set predicate for the material
  3. Write this predicate into render target (You need to write it in render script)
  4. Use this render target as texture in other node and apply material with shader to this node

(shortly: write your text to texture, later apply shader to entire texture and draw it as node)

Yup, this is what I’ve been trying to do and kept running into issues (specifically, nothing from my texture would render).

But I think I finally figured out what I was doing wrong(?)… I was trying to apply the material for my render target texture to a sprite, but for some reason that acts really strangely. I ended up having to apply it to a model file (quad.dae), and that started rendering things correctly.

I don’t really understand why a model is required to do this, though. And it means I had to modify my appmanifest to bring Model+Rig back in just to do this effect which is kind of annoying.

This is what I’ve been using that has been the most hepful: Mastering Render Targets

1 Like

This looks like it’s working so far: using a quad I can display my gui from a secondary render target. The two things I had to do:

  • make sure to update the params of the render target if the window is resized
  • add a ‘discard’ clause to the shader so that areas with no pixels are drawn transparent
#version 140

in vec2 var_texcoord0;
out vec4 color;

uniform sampler2D texture_sampler;

void main()
{
	color = texture(texture_sampler, var_texcoord0.xy);
	if (color.a == 0) discard;
}

The thing I need to figure out now is how to keep the quad matching position with the gui element, since the quad is not a gui element itself.

Is there some option to apply the material to a gui box instead of a quad? I haven’t gotten it to work yet, but maybe your fp needs to be different?

More progress. I was able to use a new gui object instead of a quad model. The secret was to make a new gui, set the material for the text to a copy of the builtin font material, with a new unique tag (gui2 lol naming is hard) which gets its own predicate in the render script.

The blog postI linked had a clear call that didn’t work; had to change it to:

render.clear({
        [graphics.BUFFER_TYPE_COLOR0_BIT] = vmath.vector4(0),
        [graphics.BUFFER_TYPE_DEPTH_BIT] = 1,
        [graphics.BUFFER_TYPE_STENCIL_BIT] = 0
    })

Here is my main game screen now:

image

window.gui is just a full-sized box with the custom gui material set on it. The text I want to run a shader against uses a custom font material with a gui2 tag on it, so it’s the only thing I’m writing to a texture for post-processing.

Now I have to try and actually add the shader code to my fp. But that’s for tomorrow.

Okay, it worked. Just change what material I use to render the post processed gui elements.

The shader is from Shadertoy [link]. Used this article to convert it.

gui_ripple.fp

#version 140

in vec2 var_texcoord0;
out vec4 fragColor;

uniform sampler2D texture_sampler;

uniform params {
	vec4 u_time;
	vec4 u_res;
};

void main()
{
	float time = u_time.x;

	vec2 res = u_res.xy;
	vec2 uv = var_texcoord0.xy;

	float w = (0.5 - (uv.x)) * (res.x / res.y);
	float h = 0.5 - uv.y;
	float distanceFromCenter = sqrt(w * w + h * h);

	float sinArg = distanceFromCenter * 10.0 - time * 10.0;
	float slope = cos(sinArg) ;
	vec4 color = texture(texture_sampler, uv + normalize(vec2(w, h)) * slope * 0.05);

	if (color.a == 0) discard;
	
	fragColor = color;
}

Note that I pass in a resolution scale so I can update it when the window size changes, and a time. Both from the render script.

self.time.x = self.time.x + dt
    local constants = render.constant_buffer()
    constants.u_time = self.time    

...

local res = vmath.vector4(0)
    if state.window_height > state.window_width then
        local scale = state.window_height / state.window_width
        res.x = 1.0
        res.y = scale
    else
        local scale = state.window_width / state.window_height
        res.x = scale
        res.y = 1.0
    end    

    constants.u_res = res

...

render.enable_texture(0, self.gui_pp_target, render.BUFFER_COLOR_BIT)
    render.draw(predicates.gui_pp, { constants = constants })
    render.disable_texture(0, self.gui_pp_target)

Also I set uv to be equal to var_textcoord0 instead of multiplying by the resolution. Not sure why, it just worked when I did that.

2 Likes