繁体   English   中英

C++ 和 DirectX:使用任意法线计算网格的切线

[英]C++ and DirectX: Calculate tangent to mesh with arbitrary normals

我有一个带有任意法线的网格,我使用标准方法计算了它们来计算切线..

void calcTangent(uint32_t idx1, uint32_t idx2, uint32_t idx3)
{
    vertex v1 = _vertex[idx1];
    vertex v2 = _vertex[idx2];
    vertex v3 = _vertex[idx3];

    float du1 = v3.Text.x - v1.Text.x;
    float dv1 = v3.Text.y - v1.Text.y;

    float du2 = v2.Text.x - v1.Text.x;
    float dv2 = v2.Text.y - v1.Text.y;

    float tx1 = v3.Pos.x - v1.Pos.x;
    float ty1 = v3.Pos.y - v1.Pos.z;
    float tz1 = v3.Pos.z - v1.Pos.z;

    float tx2 = v2.Pos.x - v1.Pos.x;
    float ty2 = v2.Pos.y - v1.Pos.z;
    float tz2 = v2.Pos.z - v1.Pos.z;

    float r = 1.0f / (du1 * dv2 - dv1 * du2);

    float e1x = (dv2 * tx1 - dv1 * tx2) * r;
    float e1y = (dv2 * ty1 - dv1 * ty2) * r;
    float e1z = (dv2 * tz1 - dv1 * tz2) * r;

    //Binormals
    float e2x = (du1 * tx2 - du2 * tx1) * r;
    float e2y = (du1 * ty2 - du2 * ty1) * r;
    float e2z = (du1 * tz2 - du2 * tz1) * r;


    XMFLOAT3 ot1 = Math::gramSchmidthF({ v1.Norm.x, v1.Norm.y, v1.Norm.z }, { e1x, e1y, e1z });
    XMFLOAT3 ot2 = Math::gramSchmidthF({ v2.Norm.x, v2.Norm.y, v2.Norm.z }, { e1x, e1y, e1z });
    XMFLOAT3 ot3 = Math::gramSchmidthF({ v3.Norm.x, v3.Norm.y, v3.Norm.z }, { e1x, e1y, e1z });

    _vertex[idx1].Tangent = ot1;
    _vertex[idx2].Tangent = ot2;
    _vertex[idx3].Tangent = ot3;
}

bitangent 不会传递给着色器,而是在 PS.. 顶点着色器和像素着色器中计算。

struct VS_INPUT
{
    float4 Position  : POSITION;
    float3 Normal : NORMAL;
    float2 Texture  : TEXCOORD;
    float3 Tangent : TANGENT;
};

struct PS_INPUT
{
    float4 Position  : SV_POSITION;
    float3 Normal : NORMAL;
    float3 Tangent : TANGENT;
    float3 Binormal : BINORMAL;
    float2 Texture  : TEXCOORD0;
    float3 ViewDirection : TEXCOORD1;
};

PS_INPUT vertex_shader(VS_INPUT input)
{
    PS_INPUT output = (PS_INPUT)0;
    input.Position.w = 1.0f;

    //transformations
    output.Position = mul(input.Position, World);
    output.Position = mul(output.Position, View);
    output.Position = mul(output.Position, Projection);

    //
    output.Normal = normalize(mul(float4(input.Normal, 0), World).xyz);

    output.Texture = input.Texture;

    float3 worldPosition = mul(input.Position, World).xyz;
    output.ViewDirection = normalize(CAMERA_POSITION - worldPosition);

    //add the tangent and binormal
    output.Tangent = normalize(mul(float4(input.Tangent, 0), World).xyz);
    output.Binormal = normalize(cross(output.Normal, output.Tangent));

    return output;

}


    float4 ps(PS_INPUT input) : SV_Target
{
    float4 OUT = (float4)0;

    //texture normal
    float3 sampledNormal = (2 * normalMapTexture.Sample(normalMapSampler, input.Texture).xyz) - 1.0; // Map normal from [0..1] to [-1..1]

    //creating matrix   
    //                  Tangent
    //                  Binormal
    //                  Normal
    float3x3 tbn = float3x3(input.Tangent, input.Binormal, input.Normal);

    //convert tangent space to world space
    sampledNormal = mul(sampledNormal, tbn); // Transform normal from normal map to world space

    float3 viewDirection = normalize(input.ViewDirection);

    //texture color
    float4 color = colorTexture.Sample(samLinear, input.Texture);   //getting the color from texture without normals..

    //ambient color
    float3 ambient = getVectorColorContribution(AMBIENT_COLOR, color.rgb);      //mult AMBIENT_COLOR(.rgb) * AMBIENT_COLOR Intensity (.a) * color

    float3 diffuse = (float3)0;
    float3 specular = (float3)0;

        float3 lightDirection = normalize(-LIGHT_DIR.xyz);
        float n_dot_l = dot(sampledNormal, lightDirection);

            //calculating the diffuse value
            diffuse = saturate(n_dot_l) * LIGHT_COLOR.rgb * LIGHT_COLOR.a;


    }

    //changing the return types will change the result to basic or specular..
    OUT.rgb = diffuse * color;
    OUT.a = 1.0f;

    return OUT;
}

这是结果,仅使用漫反射,以避免镜面反射错误..请任何人知道这是为什么? 第一个是结果图像,第二个是法线贴图..

经过大量挖掘,在 DirectXMesh 中有一个 function 来计算切线权(ComputeTangentFrame)它将结果保存在XMFLOAT4 tangent[3]数组中,效果很好,所以我的问题是计算切线。 希望它对其他人有帮助.. 使用它的小例子:

            uint32_t idx[3];
            idx[0] = 0;
            idx[1] = 1;
            idx[2] = 2;

            XMFLOAT3 pos[3];
            pos[0] = Pos1;
            pos[1] = Pos2;
            pos[2] = Pos3;

            XMFLOAT3 normals[3];
            normals[0] = Normal1;
            normals[1] = Normal2;
            normals[2] = Normal3;

            XMFLOAT2 t[3];
            t[0] = TextureCoord1;
            t[1] = TextureCoord2;
            t[2] = TextureCoord3;

            XMFLOAT4 tangent[3];

            ComputeTangentFrame(idx,1, pos, normals,t,3, tangent);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM