diff options
author | clayjohn <claynjohn@gmail.com> | 2022-11-25 15:30:36 -0800 |
---|---|---|
committer | clayjohn <claynjohn@gmail.com> | 2022-11-29 09:45:03 -0800 |
commit | f33ffd9ab4948e0fa649de9bbe490a1d4f977691 (patch) | |
tree | d3b2dc0307fcae13eb6f027216aac949e85dba6f /drivers/gles3/shaders | |
parent | 55dae301777ba609c3255026f9a9852a0dbfd43b (diff) |
Add Skeletons and Blend Shapes to the OpenGL renderer
This uses a similar multipass approach to blend shapes
as Godot 3.x, the major difference here is that we
need to convert the normals and tangents to octahedral
for rendering.
Skeletons work the same as the Vulkan renderer except the bones
are stored in a texture as they were in 3.x.
Diffstat (limited to 'drivers/gles3/shaders')
-rw-r--r-- | drivers/gles3/shaders/SCsub | 1 | ||||
-rw-r--r-- | drivers/gles3/shaders/canvas.glsl | 9 | ||||
-rw-r--r-- | drivers/gles3/shaders/skeleton.glsl | 269 |
3 files changed, 270 insertions, 9 deletions
diff --git a/drivers/gles3/shaders/SCsub b/drivers/gles3/shaders/SCsub index 2686b1aa48..34713e7e29 100644 --- a/drivers/gles3/shaders/SCsub +++ b/drivers/gles3/shaders/SCsub @@ -21,3 +21,4 @@ if "GLES3_GLSL" in env["BUILDERS"]: env.GLES3_GLSL("canvas_sdf.glsl") env.GLES3_GLSL("particles.glsl") env.GLES3_GLSL("particles_copy.glsl") + env.GLES3_GLSL("skeleton.glsl") diff --git a/drivers/gles3/shaders/canvas.glsl b/drivers/gles3/shaders/canvas.glsl index cdae05a516..60139de472 100644 --- a/drivers/gles3/shaders/canvas.glsl +++ b/drivers/gles3/shaders/canvas.glsl @@ -19,9 +19,6 @@ layout(location = 0) in vec2 vertex_attrib; layout(location = 3) in vec4 color_attrib; layout(location = 4) in vec2 uv_attrib; -layout(location = 10) in uvec4 bone_attrib; -layout(location = 11) in vec4 weight_attrib; - #ifdef USE_INSTANCING layout(location = 1) in highp vec4 instance_xform0; @@ -81,8 +78,6 @@ void main() { uv = draw_data[draw_data_instance].uv_c; color = vec4(unpackHalf2x16(draw_data[draw_data_instance].color_c_rg), unpackHalf2x16(draw_data[draw_data_instance].color_c_ba)); } - uvec4 bones = uvec4(0, 0, 0, 0); - vec4 bone_weights = vec4(0.0); #elif defined(USE_ATTRIBUTES) draw_data_instance = gl_InstanceID; @@ -93,9 +88,6 @@ void main() { vec4 color = color_attrib * draw_data[draw_data_instance].modulation; vec2 uv = uv_attrib; - uvec4 bones = bone_attrib; - vec4 bone_weights = weight_attrib; - #ifdef USE_INSTANCING vec4 instance_color = vec4(unpackHalf2x16(instance_color_custom_data.x), unpackHalf2x16(instance_color_custom_data.y)); color *= instance_color; @@ -110,7 +102,6 @@ void main() { vec2 uv = draw_data[draw_data_instance].src_rect.xy + abs(draw_data[draw_data_instance].src_rect.zw) * ((draw_data[draw_data_instance].flags & FLAGS_TRANSPOSE_RECT) != uint(0) ? vertex_base.yx : vertex_base.xy); vec4 color = draw_data[draw_data_instance].modulation; vec2 vertex = draw_data[draw_data_instance].dst_rect.xy + abs(draw_data[draw_data_instance].dst_rect.zw) * mix(vertex_base, vec2(1.0, 1.0) - vertex_base, lessThan(draw_data[draw_data_instance].src_rect.zw, vec2(0.0, 0.0))); - uvec4 bones = uvec4(0, 0, 0, 0); #endif diff --git a/drivers/gles3/shaders/skeleton.glsl b/drivers/gles3/shaders/skeleton.glsl new file mode 100644 index 0000000000..a1e3c098f4 --- /dev/null +++ b/drivers/gles3/shaders/skeleton.glsl @@ -0,0 +1,269 @@ +/* clang-format off */ +#[modes] + +mode_base_pass = +mode_blend_pass = #define MODE_BLEND_PASS + +#[specializations] + +MODE_2D = true +USE_BLEND_SHAPES = false +USE_SKELETON = false +USE_NORMAL = false +USE_TANGENT = false +FINAL_PASS = false +USE_EIGHT_WEIGHTS = false + +#[vertex] + +#include "stdlib_inc.glsl" + +#ifdef MODE_2D +#define VFORMAT vec2 +#else +#define VFORMAT vec3 +#endif + +#ifdef FINAL_PASS +#define OFORMAT vec2 +#else +#define OFORMAT uvec2 +#endif + +// These come from the source mesh and the output from previous passes. +layout(location = 0) in highp VFORMAT in_vertex; +#ifdef MODE_BLEND_PASS +#ifdef USE_NORMAL +layout(location = 1) in highp uvec2 in_normal; +#endif +#ifdef USE_TANGENT +layout(location = 2) in highp uvec2 in_tangent; +#endif +#else // MODE_BLEND_PASS +#ifdef USE_NORMAL +layout(location = 1) in highp vec2 in_normal; +#endif +#ifdef USE_TANGENT +layout(location = 2) in highp vec2 in_tangent; +#endif +#endif // MODE_BLEND_PASS + +#ifdef USE_SKELETON +#ifdef USE_EIGHT_WEIGHTS +layout(location = 10) in highp uvec4 in_bone_attrib; +layout(location = 11) in highp uvec4 in_bone_attrib2; +layout(location = 12) in mediump vec4 in_weight_attrib; +layout(location = 13) in mediump vec4 in_weight_attrib2; +#else +layout(location = 10) in highp uvec4 in_bone_attrib; +layout(location = 11) in mediump vec4 in_weight_attrib; +#endif + +uniform mediump sampler2D skeleton_texture; // texunit:0 +#endif + +/* clang-format on */ +#ifdef MODE_BLEND_PASS +layout(location = 3) in highp VFORMAT blend_vertex; +#ifdef USE_NORMAL +layout(location = 4) in highp vec2 blend_normal; +#endif +#ifdef USE_TANGENT +layout(location = 5) in highp vec2 blend_tangent; +#endif +#endif // MODE_BLEND_PASS + +out highp VFORMAT out_vertex; //tfb: + +#ifdef USE_NORMAL +flat out highp OFORMAT out_normal; //tfb:USE_NORMAL +#endif +#ifdef USE_TANGENT +flat out highp OFORMAT out_tangent; //tfb:USE_TANGENT +#endif + +#ifdef USE_BLEND_SHAPES +uniform highp float blend_weight; +uniform lowp float blend_shape_count; +#endif + +vec2 signNotZero(vec2 v) { + return mix(vec2(-1.0), vec2(1.0), greaterThanEqual(v.xy, vec2(0.0))); +} + +vec3 oct_to_vec3(vec2 oct) { + oct = oct * 2.0 - 1.0; + vec3 v = vec3(oct.xy, 1.0 - abs(oct.x) - abs(oct.y)); + if (v.z < 0.0) { + v.xy = (1.0 - abs(v.yx)) * signNotZero(v.xy); + } + return normalize(v); +} + +vec2 vec3_to_oct(vec3 e) { + e /= abs(e.x) + abs(e.y) + abs(e.z); + vec2 oct = e.z >= 0.0f ? e.xy : (vec2(1.0f) - abs(e.yx)) * signNotZero(e.xy); + return oct * 0.5f + 0.5f; +} + +vec4 oct_to_tang(vec2 oct_sign_encoded) { + // Binormal sign encoded in y component + vec2 oct = vec2(oct_sign_encoded.x, abs(oct_sign_encoded.y) * 2.0 - 1.0); + return vec4(oct_to_vec3(oct), sign(oct_sign_encoded.y)); +} + +vec2 tang_to_oct(vec4 base) { + vec2 oct = vec3_to_oct(base.xyz); + // Encode binormal sign in y component + oct.y = oct.y * 0.5f + 0.5f; + oct.y = base.w >= 0.0f ? oct.y : 1.0 - oct.y; + return oct; +} + +// Our original input for normals and tangents is 2 16-bit floats. +// Transform Feedback has to write out 32-bits per channel. +// Octahedral compression requires normalized vectors, but we need to store +// non-normalized vectors until the very end. +// Therefore, we will compress our normals into 16 bits using signed-normalized +// fixed point precision. This works well, because we know that each normal +// is no larger than |1| so we can normalize by dividing by the number of blend +// shapes. +uvec2 vec4_to_vec2(vec4 p_vec) { + return uvec2(packSnorm2x16(p_vec.xy), packSnorm2x16(p_vec.zw)); +} + +vec4 vec2_to_vec4(uvec2 p_vec) { + return vec4(unpackSnorm2x16(p_vec.x), unpackSnorm2x16(p_vec.y)); +} + +void main() { +#ifdef MODE_2D + out_vertex = in_vertex; + +#ifdef USE_BLEND_SHAPES +#ifdef MODE_BLEND_PASS + out_vertex = in_vertex + blend_vertex * blend_weight; +#else + out_vertex = in_vertex * blend_weight; +#endif +#ifdef FINAL_PASS + out_vertex = normalize(out_vertex); +#endif +#endif // USE_BLEND_SHAPES + +#ifdef USE_SKELETON + +#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0) +#define GET_BONE_MATRIX(a, b, w) mat2x4(TEX(a), TEX(b)) * w + + uvec4 bones = in_bone_attrib * uvec4(2u); + uvec4 bones_a = bones + uvec4(1u); + + highp mat2x4 m = GET_BONE_MATRIX(bones.x, bones_a.x, in_weight_attrib.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, in_weight_attrib.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, in_weight_attrib.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, in_weight_attrib.w); + + mat4 bone_matrix = mat4(m[0], m[1], vec4(0.0, 0.0, 1.0, 0.0), vec4(0.0, 0.0, 0.0, 1.0)); + + //reverse order because its transposed + out_vertex = (vec4(out_vertex, 0.0, 1.0) * bone_matrix).xy; +#endif // USE_SKELETON + +#else // MODE_2D + +#ifdef USE_BLEND_SHAPES +#ifdef MODE_BLEND_PASS + out_vertex = in_vertex + blend_vertex * blend_weight; + +#ifdef USE_NORMAL + vec3 normal = vec2_to_vec4(in_normal).xyz * blend_shape_count; + vec3 normal_blend = oct_to_vec3(blend_normal) * blend_weight; +#ifdef FINAL_PASS + out_normal = vec3_to_oct(normalize(normal + normal_blend)); +#else + out_normal = vec4_to_vec2(vec4(normal + normal_blend, 0.0) / blend_shape_count); +#endif +#endif // USE_NORMAL + +#ifdef USE_TANGENT + vec4 tangent = vec2_to_vec4(in_tangent) * blend_shape_count; + vec4 tangent_blend = oct_to_tang(blend_tangent) * blend_weight; +#ifdef FINAL_PASS + out_tangent = tang_to_oct(vec4(normalize(tangent.xyz + tangent_blend.xyz), tangent.w)); +#else + out_tangent = vec4_to_vec2(vec4((tangent.xyz + tangent_blend.xyz) / blend_shape_count, tangent.w)); +#endif +#endif // USE_TANGENT + +#else // MODE_BLEND_PASS + out_vertex = in_vertex * blend_weight; + +#ifdef USE_NORMAL + vec3 normal = oct_to_vec3(in_normal); + out_normal = vec4_to_vec2(vec4(normal * blend_weight / blend_shape_count, 0.0)); +#endif +#ifdef USE_TANGENT + vec4 tangent = oct_to_tang(in_tangent); + out_tangent = vec4_to_vec2(vec4(tangent.rgb * blend_weight / blend_shape_count, tangent.w)); +#endif +#endif // MODE_BLEND_PASS +#else // USE_BLEND_SHAPES + + // Make attributes available to the skeleton shader if not written by blend shapes. + out_vertex = in_vertex; +#ifdef USE_NORMAL + out_normal = in_normal; +#endif +#ifdef USE_TANGENT + out_tangent = in_tangent; +#endif +#endif // USE_BLEND_SHAPES + +#ifdef USE_SKELETON + +#define TEX(m) texelFetch(skeleton_texture, ivec2(m % 256u, m / 256u), 0) +#define GET_BONE_MATRIX(a, b, c, w) mat4(TEX(a), TEX(b), TEX(c), vec4(0.0, 0.0, 0.0, 1.0)) * w + + uvec4 bones = in_bone_attrib * uvec4(3); + uvec4 bones_a = bones + uvec4(1); + uvec4 bones_b = bones + uvec4(2); + + highp mat4 m; + m = GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib.w); + +#ifdef USE_EIGHT_WEIGHTS + bones = in_bone_attrib2 * uvec4(3); + bones_a = bones + uvec4(1); + bones_b = bones + uvec4(2); + + m += GET_BONE_MATRIX(bones.x, bones_a.x, bones_b.x, in_weight_attrib2.x); + m += GET_BONE_MATRIX(bones.y, bones_a.y, bones_b.y, in_weight_attrib2.y); + m += GET_BONE_MATRIX(bones.z, bones_a.z, bones_b.z, in_weight_attrib2.z); + m += GET_BONE_MATRIX(bones.w, bones_a.w, bones_b.w, in_weight_attrib2.w); +#endif + + // Reverse order because its transposed. + out_vertex = (vec4(out_vertex, 1.0) * m).xyz; +#ifdef USE_NORMAL + vec3 vertex_normal = oct_to_vec3(out_normal); + out_normal = vec3_to_oct(normalize((vec4(vertex_normal, 0.0) * m).xyz)); +#endif // USE_NORMAL +#ifdef USE_TANGENT + vec4 vertex_tangent = oct_to_tang(out_tangent); + out_tangent = tang_to_oct(vec4(normalize((vec4(vertex_tangent.xyz, 0.0) * m).xyz), vertex_tangent.w)); +#endif // USE_TANGENT +#endif // USE_SKELETON +#endif // MODE_2D +} + +/* clang-format off */ +#[fragment] + +void main() { + +} +/* clang-format on */ |