(L) [2011/12/30] [ost
by spectral] [Tangents computation problem] Wayback!Finally not,
I'm still playing to find a correct way to compute good tangents... but no way... for each technique a model fail to be rendered correctly !
To test I use 3 models and a "tangent shader" (I can provide the obj or the blender model if someone is interested):
- Lego man
- RS8 model
- Cornell box with 2 spheres
I have use several techniques for now :
- Precomputed techniques : where I compute all the tangents once (preprocessing)
- Live computation
I have try to mix all theses methods, but no way... I can render all my models without problems !
If someone has more information about the right way of doing this... ??
Pre-computation
- Code from the assimp library : [LINK http://assimp.svn.sourceforge.net/viewvc/assimp/trunk/code/CalcTangentsProcess.cpp?revision=1027&view=markup]
- Code from [LINK http://www.terathon.com/code/tangent.html]
- Code from NVidia NVMeshMender.cc : [LINK http://www.koders.com/cpp/fid2F46C590DE141A7E8BECB3F4AE8038E1CBEFFED3.aspx]
Live computation methods : based on the N vector
Code: [LINK # Select all]void GetOrthonormalBasis(clVector3 N, clDualVector3* TnBiT)
{
    N = normalize(N);
    clVector3 up = (clVector3)(0.f, 1.f, 0.f);
   
    if (fabs(dot(up, N)) > 0.8f)
      up = (clVector3)(1.f, 0.f, 0.f);
   
    TnBiT->v1 = normalize(cross(up, N));
    TnBiT->v2 = normalize(cross(N, TnBiT->v1));
}
Code: [LINK # Select all]void GetOrthonormalBasis(const clVector3 v0, clDualVector3* dv)
{
    if (fabs(v0.x) > fabs(v0.y))
    {
        float invLen = 1.f / sqrt(v0.x * v0.x + v0.z * v0.z);
        dv->v1.x = -v0.z * invLen;
        dv->v1.y = 0.f;
        dv->v1.z = v0.x * invLen;
    }
    else
    {
        float invLen = 1.f / sqrt(v0.y * v0.y + v0.z * v0.z);
        dv->v1.x = 0.f;
        dv->v1.y = v0.z * invLen;
        dv->v1.z = -v0.y * invLen;
    }
    dv->v2 = cross(v0, dv->v1);
}
Code: [LINK # Select all]void GetOrthonormalBasis(clVector3 N, clDualVector3* TnBiT)
{
    // Reference:
    //
    //   Hughes, J. F., and Moller, T. Building an Orthonormal Basis from a Unit Vector.
    //   Journal of Graphics Tools 4, 4 (1999), 33-35.
    //   http://www.cs.brown.edu/research/pubs/pdfs/1999/Hughes-1999-BAO.pdf
    // Compute u so that it is orthogonal to n.
    if (fabs(N.x) < fabs(N.y))
    {
        if (fabs(N.x) < fabs(N.z))
        {
            // N.x is the smallest component.
            TnBiT->v1.x =  0.f;
            TnBiT->v1.y = -N.z;
            TnBiT->v1.z =  N.y;
        }
        else
        {
            // N.z is the smallest component.
            TnBiT->v1.x = -N.y;
            TnBiT->v1.y =  N.x;
            TnBiT->v1.z =  0.f;
        }
    }
    else
    {
        if (fabs(N.y) < fabs(N.z))
        {
            // N.y is the smallest component.
            TnBiT->v1.x = -N.z;
            TnBiT->v1.y =  0.f;
            TnBiT->v1.z =  N.x;
        }
        else
        {
            // N.z is the smallest component.
            TnBiT->v1.x = -N.y;
            TnBiT->v1.y =  N.x;
            TnBiT->v1.z =  0.f;
        }
    }
    // u is orthogonal to n, but not unit-length. Normalize it.
    TnBiT->v1 = normalize(TnBiT->v1);
    // Compute v.
    TnBiT->v2 = cross(TnBiT->v1, N);
}
Live computation methods : based on the uv coordinates
Code: [LINK # Select all]    if (!isnan(vp0.Tex.u))
    {
        // Compute deltas for triangle partial derivatives of normal
        float du1 = vp1.Tex.u - vp0.Tex.u;
        float du2 = vp2.Tex.u - vp0.Tex.u;
        float dv1 = vp1.Tex.v - vp0.Tex.v;
        float dv2 = vp2.Tex.v - vp0.Tex.v;
        
        float determinant = du1 * dv2 - dv1 * du2;
        if (determinant != 0.f)
        {
            float invdet = 1.f / determinant;
        
            clVector3 dp1 = vp1.P - vp0.P;
            clVector3 dp2 = vp2.P - vp0.P;
            
            clVector3 dpdu = (dv2 * dp1 - dv1 * dp2) * invdet;
            rayHit->T = normalize(cross(rayHit->Ng, dpdu));
            return;
        }
    }