Search code examples
openglinterpolationskinning

Interpolating Skinning Weights


I am subdividing the triangles of mesh, and as you can guess, I need weight values for these new vertices. Currently I am using linear interpolation (Vnew.weight[i] = (V1.weight[i] + V2.weight[i]) * 0.5) but, it seems like I cannot get correct values.

Do you know a better solution for using interpolating the weights?

Edit:

Right know, I am using LBS, and dividing one triangles into two triangles by taking halfway point. This division is done as soon as triangle information is read from the file (I am using SMD files).

I think the problem is weights because, in the rest pose (Without any skinning) everything is fine. But when the poses are started to apply, and skinning is done, some crazy triangles, and holes appear. And when I looked closely to these "crazy triangles", their vertices are moving with the mesh but, not fast enough with other vertices.

And here is the code of division process, and interpolating vertices, normals, UVs, and weights

int pi=0;
    while (1)
    {
        // Some control, and decleration

        for (int w = 0; w < 3; w++)
        {
            // Some declerations
            Vert v;

            // Values are read from file into Cert v

            // Using boneIndex2[] and boneWeight2[] because GLSL 1.30 does
            // not support shader storage buffer object, and I need just
            // 8 indices most for now.

            v.boneIndex2[0] = 0;
            v.boneIndex2[1] = 0;
            v.boneIndex2[2] = 0;
            v.boneIndex2[3] = 0;

            v.boneWeight2[0] = 0;
            v.boneWeight2[1] = 0;
            v.boneWeight2[2] = 0;
            v.boneWeight2[3] = 0;

            m.vert.push_back(v);

            pi++;
        }

    // Dividing the triangle

    Vert a = m.vert[pi - 2];
    Vert b = m.vert[pi - 1];
    Vert v;

    // Interpolate position
    v.pos[0] = (a.pos[0] + b.pos[0]) / 2;
    v.pos[1] = (a.pos[1] + b.pos[1]) / 2;
    v.pos[2] = (a.pos[2] + b.pos[2]) / 2;

    // Interpolate normal
    v.norm[0] = (a.norm[0] + b.norm[0]) / 2;
    v.norm[1] = (a.norm[1] + b.norm[1]) / 2;
    v.norm[2] = (a.norm[2] + b.norm[2]) / 2;

    // Interpolate UV
    v.uv[0] = (a.uv[0] + b.uv[0]) / 2;
    v.uv[1] = (a.uv[1] + b.uv[1]) / 2;

    // Assign bone indices
    // The new vertex should be treated by each bone of Vert a, and b
    v.boneIndex[0] = a.boneIndex[0];
    v.boneIndex[1] = a.boneIndex[1];
    v.boneIndex[2] = a.boneIndex[2];
    v.boneIndex[3] = a.boneIndex[3];

    v.boneIndex2[0] = b.boneIndex[0];
    v.boneIndex2[1] = b.boneIndex[1];
    v.boneIndex2[2] = b.boneIndex[2];
    v.boneIndex2[3] = b.boneIndex[3];

    // Interpolate weights
    float we[4];
    we[0] = (a.boneWeight[0] + b.boneWeight[0]) / 2;
    we[1] = (a.boneWeight[1] + b.boneWeight[1]) / 2;
    we[2] = (a.boneWeight[2] + b.boneWeight[2]) / 2;
    we[3] = (a.boneWeight[3] + b.boneWeight[3]) / 2;

    // Assign weights
    v.boneWeight[0] = we[0];
    v.boneWeight[1] = we[1];
    v.boneWeight[2] = we[2];
    v.boneWeight[3] = we[3];

    v.boneWeight2[0] = we[0];
    v.boneWeight2[1] = we[1];
    v.boneWeight2[2] = we[2];
    v.boneWeight2[3] = we[3];

    // Push new vertex
    m.vert.push_back(v);
    pi++;

    // Push new faces
    m.face.push_back(Face(pi - 4, pi - 1, pi - 2));
    m.face.push_back(Face(pi - 4, pi - 3, pi - 1));

}   // End of while(1)

Solution

  • You are blending weights that just might belong to different bones (i.e. if the bone indices are not equal).

    Instead, gather all influencing bone indices from the two vertices. If only one vertex refers to any bone, use half of this weight. If both vertices refer to the bone, use the interpolation as you already did. Then, pick the four bones with the highest weights and re-normalize to a sum of 1.

    Here is an example. Consider you have two vertices with these bone indices and weights:

         v1                 v2  
    index | weight     index | weight
    ------+--------    ------+--------
      0   |  0.2         2   |  0.1
      1   |  0.5         3   |  0.6
      2   |  0.1         4   |  0.2
      3   |  0.2         5   |  0.1
    

    You would start by building the table of joint weights:

    index | weight  
    ------+-------- 
      0   |  0.2 / 2 = 0.1    
      1   |  0.5 / 2 = 0.25
      2   |  (0.1 + 0.1) / 2 = 0.1    
      3   |  (0.2 + 0.6) / 2 = 0.4
      4   |  0.2 / 2 = 0.1   
      5   |  0.1 / 2 = 0.05
    

    Sort wrt weight and pick the four greatest:

    index | weight  
    ------+-------- 
      3   |  0.4  *
      1   |  0.25 *
      0   |  0.1  *
      2   |  0.1  * 
      4   |  0.1   
      5   |  0.05
    

    These weights sum to 0.85. So divide the weights by 0.85 to get the final weights and indices:

    index | weight  
    ------+-------- 
      3   |  0.47
      1   |  0.29
      0   |  0.12
      2   |  0.12
    

    The other option would be to extend your structure to use more (either static eight or dynamically) bones. But it's probably not worth the effort.