2025-05-29 06:14:02 +00:00
|
|
|
//https://godotshaders.com/shader/various-canvas-outlines/
|
|
|
|
shader_type canvas_item;
|
|
|
|
|
|
|
|
// The outline color. GradientTexture1D is recommended.
|
|
|
|
uniform sampler2D color: source_color;
|
2025-05-30 11:05:06 +00:00
|
|
|
// 影响 alpha 的比重
|
|
|
|
uniform float alpha_ratio: hint_range(0.0, 1.0) = 1.0;
|
2025-05-29 06:14:02 +00:00
|
|
|
// The resolution for the gradient. Higher numbers will result in smoother but more expensive passes.
|
|
|
|
uniform int gradientResolution: hint_range(1, 30) = 10;
|
|
|
|
// Outline thickness.
|
|
|
|
uniform float thickness: hint_range(0.0, 100.0) = 1.0;
|
|
|
|
// Used to compensate for alpha values.
|
|
|
|
uniform float tolerance: hint_range(0.0, 0.999) = 0.0;
|
|
|
|
// If on will draw at diagonals. Off is mainly for pixel art but you do you.
|
|
|
|
uniform bool diagonals = true;
|
|
|
|
// If diagonals are checked will check for half pixels so it rounds the outline a bit more.
|
|
|
|
uniform bool rounded = true;
|
|
|
|
|
|
|
|
// Compensate UV for outline.
|
|
|
|
void vertex() {
|
|
|
|
VERTEX = vec2(VERTEX.x * (1.0 + TEXTURE_PIXEL_SIZE.x * thickness * 2.0), VERTEX.y * (1.0 + TEXTURE_PIXEL_SIZE.y * thickness * 2.0));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks a fragment for the edge of an uv.
|
|
|
|
bool border(vec2 uv) {
|
|
|
|
vec2 uvBorder = abs(uv - vec2(0.5));
|
|
|
|
return max(step(0.5, uvBorder.x), step(0.5, uvBorder.y)) > 0.0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Gets alpha of given fragment if not near the edge.
|
|
|
|
float get_alpha(sampler2D tex, vec2 uv){
|
|
|
|
float res = 0.0;
|
|
|
|
if (!border(uv)) {
|
|
|
|
res = texture(tex, uv).a;
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Checks for neighboring pixels.
|
|
|
|
bool in_range(vec2 size, sampler2D tex, vec2 uv) {
|
|
|
|
for (float i = -1.0; i < 2.0; i += 2.0) {
|
|
|
|
if (get_alpha(tex, uv + vec2(i * size.x, 0.0)) > 0.0) {return true;};
|
|
|
|
if (get_alpha(tex, uv + vec2(0.0, i * size.y)) > 0.0) {return true;};
|
|
|
|
if (diagonals) {
|
|
|
|
for (float j = -1.0; j < 2.0; j += 2.0) {
|
|
|
|
if (get_alpha(tex, uv + vec2(i * size.x, j * size.y)) > 0.0) {return true;};
|
|
|
|
if (rounded) {
|
|
|
|
if (get_alpha(tex, uv + vec2(i * size.x, j * size.y * 0.5)) > 0.0) {return true;};
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get's closes pixel.
|
|
|
|
float get_distance(vec2 maxDistance, sampler2D tex, vec2 uv) {
|
|
|
|
for (int i = 1; i < gradientResolution; i++) {
|
|
|
|
vec2 actualDistance = float(i) / float (gradientResolution) * maxDistance;
|
|
|
|
if (in_range(actualDistance, tex, uv)) {
|
|
|
|
return float(i) / float (gradientResolution);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void fragment() {
|
|
|
|
if (thickness > 0.0) {
|
|
|
|
// Correct image size to for outline in frame.
|
|
|
|
vec2 uv = UV;
|
|
|
|
uv -= vec2(0.5);
|
|
|
|
vec2 edge = TEXTURE_PIXEL_SIZE * thickness * 2.0;
|
|
|
|
uv = uv + uv * edge;
|
|
|
|
uv += vec2(0.5);
|
|
|
|
|
|
|
|
// Apply outline.
|
|
|
|
vec4 newColor = texture(TEXTURE, uv);
|
|
|
|
if (newColor.a <= tolerance || border(uv)) {
|
|
|
|
vec4 finalColor = step(1.0 - tolerance, in_range(edge / 2.0, TEXTURE, uv) ? 1.0: 0.0) * texture(color, vec2(get_distance(edge / 2.0 + 0.001, TEXTURE, uv)));
|
|
|
|
newColor = finalColor;
|
2025-05-30 11:05:06 +00:00
|
|
|
newColor.a *= alpha_ratio;
|
2025-05-29 06:14:02 +00:00
|
|
|
}
|
|
|
|
COLOR = newColor;
|
|
|
|
}
|
|
|
|
}
|