Skip to main content

KERNEL_SKINNING

Constant KERNEL_SKINNING 

Source
pub const KERNEL_SKINNING: &str = r#"
// GPU skinning kernel
// Transforms vertices by a weighted sum of bone matrices.

layout(local_size_x = 256) in;

struct Vertex {
    vec4 position;     // xyz = position, w = 1
    vec4 normal;       // xyz = normal, w = 0
    vec4 tangent;      // xyz = tangent, w = handedness
    vec4 bone_weights; // up to 4 bone weights
    uvec4 bone_indices; // up to 4 bone indices
};

struct SkinnedVertex {
    vec4 position;
    vec4 normal;
    vec4 tangent;
    vec4 _reserved;
};

layout(std430, binding = 0) readonly buffer VerticesIn {
    Vertex vertices_in[];
};

layout(std430, binding = 1) writeonly buffer VerticesOut {
    SkinnedVertex vertices_out[];
};

layout(std430, binding = 2) readonly buffer BoneMatrices {
    mat4 bones[];
};

layout(std430, binding = 3) readonly buffer InverseBindMatrices {
    mat4 inv_bind[];
};

uniform uint u_vertex_count;
uniform uint u_bone_count;
uniform uint u_max_bones_per_vertex;

mat4 get_skin_matrix(uvec4 indices, vec4 weights) {
    mat4 skin = mat4(0.0);

    // Bone 0
    if (weights.x > 0.0 && indices.x < u_bone_count) {
        skin += weights.x * (bones[indices.x] * inv_bind[indices.x]);
    }

    // Bone 1
    if (u_max_bones_per_vertex > 1u && weights.y > 0.0 && indices.y < u_bone_count) {
        skin += weights.y * (bones[indices.y] * inv_bind[indices.y]);
    }

    // Bone 2
    if (u_max_bones_per_vertex > 2u && weights.z > 0.0 && indices.z < u_bone_count) {
        skin += weights.z * (bones[indices.z] * inv_bind[indices.z]);
    }

    // Bone 3
    if (u_max_bones_per_vertex > 3u && weights.w > 0.0 && indices.w < u_bone_count) {
        skin += weights.w * (bones[indices.w] * inv_bind[indices.w]);
    }

    return skin;
}

void main() {
    uint idx = gl_GlobalInvocationID.x;
    if (idx >= u_vertex_count) return;

    Vertex v = vertices_in[idx];
    mat4 skin = get_skin_matrix(v.bone_indices, v.bone_weights);

    // If no bones affect this vertex, use identity
    if (v.bone_weights.x + v.bone_weights.y + v.bone_weights.z + v.bone_weights.w < 0.001) {
        skin = mat4(1.0);
    }

    vec4 skinned_pos = skin * v.position;
    vec3 skinned_normal = normalize(mat3(skin) * v.normal.xyz);
    vec3 skinned_tangent = normalize(mat3(skin) * v.tangent.xyz);

    vertices_out[idx].position = skinned_pos;
    vertices_out[idx].normal = vec4(skinned_normal, 0.0);
    vertices_out[idx].tangent = vec4(skinned_tangent, v.tangent.w);
}
"#;
Expand description

Skeletal skinning kernel: apply bone matrix palette transforms.