ПрограммированиеFAQГрафика

Как расчитать правильный Tangent Space?

Ниже представлены два варианта просчёта Tangent Space (Пространство касательных).

  Вариант 1
  Вариант 2

Вариант 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)

Комментарии [70]