Created
January 1, 2025 17:37
-
-
Save maidopi-usagi/66aaaf63ff0814ddb8b0c44dac6696df to your computer and use it in GitHub Desktop.
Godot AtlasTexture Outline Shader
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| shader_type canvas_item; | |
| const vec2 VERTICES[4] = vec2[](vec2(0.0),vec2(0.0,1.0),vec2(1.0),vec2(1.0,0.0)); | |
| varying vec2 originalUV; | |
| varying flat vec4 regionInfo; | |
| uniform int outline_size:hint_range(0,10) = 1; | |
| uniform vec4 outline_color:source_color = vec4(1.0); | |
| void vertex() { | |
| originalUV= VERTICES[VERTEX_ID]; | |
| regionInfo.xy = VERTEX.xy/(VERTICES[0] - 0.5); | |
| regionInfo.zw = UV; | |
| VERTEX.xy += vec2(float(outline_size)*2.0) * (originalUV - vec2(0.5)); | |
| } | |
| vec2 region_from_texture(vec2 uv, vec2 size, vec4 region) { | |
| return region.zw + uv / size * region.xy; | |
| } | |
| vec2 texture_from_region(vec2 uv, vec2 size, vec4 region) { | |
| return uv * size / region.xy - region.zw; | |
| } | |
| vec4 sample_tex(sampler2D t, vec2 offset, vec2 size, vec2 uv_to_transform) | |
| { | |
| vec2 uv = uv_to_transform + offset; | |
| bool inside = uv.x <= 1.0 && uv.x >= 0.0 && uv.y >= 0.0 && uv.y <= 1.0; | |
| if (!inside) return vec4(0.0); | |
| vec2 restoredUV = region_from_texture(uv, size, regionInfo); | |
| return texture(t, restoredUV); | |
| } | |
| void fragment() { | |
| vec2 size = vec2(textureSize(TEXTURE, 0)); | |
| vec2 atlas_size = regionInfo.xy; | |
| vec2 scaled_ratio = (atlas_size + vec2(float(outline_size) * 2.0)) / atlas_size; | |
| vec2 uv = (originalUV - 0.5) * scaled_ratio + 0.5; | |
| float outline = 0.0; | |
| vec2 offset_ratio = float(outline_size) / atlas_size; | |
| outline = max(sample_tex(TEXTURE, vec2(0.0, -1.0) * offset_ratio, size, uv).a, outline); | |
| outline = max(sample_tex(TEXTURE, vec2(1.0, -0.0) * offset_ratio, size, uv).a, outline); | |
| outline = max(sample_tex(TEXTURE, vec2(-1.0, 0.0) * offset_ratio, size, uv).a, outline); | |
| outline = max(sample_tex(TEXTURE, vec2(0.0, 1.0) * offset_ratio, size, uv).a, outline); | |
| vec4 tex = sample_tex(TEXTURE, vec2(0.0), size, uv); | |
| COLOR = mix(vec4(outline_color.rgb, outline * outline_color.a), tex, tex.a); | |
| } |
A wip implementation
shader_type canvas_item;
uniform float outline_size = 1.0;
uniform float alpha_clip_threshold = 0.01;
varying vec4 self_modulate;
varying vec2 uv_scale_amount;
varying vec2 uv_offset_amount;
const vec2 vertice_info[4] = vec2[](vec2(0, 0), vec2(0, 1), vec2(1, 1), vec2(1, 0));
const vec2 vertice_offsets[4] = vec2[](vec2(-1, -1), vec2(-1, +1), vec2(+1, +1), vec2(+1, -1));
varying vec2 original_uv;
varying flat vec4 region_info;
void vertex() {
original_uv = vertice_info[VERTEX_ID];
region_info.xy = VERTEX.xy / (vertice_info[0] - 0.5);
region_info.zw = UV;
self_modulate = COLOR;
VERTEX += vertice_offsets[VERTEX_ID] * outline_size;
}
//vec2 region_from_texture(vec2 uv, vec2 size, vec4 region) {
//return region.zw + uv / size * region.xy;
//}
//
//vec2 texture_from_region(vec2 uv, vec2 size, vec4 region) {
//return uv * size / region.xy - region.zw;
//}
vec4 sample(sampler2D tex, vec2 validation_uv, vec2 sampling_uv) {
float factor = float(step(validation_uv, vec2(0.)) + step(vec2(1.), validation_uv) == vec2(0));
return texture(tex, sampling_uv) * factor;
return vec4(factor, factor, factor, 1);
}
vec4 sample_offset(sampler2D tex, vec2 validation_uv, vec2 sampling_uv, vec2 offset, vec2 validation_multiplier) {
return sample(tex, validation_uv + offset * validation_multiplier, sampling_uv + offset);
}
void fragment() {
vec2 corrected_uv = UV;
vec2 size = vec2(textureSize(TEXTURE, 0));
vec4 color = sample(TEXTURE, original_uv, corrected_uv);
COLOR = color;
vec2 multiplier = 1. / region_info.zw;
float outlineL = sample_offset(TEXTURE, original_uv, corrected_uv, TEXTURE_PIXEL_SIZE * vec2(outline_size, 0), multiplier).a;
float outlineR = sample_offset(TEXTURE, original_uv, corrected_uv, TEXTURE_PIXEL_SIZE * vec2(-outline_size, 0), multiplier).a;
float outlineU = sample_offset(TEXTURE, original_uv, corrected_uv, TEXTURE_PIXEL_SIZE * vec2(0, outline_size), multiplier).a;
float outlineD = sample_offset(TEXTURE, original_uv, corrected_uv, TEXTURE_PIXEL_SIZE * vec2(0, -outline_size), multiplier).a;
float sum = outlineL + outlineR + outlineU + outlineD;
float clampedSum = ceil(sum);
if(color.a <= alpha_clip_threshold) COLOR = vec4(self_modulate.rgb, self_modulate.a * clampedSum);
else COLOR = color;
}
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
An updated version that accounts for corners.