Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Applying weights to matrixes and vertices (bone rotation)

Tags:

I'm rotating the bones of a skeleton inside a mesh for a low poly 3D figure. On the vertex shader its applied like this.
glsl:

    vec4 vert1 = (bone_matrix[index1]*vertex_in)*weight;     vec4 vert2 = (bone_matrix[index2]*vertex_in)*(1-weight);     gl_Position =  vert1+vert2; 

bone_matrix[index1] is the matrix of one bone and bone_matrix[index2] is the matrix of the other. weight designates vertex_in's membership to these bones. The problem is the closer the weight is to .5, the more the diameter of the elbow shrinks when a rotation is applied. I've tested it with around a 10,000 vertex cylinder shape (with a gradient of weights). The result looked like bending a garden hose.

I got my weighting method from these sources. Its actually the only way I could find:
http://www.opengl.org/wiki/Skeletal_Animation
http://ogldev.atspace.co.uk/www/tutorial38/tutorial38.html
http://blenderecia.orgfree.com/blender/skinning_proposal.pdf

initial_ugly_good

The left is how the shape starts, the middle is how the above equation rotates it, and the right is my goal. The mid points are weighted 0.5. It only gets worse the more bent it is, at 180 degrees it has zero diameter.

  • I've tried assembling the matrix on the shader, so that I can apply the weights to the rotation instead of the resulting vertices. It looks perfect like the one in picture on the right, but it requires assembling the matrix for every single vertex (expensive)
  • I've looked into quaternions, but glsl doesn't natively support them (correct me if I'm wrong) and they're confusing. Is that what I need to do?
  • I've considered having three bones per joint, and add a "kneecap" between every bone. This wouldn't eliminate the problem but would mitigate it.
  • I'm considering projecting the vertex its original distance from the axis after they're rotated. This would fail at 180 degrees but would be (relatively) cheap.

So considering the options, or other options that I may not have considered, How have others avoid this pinching effect?

EDIT: I've gotten SLERP to work using quaternions but I opted not to use it as GLSL does not natively support it. I couldn't get the geometric SLERP to work as described by Tom. I got NLERP working for the first 90 degrees, so I added an extra "bone" between each joint. So to bend the forearm 40 degrees I bend the elbow and the forearm by 20 degrees each. This eliminates the pinching effect at the expense of doubling the quantity of bones which is not an ideal solution.

like image 547
gunfulker Avatar asked Aug 25 '14 07:08

gunfulker


People also ask

What are bone weights?

Abstract. Skeletal weight and/or weight of the different bones of the human skeleton are currently used in a wide range of applications such as archaeological cremations and forensics. Still, few reference values are available that compare the mean weights for the different skeletal parts.

How do you do matrix rotations?

Find the coordinates of the vertices of the image ΔXYZ with X(1,2),Y(3,5) and Z(−3,4) after it is rotated 180° counterclockwise about the origin. Write the ordered pairs as a vertex matrix. To rotate the ΔXYZ 180° counterclockwise about the origin, multiply the vertex matrix by the rotation matrix, [−100−1] .

Why do we use rotation matrix?

Since matrix multiplication has no effect on the zero vector (the coordinates of the origin), rotation matrices describe rotations about the origin. Rotation matrices provide an algebraic description of such rotations, and are used extensively for computations in geometry, physics, and computer graphics.

What is a skinning matrix?

Linear blend skinning is the idea of transforming vertices inside a single mesh by a (blend) of multiple transforms. Each transform is the concatenation of a "bind matrix" that takes the vertex into the local space of a given "bone" and a transformation matrix that moves from that bone's local space to a new position.


1 Answers

Disclaimer : I'm not a lot of a 3D guy, so I'll just suggest you a mathematical approach that may help you.

First of all, let me put this little schema, this way we'll be sure we are all talking about the same thing :

enter image description here

The blue and green figures are the original bones, rotated completely with either bone_matrix[index1] or bone_matrix[index2]. The red dot is the center of rotation, the orange figure is what you want and the black one is what you have.

So, you figure being build as a weighted average of the blue and green ones, on this drawing we see (thanks to the gray lines), why it shrinks like that.

You need to somehow compensate for this shrinking, I would suggest scaling back the points from your center of rotation, we need a scaling of value 2 at the junction between the bones, and of value 1 at the extremities.

Let scale_matrix be a pre-computed matrix : a scaling of amplitude 2 centered at your center of rotation (red dot).

You end up with this shader :

vec4 vert1 = (bone_matrix[index1]*vertex_in)*weight; vec4 vert2 = (bone_matrix[index2]*vertex_in)*(1-weight); vec4 inter =  vert1+vert2; vec4 scaled1 = inter*(1-2*min(weight, 1-weight)); vec4 scaled2 = (scale_matrix*inter)*(2*min(weight, 1-weight)); gl_Position =  scaled1+scaled2; 

I'm afraid I can't test it right now (I don't know a lot about GLSL), but I think you'll be able to adapt it to your case if something doesn't fit.

like image 161
Levans Avatar answered Sep 18 '22 16:09

Levans