Animated Dotted Outline
I mixed an inner stroke shader I made with dotted-circle.
The result is a new shader that you can tweak to have both an inner stroke, but also mix it to have a dotted line as your inner stroke. It can be animated by changing the phase speed.
It doesn’t make a perfectly regular dotted line, but I think is good enough.
Got a bit of inspiration from Animal Well to make this one.
It has several options as shown in the screenshot. It allows to create several effects.
P.S. I’m pretty sure you can make it work in Godot 3.0 by changing source_color to hint_color, but let me know if that doesn’t work. I am not knowdlegeable enough to be sure, but I believe this would work on any renderer, maybe not GLES2.
Shader code
shader_type canvas_item;
uniform vec4 color : source_color = vec4(1.0);
uniform float inner_stroke_thickness = 1.0; // Inner stroke thickness, adjust according to sprite size
uniform float inner_stroke_opacity = 1.0; // Inner stroke opacity, ranges from 0.0 to 1.0
uniform float inside_opacity = 0.0;
uniform float frequency = 8.0; // Controls the number of dotted lines
uniform float phase_speed = 1.0; // Controls the rotation of the border
void fragment() {
// Final outputs
vec4 inner_stroke;
vec4 circle_outline;
// INNER STROKE
float radius = inner_stroke_thickness / float(textureSize(TEXTURE, 0).x);
// Initialize alpha to maximum
float minAlpha = 1.0;
// Sample a grid around the pixel based on the defined radius to find the minimum alpha
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) * radius;
float sampleAlpha = texture(TEXTURE, UV + offset).a;
minAlpha = min(minAlpha, sampleAlpha);
}
}
// Get the original alpha value at the fragment
float originalAlpha = texture(TEXTURE, UV).a;
// Compare and apply the inner stroke color if in the inner stroke region
if (originalAlpha > minAlpha) {
float innerStrokeAlpha = originalAlpha * (originalAlpha - minAlpha) * inner_stroke_opacity; // Blend the inner stroke alpha based on the difference in alpha and opacity setting
inner_stroke = vec4(1.0, 1.0, 1.0, innerStrokeAlpha);
} else {
float insideAlpha = originalAlpha * inside_opacity; // Blend the inner stroke alpha based on the difference in alpha and opacity setting
inner_stroke = vec4(1.0, 1.0, 1.0, insideAlpha);
}
// INNER STROKE END
// CIRCLE OUTLINE
vec2 pos = UV - vec2(0.5);
float outer_radius = inner_stroke_thickness / 2.0;
float inner_radius = outer_radius - inner_stroke_thickness;
float outer_circle = step(length(pos), outer_radius);
float inner_circle = step(length(pos), inner_radius);
float angle = atan(pos.y, pos.x);
if (angle < 0.0) {
angle += 2.0 * PI;
}
float wave = 0.5 * sin(frequency * angle + TIME * phase_speed) + 0.5;
float ring = outer_circle - inner_circle;
ring *= step(0.5, wave);
circle_outline = vec4(color.rgb, ring * color.a);
// CIRCLE OUTLINE END
COLOR = inner_stroke * circle_outline;
}
Can’t seem to get it to work, instead of an outline I get a cut-up circle. Are there extra steps that need to be done to get the outline?
https://imgur.com/a/SNDb2aS
@choco – I think this shader assumes that you have a parent mask applied. I got it to work like this:
Root -Texture Rect of a solid white square with transparent borders.Visibility – Clip Children is set to “Clip Only”Size: Arbitrary, I used 100×100.
Child – Color Rect with this shader applied.Size: Same as parent.Shader paramsColor – WhiteInner Stroke Thickness – 1.5Inner Stroke Opacity – 1Inside Opacity – 1Frequency – 8Phase Speed – 6
Then it looks just like in the preview picture.
“Root -Texture Rect of a solid white square with transparent borders.”
I mistyped this. It should be the inverse rather. A texture of a solid white outline with a transparent center.
I have no idea what you are talking about.
Does inverse mean we have to switch textureRect and colorRect around? Which node clip its children? This is causing me such headaches.
Parent = Texture Rect with an image of a white square border
should be
then ok
don’t work
shader_type canvas_item;
uniform vec4 color : source_color = vec4(1.0);
uniform float inner_stroke_thickness = 1.0; // Inner stroke thickness, adjust according to sprite size
uniform float inner_stroke_opacity = 1.0; // Inner stroke opacity, ranges from 0.0 to 1.0
uniform float inside_opacity = 0.0;
uniform float frequency = 8.0; // Controls the number of dotted lines
uniform float phase_speed = 1.0; // Controls the rotation of the border
uniform vec2 rect_size = vec2(0.3, 0.3); // 矩形大小
uniform vec2 rect_position = vec2(0.5, 0.5); // 矩形位置
void fragment() {
// Final outputs
vec4 inner_stroke;
vec4 circle_outline;
// INNER STROKE
float radius = inner_stroke_thickness / float(textureSize(TEXTURE, 0).x);
// Initialize alpha to maximum
float minAlpha = 1.0;
// Sample a grid around the pixel based on the defined radius to find the minimum alpha
for (int x = -1; x <= 1; x++) {
for (int y = -1; y <= 1; y++) {
vec2 offset = vec2(float(x), float(y)) * radius;
float sampleAlpha = texture(TEXTURE, UV + offset).a;
minAlpha = min(minAlpha, sampleAlpha);
}
}
// Get the original alpha value at the fragment
float originalAlpha = texture(TEXTURE, UV).a;
// Compare and apply the inner stroke color if in the inner stroke region
if (originalAlpha > minAlpha) {
float innerStrokeAlpha = originalAlpha * (originalAlpha – minAlpha) * inner_stroke_opacity; // Blend the inner stroke alpha based on the difference in alpha and opacity setting
inner_stroke = vec4(1.0, 1.0, 1.0, innerStrokeAlpha);
} else {
float insideAlpha = originalAlpha * inside_opacity; // Blend the inner stroke alpha based on the difference in alpha and opacity setting
inner_stroke = vec4(1.0, 1.0, 1.0, insideAlpha);
}
// INNER STROKE END
// CIRCLE OUTLINE
vec2 pos = UV – vec2(0.5);
float outer_radius = inner_stroke_thickness / 2.0;
float inner_radius = outer_radius – inner_stroke_thickness;
float outer_circle = step(length(pos), outer_radius);
float inner_circle = step(length(pos), inner_radius);
float angle = atan(pos.y, pos.x);
if (angle < 0.0) {
angle += 2.0 * PI;
}
float wave = 0.5 * sin(frequency * angle + TIME * phase_speed) + 0.5;
float ring = outer_circle – inner_circle;
ring *= step(0.5, wave);
circle_outline = vec4(color.rgb, ring * color.a);
// CIRCLE OUTLINE END
// 计算矩形区域
vec2 uv_offset = UV – rect_position;
float rect_alpha = 1.0 – step(abs(uv_offset.x), rect_size.x/2.0) * step(abs(uv_offset.y), rect_size.y/2.0);
COLOR = inner_stroke * circle_outline * vec4(1.0, 1.0, 1.0, rect_alpha);
}