Как расчитать правильный Tangent Space?
Ниже представлены два варианта просчёта Tangent Space (Пространство касательных).
Вариант 1
Здесь
vPosition - позиции вершин
indexes - массив индексов
vVec3Tangent - массив тангентов
vVec3Binormal - массив бинормалей
vTexCoords[0] - массив текстурных координат
void DIndexedEntity::GenerateTangent(bool genBiNor) { if ( !VertexFormat->vec3tangent || !VertexFormat->texcoords || !indexCreated || !verticesCreated) return; D3DXVECTOR3 tangent, bitangent; DWORD v0, v1, v2; // ZeroMemory( vVec3Tangent, CountVertices*sizeof( D3DXVECTOR3) ); if ( genBiNor&&VertexFormat->vec3binormal) ZeroMemory( vVec3Binormal,CountVertices*sizeof( D3DXVECTOR3)); for( DWORD j=0; j<( DWORD)CountFaces; j+=3) { if ( !index32) { v0 = indexes[j]; v1 = indexes[j+1]; v2 = indexes[j+2]; }else{ v0 = indexes32[j]; v1 = indexes32[j+1]; v2 = indexes32[j+2]; } // D3DXVECTOR3 Q = vPosition[v2] - vPosition[v0]; D3DXVECTOR3 P = vPosition[v1] - vPosition[v0]; float s1 = vTexCoords[0][v1].x - vTexCoords[0][v0].x; float t1 = vTexCoords[0][v1].y - vTexCoords[0][v0].y; float s2 = vTexCoords[0][v2].x - vTexCoords[0][v0].x; float t2 = vTexCoords[0][v2].y - vTexCoords[0][v0].y; float tmp = 0.0f; if( fabs( s1*t2 - s2*t1) <= 0.0001f) { tmp = 1.0f; } else { tmp = 1.0f/( s1*t2 - s2*t1); } tangent.x = ( t1*Q.x - t2*P.x); tangent.y = ( t1*Q.y - t2*P.y); tangent.z = ( t1*Q.z - t2*P.z); tangent *= tmp; D3DXVec3Normalize ( &tangent, &tangent); vVec3Tangent[v0]+=tangent; vVec3Tangent[v1]+=tangent; vVec3Tangent[v2]+=tangent; // if ( genBiNor&&VertexFormat->vec3binormal) { bitangent.x = ( s1*Q.x - s2*P.x); bitangent.y = ( s1*Q.y - s2*P.y); bitangent.z = ( s1*Q.z - s2*P.z); bitangent *= tmp; D3DXVec3Normalize ( &bitangent, &bitangent); vVec3Binormal[v0]+=bitangent; vVec3Binormal[v1]+=bitangent; vVec3Binormal[v2]+=bitangent; } // } for( DWORD j=0; j<( DWORD)CountVertices; j++) { D3DXVec3Normalize( &vVec3Tangent[j],&vVec3Tangent[j]); if ( genBiNor&&VertexFormat->vec3binormal) D3DXVec3Normalize( &vVec3Binormal[j],&vVec3Binormal[j]); } needUpdate=true; }
Но даже так тангент-спейс не всегда расчитывается совсем правильно, лучше его загружать вместе с моделькой, сделанной в хорошем редакторе.
Вариант 2
Updated by Blew_zc:
void CalcTangentBasis(const vector3D &p1, const vector3D &p2, const vector3D &p3, const vector2D &t1, const vector2D &t2, const vector2D &t3, vector3D &tangent, vector3D &binormal ) { vector3D e1 = p2 - p1; vector3D e2 = p3 - p1; vector2D et1 = t2 - t1; vector2D et2 = t3 - t1; tangent = 0.0f; binormal = 0.0f; float tmp = 0.0; if ( fabsf( et1.x*et2.y - et1.y*et2.x)<0.0001f) tmp = 1.0f; else tmp = 1.0 / ( et1.x*et2.y - et1.y*et2.x); tangent = ( e1 * et2.y - e2 * et1.y) * tmp; binormal = ( e2 * et1.x - e1 * et2.x) * tmp; tangent.normalize( ); binormal.normalize( ); } void CMeshes::CalcTB ( ) { for ( unsigned int i=0;i<numvertices;i++) { Vertices [i].tangent = 0.0; Vertices [i].binormal = 0.0; } for ( unsigned int i=0;i<( numindices / 3);i++) { unsigned int a = Indices [i * 3 + 0]; unsigned int b = Indices [i * 3 + 1]; unsigned int c = Indices [i * 3 + 2]; vector3D bin, tan; CalcTangentBasis ( Vertices [a].position, Vertices [b].position, Vertices [c].position, Vertices [a].tcoord, Vertices [b].tcoord, Vertices [c].tcoord, tan, bin); Vertices [a].tangent += tan; Vertices [b].tangent += tan; Vertices [c].tangent += tan; Vertices [a].binormal += bin; Vertices [b].binormal += bin; Vertices [c].binormal += bin; } for ( unsigned int i=0;i<numvertices;i++) { Vertices [i].tangent.normalize( ); Vertices [i].binormal.normalize( ); vector3D tmpT = Vertices [i].tangent; vector3D tmpB = Vertices [i].binormal; vector3D tmpN = Vertices [i].normal; vector3D newT = tmpT - ( ( tmpN & tmpT) * tmpN); vector3D newB = tmpB - ( ( tmpN & tmpB) * tmpN) - ( ( newT & tmpB)*newT); newT.normalize ( ); newB.normalize ( ); Vertices [i].tangent = newT; Vertices [i].binormal = newB; float lenT = newT.length ( ); float lenB = newB.length ( ); if ( lenT <= 0.0001 || lenB <= 0.0001) { if ( lenT > 0.5) Vertices [i].binormal = tmpN ^ Vertices [i].tangent; else if ( lenB > 0.5) Vertices [i].tangent = Vertices [i].binormal ^ tmpN; else { vector3D xAxis ( 1.0, 0.0, 0.0); vector3D yAxis ( 0.0, 1.0, 0.0); vector3D startAxis; if ( ( xAxis & tmpN) < ( yAxis & tmpN)) startAxis = xAxis; else startAxis = yAxis; Vertices [i].tangent = tmpN ^ startAxis; Vertices [i].binormal = tmpN ^ Vertices [i].tangent; } } else if ( ( Vertices [i].binormal & Vertices [i].tangent)> 0.9999) Vertices [i].binormal = tmpN ^ Vertices [i].tangent; } } // & - скалярное произведение // ^ - векторное произведение
12 февраля 2008 (Обновление: 22 сен 2009)