11 ComputeTangentFrame
Chuck Walbourn редактировал(а) эту страницу 2022-01-20 17:34:24 -08:00
DirectXMesh

Generates per-vertex tangent and bi-tangent information

HRESULT ComputeTangentFrame(
   const uint16_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT3* tangents, XMFLOAT3* bitangents );

HRESULT ComputeTangentFrame(
   const uint16_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT4* tangents, XMFLOAT3* bitangents );

HRESULT ComputeTangentFrame(
   const uint16_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT4* tangents );
HRESULT ComputeTangentFrame(
   const uint32_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT3* tangents, XMFLOAT3* bitangents );

HRESULT ComputeTangentFrame(
   const uint32_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT4* tangents, XMFLOAT3* bitangents );

HRESULT ComputeTangentFrame(
   const uint32_t* indices, size_t nFaces,
   const XMFLOAT3* positions, const XMFLOAT3* normals,
   const XMFLOAT2* texcoords, size_t nVerts,
   XMFLOAT4* tangents );

Parameters

The normals parameter can be computed from the mesh indices and vertex positions using ComputeNormals.

The tangents can be optionally returned as a 4-vector where the .w component indicates 'handedness' which allows for an easy reconstruction of the bi-tangent in the shader.

float3 normal : NORMAL0
float4 tangent : TANGENT0

...

float3 tan1 = tangent.xyz;
float3 tan2 = cross( normal, tangent.xyz ) * tangent.w;

In the overloads that take both tangents and bitangents, either but not both can be nullptr.

Remark

Note that bi-normal is another common term used for bi-tangent, although technically a bi-normal only applies in 2D and not 3D. The Direct3D HLSL semantic name for the bi-tangent is BINORMAL.

Due to the C++ overload resolution rules, if you want to call ComputeTangentFrame for only the binormal output and not the tangent output, you need to use an explicit cast on the nullptr parameter:

auto bitangents = std::make_unique<XMFLOAT3[]>(nVerts);

if ( FAILED( ComputeTangentFrame( mesh->indices.data(), nFaces,
   pos.get(), normals.get(), texcoords.get(), nVerts,
   static_cast<XMFLOAT3*>(nullptr), bitangents.get() ) ) )
   // Error

Example

auto mesh = std::make_unique<WaveFrontReader<uint16_t>>();

if ( FAILED( mesh->Load( L"test.obj" ) ) )
   // Error

if ( mesh->hasNormals )
   // Skip next computation

size_t nFaces = mesh->indices.size() / 3;
size_t nVerts = mesh->vertices.size();

auto pos = std::make_unique<XMFLOAT3[]>(nVerts);
for( size_t j = 0; j < nVerts; ++j )
   pos[ j ] = mesh->vertices[ j ].position;

auto normals = std::make_unique<XMFLOAT3[]>(nVerts);
if ( FAILED( ComputeNormals( mesh->indices.data(), nFaces,
   pos.get(), nVerts, CNORM_DEFAULT, normals.get() ) ) )
   // Error

if ( !mesh->hasTexcoords )
   // Skip next computation

auto texcoords = std::make_unique<XMFLOAT2[]>(nVerts);
for( size_t j = 0; j < nVerts; ++j )
   texcoords[ j ] = mesh->vertices[ j ].textureCoordinate;

auto tangents = std::make_unique<XMFLOAT3[]>(nVerts);
auto bitangents = std::make_unique<XMFLOAT3[]>(nVerts);

if ( FAILED( ComputeTangentFrame( mesh->indices.data(), nFaces,
   pos.get(), normals.get(), texcoords.get(), nVerts,
   tangents.get(), bitangents.get() ) ) )
   // Error

Further Reading

Lengyel, Eric. "7.5 Tangent Space", Foundations of Game Engine Development: Volume 2 - Rendering, Terathon Software LLC (2019) link

Mittring, Martin. "Triangle Mesh Tangent Space Calculation". Shader X^4 Advanced Rendering Techniques, 2006