godot/drivers/gles3/shaders/canvas_sdf.glsl
clayjohn 2ec234ff67 Add 2D shadows and canvas SDF to OpenGL3 renderer
This is an initial implementation based on the current RD implementation

Performance will improve later
2022-10-28 11:33:23 -07:00

206 lines
4.0 KiB
GLSL

/* clang-format off */
#[modes]
mode_load = #define MODE_LOAD
mode_load_shrink = #define MODE_LOAD_SHRINK
mode_process = #define MODE_PROCESS
mode_store = #define MODE_STORE
mode_store_shrink = #define MODE_STORE_SHRINK
#[specializations]
#[vertex]
layout(location = 0) in vec2 vertex_attrib;
/* clang-format on */
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
void main() {
gl_Position = vec4(vertex_attrib, 1.0, 1.0);
}
/* clang-format off */
#[fragment]
#define SDF_MAX_LENGTH 16384.0
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK)
uniform lowp sampler2D src_pixels;//texunit:0
#else
uniform highp isampler2D src_process;//texunit:0
#endif
uniform ivec2 size;
uniform int stride;
uniform int shift;
uniform ivec2 base_size;
#if defined(MODE_LOAD) || defined(MODE_LOAD_SHRINK) || defined(MODE_PROCESS)
layout(location = 0) out ivec4 distance_field;
#else
layout(location = 0) out vec4 distance_field;
#endif
vec4 float_to_vec4(float p_float) {
highp vec4 comp = fract(p_float * vec4(255.0 * 255.0 * 255.0, 255.0 * 255.0, 255.0, 1.0));
comp -= comp.xxyz * vec4(0.0, 1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0);
return comp;
}
void main() {
ivec2 pos = ivec2(gl_FragCoord.xy);
#ifdef MODE_LOAD
bool solid = texelFetch(src_pixels, pos, 0).r > 0.5;
distance_field = solid ? ivec4(ivec2(-32767), 0, 0) : ivec4(ivec2(32767), 0, 0);
#endif
#ifdef MODE_LOAD_SHRINK
int s = 1 << shift;
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = ivec2(32767);
float d = 1e20;
int found = 0;
int solid_found = 0;
for (int i = 0; i < s; i++) {
for (int j = 0; j < s; j++) {
ivec2 src_pos = base + ivec2(i, j);
if (any(greaterThanEqual(src_pos, base_size))) {
continue;
}
bool solid = texelFetch(src_pixels, src_pos, 0).r > 0.5;
if (solid) {
float dist = length(vec2(src_pos - center));
if (dist < d) {
d = dist;
rel = src_pos;
}
solid_found++;
}
found++;
}
}
if (solid_found == found) {
//mark solid only if all are solid
rel = ivec2(-32767);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_PROCESS
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
if (center != rel) {
//only process if it does not point to itself
const int ofs_table_size = 8;
const ivec2 ofs_table[ofs_table_size] = ivec2[](
ivec2(-1, -1),
ivec2(0, -1),
ivec2(+1, -1),
ivec2(-1, 0),
ivec2(+1, 0),
ivec2(-1, +1),
ivec2(0, +1),
ivec2(+1, +1));
float dist = length(vec2(rel - center));
for (int i = 0; i < ofs_table_size; i++) {
ivec2 src_pos = pos + ofs_table[i] * stride;
if (any(lessThan(src_pos, ivec2(0))) || any(greaterThanEqual(src_pos, size))) {
continue;
}
ivec2 src_rel = texelFetch(src_process, src_pos, 0).xy;
bool src_solid = src_rel.x < 0;
if (src_solid) {
src_rel = -src_rel - ivec2(1);
}
if (src_solid != solid) {
src_rel = ivec2(src_pos << shift); //point to itself if of different type
}
float src_dist = length(vec2(src_rel - center));
if (src_dist < dist) {
dist = src_dist;
rel = src_rel;
}
}
}
if (solid) {
rel = -rel - ivec2(1);
}
distance_field = ivec4(rel, 0, 0);
#endif
#ifdef MODE_STORE
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - pos));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
#ifdef MODE_STORE_SHRINK
ivec2 base = pos << shift;
ivec2 center = base + ivec2(shift);
ivec2 rel = texelFetch(src_process, pos, 0).xy;
bool solid = rel.x < 0;
if (solid) {
rel = -rel - ivec2(1);
}
float d = length(vec2(rel - center));
if (solid) {
d = -d;
}
d /= SDF_MAX_LENGTH;
d = clamp(d, -1.0, 1.0);
distance_field = float_to_vec4(d*0.5+0.5);
#endif
}