   recalculating tangents
  I'm using the md5 mesh format to store skinned mesh animations in my game, the problem is I need to recalculate the tangent vectors every frame for every triangle in the mesh because for each new animation pose the different triangles could potentially be a different shape. I was wondering if there is any way around this?   
If you're dealing with skeletal animation, can't you just transform the tangents by the skinning matrix as is typically done with the normals? If you have some other kind of animation, things are indeed a bit trickier. I ran into this issue with my soft body physics tinkerings and I settled on just computing the tangent frame on the CPU for the time being (as per this) and the performance isn't too bad for reasonable vertex counts. What I did do is split the work across frames, alternating between computing the normals and the tangent/binormal each frame to cut back the overall load. I also ran some basic tests to use GPGPU to calculate everything and that's doable, but so far the results haven't been worth it considering the overhead and added complexity.Finally I got a tip that it might be possible to use the ddx and ddy shader intrinsics to compute the tangent on the GPU in one go while rendering. Reportedly the technique is described in one of the NVidia GPU Gems books and I know for a fact that they also use it in their water shader demo (google ocean.fx), but I haven't looked into this yet and I don't know if it works for arbitrary meshes. If you happen to make some progress on this, please do check back in with the results [smile];Quote:If you're dealing with skeletal animation, can't you just transform the tangents by the skinning matrix as is typically done with the normals?tangent is based on the position of the three points of a triangle as well as the texture coordinates. If the positions change then I need to re-calculate the tangent vector for this triangle.I think you mean a skeletal animation without vertex weights... then you can cache one tangent per triangle and simply transform this vector into world space using a simple matrix multiplication. I was actually going to use something similar to this so that I didn't need to re-calculate the tangents but I need vertex weights for certain character animations.;  I've just found a pretty good article on the thing I'm talking about here I've read through the code but I can't see how you can store an averaged tangent vector per vertex as you would with normals. Doesn't the tangent vector ( along with the normal and bitangent vectors) describe the 'texture' space for ONE specific triangle rather than all triangles sharing a vertex?And if anyone has read the code, I don't undertstand why they first skin the mesh using the original orientation of the joints (I think this is called bind space). then calculate the averaged normals and tangents and then store these in the weights relative to the joints. ;|[Edited by - staticVoid2 on May 12, 2010 5:34:38 AM];Quote:Original post by staticVoid2Doesn't the tangent vector ( along with the normal and bitangent vectors) describe the 'texture' space for ONE specific triangle rather than all triangles sharing a vertex?I think this holds for the tangent, but the tangent frame (ie the basis for the texture space coordinate system) is calculated per vertex by averaging the triangle tangents, much like the normal. Quoting from this:Quote:To find the tangent vectors for a single vertex, we average the tangents for all triangles sharing that vertex in a manner similar to the way in which vertex normals are commonly calculated. In the case that neighboring triangles have discontinuous texture mapping, vertices along the border are generally already duplicated since they have different mapping coordinates anyway. We do not average tangents from such triangles because the result would not accurately represent the orientation of the bump map for either triangle.Continuing on...Quote:And if anyone has read the code, I don't undertstand why they first skin the mesh using the original orientation of the joints (I think this is called bind space). then calculate the averaged normals and tangents and then store these in the weights relative to the joints. ;|Your 'here' link loops back to this thread, so I can't check out the code. I'm not sure what they're doing, but it sounds like they're just transforming the normals and tangents based on the combined (weighted) skin matrices. This is what I suggested earlier and it might just work for weighted skeletal animation. It's probably not the correct way to calculate the tangents, but if the triangles don't change too much, it might produce acceptable results at a neglible cost. ;  sorry, fixed the link;The entire source code is a bit daunting to go through, especially since I know next to nothing about OpenGL, so I can't help you there. From the code snippets on the website though they just seem to be doing what I've been rambling about, that is, just faking it by interpolating the per-vertex tangents as if they're normals:Quote:vec3 n = normalize (gl_NormalMatrix * gl_Normal);vec3 t = normalize (gl_NormalMatrix * tangent);vec3 b = cross (n, t);;  I don't understand what you mean by interpolating the normals.In the md5 format the weights store the actual positions of the vertices in bone space rather than specifying a skin offset matrix like the .X format does. This means you will have a certain amount of weights per vertex. I think the author of the code is calculating the 'bone space' orientation of the tangents just like the vertex positions so they can be skinned just like the position vectors can...void md5Object::generateNormalsAndTangents(){md5Mesh* currentMesh = NULL;for(int meshCounter=0; meshCounter < meshesCount ; meshCounter++){currentMesh = &meshes[meshCounter];// Calculate the vertex positions//float3* vertexFinalPosition = (float3*)malloc(currentMesh->verticesCount * sizeof(float3)); //memset(vertexFinalPosition,0,sizeof(float3));float3* vertexFinalPosition = (float3*)calloc(currentMesh->verticesCount,sizeof(float3)); md5Vertex* currentVertex = NULL;for(int verticesCounter=0 ; verticesCounter < currentMesh->verticesCount ; verticesCounter++){currentVertex = &currentMesh->vertices[verticesCounter];float3 finalVertex;for (int weightCounter = 0; weightCounter < currentVertex->weightCount; weightCounter++){md5Weight* weight = &currentMesh->weights[currentVertex->startWeightIndex + weightCounter];md5joint* joint= &joints[weight->jointIndex];float3 displacement = joint->orientation.rotatePoint(weight->position);float3 t = joint->position + displacement;finalVertex += t * weight->bias;//Also zero normal for later useweight->normal = float3();}vertexFinalPosition[verticesCounter] = finalVertex;}// We now have all the vertex in final position// Calculate the nornalsfloat3* normalsFinalPosition = (float3*)calloc(currentMesh->verticesCount, sizeof(float3)); //memset(normalsFinalPosition,0,sizeof(float3));float3* tangentFinalPosition = (float3*)calloc(currentMesh->verticesCount, sizeof(float3)); //memset(tangentFinalPosition,0,sizeof(float3));md5Face* currentFace = NULL;for(int facesCounter = 0; facesCounter < currentMesh->facesCount ; facesCounter++){currentFace = &currentMesh->faces[facesCounter];// Normal partfloat3 v1 = vertexFinalPosition[currentFace->point3Index] - vertexFinalPosition[currentFace->point1Index];float3 v2 = vertexFinalPosition[currentFace->point2Index] - vertexFinalPosition[currentFace->point1Index];float3 normal = v1.crossProduct(v2);normalsFinalPosition[currentFace->point1Index] += normal;normalsFinalPosition[currentFace->point2Index] += normal;normalsFinalPosition[currentFace->point3Index] += normal;// End normal part// The following part is from "Mathematic for 3D programming" by Eric Lengyel// Tangent partfloat2 st1 = currentMesh->vertices[currentFace->point3Index].textureCoor - currentMesh->vertices[currentFace->point1Index].textureCoor;float2 st2 = currentMesh->vertices[currentFace->point2Index].textureCoor - currentMesh->vertices[currentFace->point1Index].textureCoor;float coef = 1/ (st1.u * st2.v - st2.u * st1.v);float3 tangent;tangent.x = coef * ((v1.x * st2.v)  + (v2.x * -st1.v));tangent.y = coef * ((v1.y * st2.v)  + (v2.y * -st1.v));tangent.z = coef * ((v1.z * st2.v)  + (v2.z * -st1.v));tangentFinalPosition[currentFace->point1Index] += tangent;tangentFinalPosition[currentFace->point2Index] += tangent;tangentFinalPosition[currentFace->point3Index] += tangent;// End tangent part}// Normalize normals and tangentfor(int verticesCounter=0 ; verticesCounter < currentMesh->verticesCount ; verticesCounter++){normalsFinalPosition[verticesCounter].normalize();tangentFinalPosition[verticesCounter].normalize();}// Translating the normal orientation from object to joint space and Store normals inside weights, for(int verticesCounter=0 ; verticesCounter < currentMesh->verticesCount ; verticesCounter++){currentVertex = &currentMesh->vertices[verticesCounter];for (int weightCounter = 0; weightCounter < currentVertex->weightCount; weightCounter++){md5Weight* weight = &currentMesh->weights[currentVertex->startWeightIndex + weightCounter];md5joint* joint= &joints[weight->jointIndex];float3 jointSpaceNormal = joint->orientation.copyAndInverse().rotatePoint(normalsFinalPosition[verticesCounter]);weight->normal += jointSpaceNormal;float3 jointSpaceTangent = joint->orientation.copyAndInverse().rotatePoint(tangentFinalPosition[verticesCounter]);weight->tangent += jointSpaceTangent; }}// Normalize weight normalsfor (int weightCounter = 0; weightCounter < currentVertex->weightCount; weightCounter++){md5Weight* weight = &currentMesh->weights[currentVertex->startWeightIndex + weightCounter];weight->normal.normalize();weight->tangent.normalize();}free(vertexFinalPosition);free(normalsFinalPosition);free(tangentFinalPosition);}}The only thing I don't get is why the normalsFinalPosition and tangentsFinalPosition arrays are filled with the averaged normals and tangents for the initial pose of the model?[Edited by - staticVoid2 on May 12, 2010 11:23:50 AM];  I just found a site that describes this process a bit better than me:Quote:Precomputing normalsYou will probably need to compute normal vectors, for example for lighting. Here is how to compute them in order to get “weight normals”, like the weight positions (this method also works for tangents and bi-tangents):First, compute all model's vertex positions in bind-pose (using the bind-pose skeleton).Compute the vertex normals. You now have the normals in object space for the bind-pose skeleton.For each weight of a vertex, transform the vertex normal by the inverse joint's orientation quaternion of the weight. You now have the normal in joint's local space.Then when calculating the final vertex positions, you will be able to do the same for the normals, except you won't have to translate from the joint's position when converting from joint's local space to object space.