Computing normals for weighted vertices (vertex skinning)

Started by
4 comments, last by V3rt3x 18 years, 10 months ago
Hello, I'm wondering how to compute vertex normals when using vertex skinning? I would like to pre-compute normals from vertex weights in order to get final vertex normals like I get final vertices:
for( i = 0; i < _numVerts; ++i ) {
  Vector3f finalVertex = kZeroVectorf;
  Vector3f finalNormal = kZeroVectorf;

  // Calculate final vertex to draw with weights
  for( int j = 0; j < _verts->countWeight; ++j ) {
    const Weight_t *pWeight = _weights[ _verts->startWeight + j ];
    const Joint_t *pJoint = skel->getJoint( pWeight->joint );

    // Calculate transformed vertex for this weight
    Vector3f wv = pWeight->pos;
    pJoint->orient.rotate( wv );

    // The sum of all pWeight->bias should be 1.0
    finalVertex += (pJoint->pos + wv) * pWeight->bias;

    Vector3f wn = pWeight->norm;
    pJoint->orient.rotate( wn );

    finalNormal += wn * pWeight->bias;
  }

  _vertexArray[0] = finalVertex._x;
  _vertexArray[1] = finalVertex._y;
  _vertexArray[2] = finalVertex._z;

  _normalArray[0] = finalNormal._x;
  _normalArray[1] = finalNormal._y;
  _normalArray[2] = finalNormal._z;
}
What I want is to compute pWeight->norm, which is the weighted normal associated to a weighted vertex, preferably at loading time. Perhaps I'm wrong and this is not a good idea... But then I would like to know how to compute these normals without having to rebuild all triangle normals at rendering time, then averaging them for vertices, etc.
Advertisement
Hey V3rt3x,

I think you're going about it the wrong way. You only need to compute the normals for each vertex in its bind pose. When you multiple the normal by the bones final combined transformation it will transform the normal to the correct space.


So just use the models original normals with all the frames original transforms.
The bind pose? is it that the bind pose?

I'm using the MD5 model format, but vertices have ”multiple positions” stored separately with the weight factor and joint index (in order to get access to quaternion orientation and joint's position).

struct Md5Vertex_t{  float st[2]; // Texture coordinates  int startWeight; // Start index weights  int countWeight; // Number of weights};struct Md5Weight_t{  int joint; // Joint index  float bias; // Weight factor  Vector3f pos;  Vector3f norm; // what I would like in my dreams (o_o)};


So if I want to compute normals I need to use a Skeleton because a vertex weights give a position depending on their joint...

I'm completely lost with theses weight positions... In books I have the position is the same but there are multiple transformation matrices (one for each bone). Here we have multiple positions and multiple matrices :(
I got the solution at doom3world :-)

http://www.doom3world.org/phpbb2/viewtopic.php?p=100174#100020

Problem solved.
Well, there are 2 good ways to do it.

You can apply the rotation of the different bones to the normal (not the translation!) and scale by the respective weights, BUT you're not going to have a unit-length normal after this step so you'll need to normalize.

Or, a lot of ps2 games do this:
Just transform the normal by the most heavily weighted bone. If there isn't scaling on the bone (which is true 99% of the time), you don't need to re-normalize the normal, so in many cases this is super cheap to do and there isn't significant visual difference. It's especially easy if you use the convention that the most heavily weighted bone is stored as the first weight/idx.

I opted for the first method you have described.

This topic is closed to new replies.

Advertisement