Animated and Gradient Outlines
It’s alot like
Third one is both!
Most uniforms are pretty self explanatory, otherwise
block_size <- size of segmentation for line width variation in animated outline
shader_type canvas_item;
uniform float max_line_width = 10.0;
uniform float min_line_width = 5.0;
uniform float freq = 1.0;
uniform float block_size = 20.0;
uniform vec4 outline_colour = vec4(0,0,0,1);
const float pi = 3.1415;
const int ang_res = 16;
float hash(vec2 p, float s) {
return fract(35.1 * sin(dot(vec3(112.3, 459.2, 753.2), vec3(p, s))));
}
float noise(vec2 p, float s) {
vec2 d = vec2(0, 1);
vec2 b = floor(p);
vec2 f = fract(p);
return mix(
mix(hash(b + d.xx, s), hash(b + d.yx, s), f.x),
mix(hash(b + d.xy, s), hash(b + d.yy, s), f.x), f.y);
}
float get_line_width(vec2 p, float s) {
p /= block_size;
float w = 0.0;
float intensity = 1.0;
for (int i = 0; i < 3; i++) {
w = mix(w, noise(p, s), intensity);
p /= 2.0;
intensity /= 2.0;
}
return mix(max_line_width, min_line_width, w);
}
void fragment() {
float alpha = 0.0;
float timeStep = floor(TIME * freq);
vec2 scale = TEXTURE_PIXEL_SIZE;
scale *= get_line_width(UV / TEXTURE_PIXEL_SIZE, timeStep);
for (int i = 0; i < ang_res; i++) {
float angle = 2.0 * pi * float(i) / float(ang_res);
vec2 disp = scale * vec2(cos(angle), sin(angle));
alpha += texture(TEXTURE, UV + disp).a;
}
if ((alpha > 0.0) && (texture(TEXTURE, UV).a < 0.1)) {
COLOR = outline_colour;
}
else {
COLOR = texture(TEXTURE, UV);
}
}
if (pixelInRange(text, uv, curr * maxDist)) {
hi = curr;
}
else {
lo = curr;
}
}
return hi;
}
void fragment() {
vec2 scaledDist = TEXTURE_PIXEL_SIZE * line_width;
float w = getClosestDistance(TEXTURE, UV, scaledDist);
if (( w > 0.0) && (texture(TEXTURE, UV).a < 0.1)) {
COLOR = mix(starting_colour, ending_colour, tanh(3.0*w));
}
else {
COLOR = texture(TEXTURE, UV);
}
}
Shader code
shader_type canvas_item;
uniform float max_line_width = 10.0;
uniform float min_line_width = 5.0;
uniform float freq = 1.0;
uniform float block_size = 20.0;
uniform vec4 starting_colour = vec4(0,0,0,1);
uniform vec4 ending_colour = vec4(1);
const float pi = 3.1415;
const int ang_res = 16;
const int grad_res = 8;
float hash(vec2 p, float s) {
return fract(35.1 * sin(dot(vec3(112.3, 459.2, 753.2), vec3(p, s))));
}
float noise(vec2 p, float s) {
vec2 d = vec2(0, 1);
vec2 b = floor(p);
vec2 f = fract(p);
return mix(
mix(hash(b + d.xx, s), hash(b + d.yx, s), f.x),
mix(hash(b + d.xy, s), hash(b + d.yy, s), f.x), f.y);
}
float getLineWidth(vec2 p, float s) {
p /= block_size;
float w = 0.0;
float intensity = 1.0;
for (int i = 0; i < 3; i++) {
w = mix(w, noise(p, s), intensity);
p /= 2.0;
intensity /= 2.0;
}
return mix(max_line_width, min_line_width, w);
}
bool pixelInRange(sampler2D text, vec2 uv, vec2 dist) {
float alpha = 0.0;
for (int i = 0; i < ang_res; i++) {
float angle = 2.0 * pi * float(i) / float(ang_res);
vec2 disp = dist * vec2(cos(angle), sin(angle));
if (texture(text, uv + disp).a > 0.0) return true;
}
return false;
}
float getClosestDistance(sampler2D text, vec2 uv, vec2 maxDist) {
if (!pixelInRange(text, uv, maxDist)) return -1.0;
float hi = 1.0; float lo = 0.0;
for (int i = 1; i <= grad_res; i++) {
float curr = (hi + lo) / 2.0;
if (pixelInRange(text, uv, curr * maxDist)) {
hi = curr;
}
else {
lo = curr;
}
}
return hi;
}
void fragment() {
float timeStep = floor(freq * TIME);
vec2 scaledDist = TEXTURE_PIXEL_SIZE;
scaledDist *= getLineWidth(UV / TEXTURE_PIXEL_SIZE, timeStep);
float w = getClosestDistance(TEXTURE, UV, scaledDist);
if (( w > 0.0) && (texture(TEXTURE, UV).a < 0.2)) {
COLOR = mix(starting_colour, ending_colour, tanh(3.0*w));
}
else {
COLOR = texture(TEXTURE, UV);
}
}
Thanks for the shader.
I think there is a mess with the published codes.
You say there are three shaders but in the center block there is only one piece of incomplete code.
cool effect, but glitches out in a couple seconds on my iphone 🙁
You need to decrease Time Rollover Secs in the Project Settings to avoid visible precision issues on mobile platforms, as mobile GPUs use lower shader precision by default. When targeting mobile, try using a value of 32 or lower (floating-point precision is halved at every power of two value).