Godot Version
4.3
Question
`Hi! I’ve been trying to make a shader for my game. Like in Super Mario World, when Mario finishes a level and this black circle surrounds him and covers the screen. I have this:
shader_type canvas_item;
uniform float radius : hint_range(0,256) = 256.0;
uniform vec2 center = vec2(128.0, 112.0);
void fragment() {
float x = UV.x * 256.0;
float y = UV.y * 224.0;
float alpha = float((x - center.x) * (x - center.x) + (y - center.y) * (y - center.y) > radius * radius);
COLOR.a = alpha;
}
The issue is that it just doesn’t work. I don’t understand how it works, I just copied it from a YouTube video, but they didn’t say how to properly implement it (it was a devlog, not a tutorial, and I couldn’t find any of those). Do I have to write some additional script? I’ve created the shader for the main node that controls the scene. Maybe that’s the problem?
Thanks!
`
You could simplify the shader, but you will need to animate it externally. have you tried altering the shader’s radius value? What about it doesn’t work?
1 Like
UV
are the coordinates of the fragment (the pixel we’re trying to draw) on the texture we’re using to paint whatever we’re drawing. They’re usually normalized (between 0.0 and 1.0) though they can go outside that range under specific circumstances. You can think of U
and V
as being the x
and y
coordinates on a texture.
This shader is assuming a 256x224 display, and generating alpha (that is, transparency) values based on distance from the center of the “screen”. I assume it’s intended to be a shader material on a full-screen quad like a ColorRect
or something.
I would probably not have done it this way.
You can get rid of a lot of the math:
shader_type canvas_item;
uniform float radius: hint_range(0.0, 1.0) = 1.0;
void fragment()
{
// Could be a vec2, kept it as floats for clarity.
float x = 0.5 - UV.x // Move the origin to the middle.
float y = 0.5 - UV.y
COLOR.rgb = 0.0 // Black.
// Length of vector xy is sqrt(x^2 + y^2), we skip the root and square radius
// instead; multiply is cheaper than square root. You could make this even
// cheaper by handing in r^2, since the math below happens per-fragment.
if (x * x) + (y * y) < (r * r)
{
COLOR.a = 0.0 // Transparent
}
else
{
COLOR.a = 1.0 // Opaque
}
}
As @gertkeno says, you need to do more than just drop this in. I think it expects to be the shader material on a ColorRect, and you need to set the radius
uniform per-frame to make it do anything.
2 Likes
I had to change a thing or two (now the shader is on a ColorRect rather than the root node of the scene), but it does work. Now I only need an animation player to finish up.
This is the code:
uniform float radius : hint_range(0.0, 1.0) = 1.0;
void fragment() {
float x = UV.x - 0.5;
float y = UV.y - 0.5;
COLOR.rgb = vec3(0.0);
if ((x * x + y * y) < (radius * radius)) {
COLOR.a = 0.0;
} else {
COLOR.a = 1.0;
}
}
Thanks!
1 Like