diff options
author | clayjohn <claynjohn@gmail.com> | 2022-10-18 17:59:31 -0700 |
---|---|---|
committer | clayjohn <claynjohn@gmail.com> | 2022-10-28 11:33:23 -0700 |
commit | 2ec234ff67496c1410df2ab14c03e144889adc8d (patch) | |
tree | 6dccea8038d37fb352dacfd7cf1c688eb11c0316 /drivers/gles3/shaders | |
parent | 282e50ac88bb02923ef6188015c121eea6c9286a (diff) |
Add 2D shadows and canvas SDF to OpenGL3 renderer
This is an initial implementation based on the current RD implementation
Performance will improve later
Diffstat (limited to 'drivers/gles3/shaders')
-rw-r--r-- | drivers/gles3/shaders/SCsub | 2 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas.glsl | 149 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas_occlusion.glsl | 68 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas_sdf.glsl | 205 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas_shadow.glsl | 60 |
5 files changed, 423 insertions, 61 deletions
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 83ffe8b1e1..b8bb08ec34 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -17,3 +17,5 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("scene.glsl") env.GLES3_GLSL("sky.glsl") env.GLES3_GLSL("cubemap_filter.glsl") + env.GLES3_GLSL("canvas_occlusion.glsl") + env.GLES3_GLSL("canvas_sdf.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index 23db41802e..ca806304c5 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -10,6 +10,7 @@ mode_instanced = #define USE_ATTRIBUTES \n#define USE_INSTANCING #[specializations] DISABLE_LIGHTING = false +USE_RGBA_SHADOWS = false #[vertex] @@ -213,8 +214,8 @@ void main() { #ifndef DISABLE_LIGHTING uniform sampler2D atlas_texture; //texunit:-2 +uniform sampler2D shadow_atlas_texture; //texunit:-3 #endif // DISABLE_LIGHTING -//uniform sampler2D shadow_atlas_texture; //texunit:-3 uniform sampler2D screen_texture; //texunit:-4 uniform sampler2D sdf_texture; //texunit:-5 uniform sampler2D normal_texture; //texunit:-6 @@ -245,6 +246,35 @@ layout(std140) uniform MaterialUniforms{ #endif #GLOBALS + +float vec4_to_float(vec4 p_vec) { + return dot(p_vec, vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0; +} + +vec2 screen_uv_to_sdf(vec2 p_uv) { + return screen_to_sdf * p_uv; +} + +float texture_sdf(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; + float d = vec4_to_float(texture(sdf_texture, uv)); + d *= SDF_MAX_LENGTH; + return d * tex_to_sdf; +} + +vec2 texture_sdf_normal(vec2 p_sdf) { + vec2 uv = p_sdf * sdf_to_tex.xy + sdf_to_tex.zw; + + const float EPSILON = 0.001; + return normalize(vec2( + vec4_to_float(texture(sdf_texture, uv + vec2(EPSILON, 0.0))) - vec4_to_float(texture(sdf_texture, uv - vec2(EPSILON, 0.0))), + vec4_to_float(texture(sdf_texture, uv + vec2(0.0, EPSILON))) - vec4_to_float(texture(sdf_texture, uv - vec2(0.0, EPSILON))))); +} + +vec2 sdf_to_screen_uv(vec2 p_sdf) { + return p_sdf * sdf_to_screen; +} + #ifndef DISABLE_LIGHTING #ifdef LIGHT_CODE_USED @@ -299,6 +329,70 @@ vec3 light_normal_compute(vec3 light_vec, vec3 normal, vec3 base_color, vec3 lig } } +#ifdef USE_RGBA_SHADOWS + +#define SHADOW_DEPTH(m_uv) (dot(textureLod(shadow_atlas_texture, (m_uv), 0.0), vec4(1.0 / (255.0 * 255.0 * 255.0), 1.0 / (255.0 * 255.0), 1.0 / 255.0, 1.0)) * 2.0 - 1.0) + +#else + +#define SHADOW_DEPTH(m_uv) (textureLod(shadow_atlas_texture, (m_uv), 0.0).r) + +#endif + +#define SHADOW_TEST(m_uv) \ + { \ + highp float sd = SHADOW_DEPTH(m_uv); \ + shadow += step(sd, shadow_uv.z / shadow_uv.w); \ + } + +//float distance = length(shadow_pos); +vec4 light_shadow_compute(uint light_base, vec4 light_color, vec4 shadow_uv +#ifdef LIGHT_CODE_USED + , + vec3 shadow_modulate +#endif +) { + float shadow = 0.0; + uint shadow_mode = light_array[light_base].flags & LIGHT_FLAGS_FILTER_MASK; + + if (shadow_mode == LIGHT_FLAGS_SHADOW_NEAREST) { + SHADOW_TEST(shadow_uv.xy); + } else if (shadow_mode == LIGHT_FLAGS_SHADOW_PCF5) { + vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0); + shadow /= 5.0; + } else { //PCF13 + vec2 shadow_pixel_size = vec2(light_array[light_base].shadow_pixel_size, 0.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 6.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 5.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 4.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 3.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy - shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 2.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 3.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 4.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 5.0); + SHADOW_TEST(shadow_uv.xy + shadow_pixel_size * 6.0); + shadow /= 13.0; + } + + vec4 shadow_color = unpackUnorm4x8(light_array[light_base].shadow_color); +#ifdef LIGHT_CODE_USED + shadow_color.rgb *= shadow_modulate; +#endif + + shadow_color.a *= light_color.a; //respect light alpha + + return mix(light_color, shadow_color, shadow); +} + void light_blend_compute(uint light_base, vec4 light_color, inout vec3 color) { uint blend_mode = light_array[light_base].flags & LIGHT_FLAGS_BLEND_MASK; @@ -527,6 +621,19 @@ void main() { } #endif + if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + + vec4 shadow_uv = vec4(shadow_pos.x, light_array[light_base].shadow_y_ofs, shadow_pos.y * light_array[light_base].shadow_zfar_inv, 1.0); + + light_color = light_shadow_compute(light_base, light_color, shadow_uv +#ifdef LIGHT_CODE_USED + , + shadow_modulate.rgb +#endif + ); + } + light_blend_compute(light_base, light_color, color.rgb); } @@ -584,6 +691,46 @@ void main() { light_color.a = 0.0; } + if (bool(light_array[light_base].flags & LIGHT_FLAGS_HAS_SHADOW)) { + vec2 shadow_pos = (vec4(shadow_vertex, 0.0, 1.0) * mat4(light_array[light_base].shadow_matrix[0], light_array[light_base].shadow_matrix[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0))).xy; //multiply inverse given its transposed. Optimizer removes useless operations. + + vec2 pos_norm = normalize(shadow_pos); + vec2 pos_abs = abs(pos_norm); + vec2 pos_box = pos_norm / max(pos_abs.x, pos_abs.y); + vec2 pos_rot = pos_norm * mat2(vec2(0.7071067811865476, -0.7071067811865476), vec2(0.7071067811865476, 0.7071067811865476)); //is there a faster way to 45 degrees rot? + float tex_ofs; + float dist; + if (pos_rot.y > 0.0) { + if (pos_rot.x > 0.0) { + tex_ofs = pos_box.y * 0.125 + 0.125; + dist = shadow_pos.x; + } else { + tex_ofs = pos_box.x * -0.125 + (0.25 + 0.125); + dist = shadow_pos.y; + } + } else { + if (pos_rot.x < 0.0) { + tex_ofs = pos_box.y * -0.125 + (0.5 + 0.125); + dist = -shadow_pos.x; + } else { + tex_ofs = pos_box.x * 0.125 + (0.75 + 0.125); + dist = -shadow_pos.y; + } + } + + dist *= light_array[light_base].shadow_zfar_inv; + + //float distance = length(shadow_pos); + vec4 shadow_uv = vec4(tex_ofs, light_array[light_base].shadow_y_ofs, dist, 1.0); + + light_color = light_shadow_compute(light_base, light_color, shadow_uv +#ifdef LIGHT_CODE_USED + , + shadow_modulate.rgb +#endif + ); + } + light_blend_compute(light_base, light_color, color.rgb); } #endif diff --git a/drivers/gles3/shaders/canvas_occlusion.glsl b/drivers/gles3/shaders/canvas_occlusion.glsl new file mode 100644 index 0000000000..512800839a --- /dev/null +++ b/drivers/gles3/shaders/canvas_occlusion.glsl @@ -0,0 +1,68 @@ +/* clang-format off */ +#[modes] + +mode_sdf = +mode_shadow = #define MODE_SHADOW +mode_shadow_RGBA = #define MODE_SHADOW \n#define USE_RGBA_SHADOWS + +#[specializations] + +#[vertex] + +layout(location = 0) in vec3 vertex; + +uniform highp mat4 projection; +uniform highp vec4 modelview1; +uniform highp vec4 modelview2; +uniform highp vec2 direction; +uniform highp float z_far; + +#ifdef MODE_SHADOW +out float depth; +#endif + +void main() { + highp vec4 vtx = vec4(vertex, 1.0) * mat4(modelview1, modelview2, vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + +#ifdef MODE_SHADOW + depth = dot(direction, vtx.xy); +#endif + gl_Position = projection * vtx; +} + +#[fragment] + + +uniform highp mat4 projection; +uniform highp vec4 modelview1; +uniform highp vec4 modelview2; +uniform highp vec2 direction; +uniform highp float z_far; + +#ifdef MODE_SHADOW +in highp float depth; +#endif + +#ifdef USE_RGBA_SHADOWS +layout(location = 0) out lowp vec4 out_buf; +#else +layout(location = 0) out highp float out_buf; +#endif + +void main() { + float out_depth = 1.0; + +#ifdef MODE_SHADOW + out_depth = depth / z_far; +#endif + +#ifdef USE_RGBA_SHADOWS + out_depth = clamp(out_depth, -1.0, 1.0); + out_depth = out_depth * 0.5 + 0.5; + highp vec4 comp = fract(out_depth * 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); + out_buf = comp; +#else + out_buf = out_depth; +#endif +} diff --git a/drivers/gles3/shaders/canvas_sdf.glsl b/drivers/gles3/shaders/canvas_sdf.glsl new file mode 100644 index 0000000000..424ec22457 --- /dev/null +++ b/drivers/gles3/shaders/canvas_sdf.glsl @@ -0,0 +1,205 @@ +/* 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 +} diff --git a/drivers/gles3/shaders/canvas_shadow.glsl b/drivers/gles3/shaders/canvas_shadow.glsl deleted file mode 100644 index 94485abd11..0000000000 --- a/drivers/gles3/shaders/canvas_shadow.glsl +++ /dev/null @@ -1,60 +0,0 @@ -/* clang-format off */ -[vertex] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -precision highp float; -precision highp int; -#endif - -layout(location = 0) in highp vec3 vertex; - -uniform highp mat4 projection_matrix; -/* clang-format on */ -uniform highp mat4 light_matrix; -uniform highp mat4 model_matrix; -uniform highp float distance_norm; - -out highp vec4 position_interp; - -void main() { - gl_Position = projection_matrix * (light_matrix * (model_matrix * vec4(vertex, 1.0))); - position_interp = gl_Position; -} - -/* clang-format off */ -[fragment] - -#ifdef USE_GLES_OVER_GL -#define lowp -#define mediump -#define highp -#else -#if defined(USE_HIGHP_PRECISION) -precision highp float; -precision highp int; -#else -precision mediump float; -precision mediump int; -#endif -#endif - -in highp vec4 position_interp; -/* clang-format on */ - -void main() { - highp float depth = ((position_interp.z / position_interp.w) + 1.0) * 0.5 + 0.0; // bias - -#ifdef USE_RGBA_SHADOWS - - highp vec4 comp = fract(depth * 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); - frag_color = comp; -#else - - frag_color = vec4(depth); -#endif -} |