# Tangent space under deformations?

This topic is 3955 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

## Recommended Posts

I want to deform an object in real time, and recomputing all the tangent vectors from scratch would be very expensive. What's the most efficient way to handle existing normals and tangents when deforming an object?

##### Share on other sites
If you are using matrices to deform your vertices, you can use the same matrices to deform your normals and tangents.

To transform a point / vector by a matrix, you multiply a 4x4 matrix by the 1x4 point / vector. The point / vector is usually in the format <x,y,z,w>, where w = 1. For the normals and tangents, simply change the w to 0, then perform the same matrix multiplication.

Hope this helps!

##### Share on other sites
Well, matrix transforms are things like moving, rotating, and scaling, which are not what "deform" means in general. I'm talking about things like bending, twisting, etc. You cannot represent these with matrices since they're not linear. What concerns me is that I can't assume that I can just apply the same general transform to normals/tangents as to vertices. This even fails with some of the matrix transforms: if you have non-uniform scaling, then it is incorrect to scale the normals--they need to be transformed by the transpose of the inverse transform matrix.
From http://www.faqs.org/faqs/graphics/algorithms-faq/
"When the model-to-world point transform is affine, the proper way to transform normals is with the transpose of the inverse of L."
And even that's not enough for the matrix transforms--normals may need renormalizing if there was scaling, and reflection reverses the inside/out. So what about more general transforms? That page says
"When a complicated distortion is used, it must be approximated differently at each point in space by a linear transform made up of partial derivatives, the Jacobian. The matrix for the Jacobian replaces L in the equation for transforming normals."
It's not clear to me how that can be implemented efficiently. Computing the partial derivatives per vertex is not too bad, but then the matrix needs to be inverted. So, what do I do?

##### Share on other sites
Quote:
 Original post by klaymore142If you are using matrices to deform your vertices, you can use the same matrices to deform your normals and tangents.

Careful. This only works for normals if your transforms are orthogonal (meaning no scale and perpendicular transforms to perpendicular). Otherwise, normals need to be transformed by the inverse transpose of the matrix used to transform points and tangents.

##### Share on other sites
I already said that. It still doesn't answer my question--I can't even use matrices because I'm doing bending, which is a nonlinear transform. How do I compute the normal/tangent transforms in that case, when the standard solution given on the Web (which I also mentioned) is unusable for realtime?

##### Share on other sites
If you're deforming without matrices then I'm assuming you're going and actually touching vertices, right? If you're doing that already, then you may as well update the tangent-space basis at that vertex when you're done (or, alternatively, keep a "dirty" list of vertices you touched during the deformation, and after the deformation is complete, re-visit them to "clean up" their tangent space basis). This shouldn't be too bad (hell, you're already deforming geometry, what's a few normal computations?), *PROVIDED* you've kept a pre-computed adjacency structure so that you can hop from a vertex to it's adjacent faces in constant time.

##### Share on other sites
Quote:
 Original post by PruneI already said that. It still doesn't answer my question--I can't even use matrices because I'm doing bending, which is a nonlinear transform. How do I compute the normal/tangent transforms in that case, when the standard solution given on the Web (which I also mentioned) is unusable for realtime?

There are 2 methods for non linear deformations.

1. just calculate the tangent basis at the end, using the solution you know, and no it's not too slow for real time usage.

2. Use the jacobian matrix of the deformation equation to determine the change in surface at a given point. This matrix can then be used to compute the change of the tangent basis (there is a paper on this in one of the game programming gems books, though i forget which).

The problem with 2, is that it's an approximation, so you still get visual artifacts. The other thing to bear in mind is that if you chain a number of surface deformations together, the cost of deforming the tangents and normals can actually start to become fairly large. My preference is towards simply calculating the tangent basis every surface update, simply because it

a) simplifies your deformation code dramatically (if you only deform points)
b) having done a, the deformation engine can work on any surface type (that has points - eg curves, lattices, particle systems etc)

The above only really applies though if you are writing the pipeline on the CPU. If you are performing the deformations in shaders, then you are probably better off using the jacobian, or just ignoring the normal deformation completely for non linear deformers.

##### Share on other sites
How is 1. fast enough for realtime? The code I'm using is from here:
http://www.terathon.com/code/tangent.php
This loops over every triangle and every vertex, and at least in the first loop, the calculation per triangle is significant (add to that a cross-product and normalization to get the normal at the vertices). I'm trying to have fish bending their bodies as they swim, and with a bunch of fish of medium complexity each, that is quite a lot of calculation to do per frame.

##### Share on other sites
if you're using bones and keyframes, can't you just store the tangents for the bindpose, and then interpolate them at runtime using quaternion representations of the bones?

maybe this code might help....

void	CMD5::PrepareMesh (const md5_mesh_t *mesh, const md5_joint_t *joints){  int i, j, k;	for (k = 0, i = 0; i < mesh->num_tris; ++i){		for (j = 0; j < 3; ++j, ++k)			vertexIndices[k] = mesh->triangles.index[j];	}	for (i = 0; i < mesh->num_verts; ++i){		vec3_t finalVertex	= { 0.0f, 0.0f, 0.0f };		vec3_t finalNormal	= { 0.0f, 0.0f, 0.0f };		vec3_t finalTangent = { 0.0f, 0.0f, 0.0f };		vec3_t finalBinormal = { 0.0f, 0.0f, 0.0f };		for (j = 0; j < mesh->vertices.count; ++j){			const md5_weight_t *weight = &mesh->weights[mesh->vertices.start + j];			if(joints){				const md5_joint_t *joint = &joints[weight->joint];				// Calculate transformed vertex for this weight				vec3_t wv;				Quat_rotatePoint (joint->orient, weight->pos, wv);				finalVertex[0] += (joint->pos[0] + wv[0]) * weight->bias;				finalVertex[1] += (joint->pos[1] + wv[1]) * weight->bias;				finalVertex[2] += (joint->pos[2] + wv[2]) * weight->bias;				// Calculate transformed normal for this weight				quat4_t inv;				vec3_t wn;				Quat_invert( joint->orient, inv ); 				Quat_rotatePoint (joint->orient, weight->norm, wn);				finalNormal[0] += (wn[0]) * weight->bias;				finalNormal[1] += (wn[1]) * weight->bias;				finalNormal[2] += (wn[2]) * weight->bias;					vec3_t wt;				Quat_rotatePoint (inv, weight->tangent, wt);				finalTangent[0] += (wt[0]) * weight->bias;				finalTangent[1] += (wt[1]) * weight->bias;				finalTangent[2] += (wt[2]) * weight->bias;								vec3_t wb;				Quat_rotatePoint (inv, weight->bitangent, wb);				finalBinormal[0] += (wb[0]) * weight->bias;				finalBinormal[1] += (wb[1]) * weight->bias;				finalBinormal[2] += (wb[2]) * weight->bias;					}			texcoordArray[0] = mesh->vertices.st[0];			texcoordArray[1] = mesh->vertices.st[1];		}		vertexArray[0] = finalVertex[0];		vertexArray[1] = finalVertex[1];		vertexArray[2] = finalVertex[2];		CVector3 n	=	CVector3( finalNormal[0], finalNormal[1], finalNormal[2] );		n.normalize();		CVector3 t	=	CVector3( finalTangent[0], finalTangent[1], finalTangent[2] );		t.normalize();		CVector3 b	=	CVector3( finalBinormal[0], finalBinormal[1], finalBinormal[2] );		b.normalize();		normalArray[0] = n.x;		normalArray[1] = n.y;		normalArray[2] = n.z;		tangentArray[0] = t.x;		tangentArray[1] = t.y;		tangentArray[2] = t.z;		binormalArray[0] = b.x;		binormalArray[1] = b.y;		binormalArray[2] = b.z;	}}

##### Share on other sites
I'm guessing RobTheBloke means calculating the tangent/bitangent in your vert shader using the normal and texcoords.

• ### What is your GameDev Story?

In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

• 13
• 9
• 9
• 15
• 14
• ### Forum Statistics

• Total Topics
634071
• Total Posts
3015336
×