1
\$\begingroup\$

I'm trying to implement Normal Mapping in Directx and I'm very close to creating it but I'm getting these weird black colors on some objects. This is how it looks like without the Normal Mapping:

enter image description here

And when I apply the Normal Mapping effect this is how it looks:

enter image description here

enter image description here

Where I get the black color on e.g the wall and some part of the cube which is odd.

My Pixel Shader file looks like this:

 cbuffer PositionBuffer : register(b1)
{
    float3 cameraPos;
    float pad;
    float3 lightPos;
    float pad1;
};

cbuffer PhongBuffer : register(b2) {
    float4 ambient;
    float4 diffus;
    float4 blank;
    float shininess;
    float3 padd;
};

Texture2D texDiffuse : register(t0);
Texture2D texNormal : register(t1);

SamplerState texSampler : register(s0);

struct PSIn
{
    float4 Pos  : SV_Position;
    float3 Normal : NORMAL;
    float2 TexCoord : TEX;
    float4 PosWorld : VIEWPOSITION;
    float4 Tangent : TANGENT; //Added this
    float4 Binormal : BINORMAL; //Added this
};

//-----------------------------------------------------------------------------------------
// Pixel Shader
//-----------------------------------------------------------------------------------------

float4 PS_main(PSIn input) : SV_Target
{   

    float3 N;
    float3 L;
    float3 R;
    float3 V;
    float3 I;

    float3 textureColor;
    float4 bumpMap;
    float3 bumpNormal;

    // Sample the texture pixel at this location.
    textureColor = texDiffuse.Sample(texSampler, input.TexCoord).xyz;

    // Sample the pixel in the bump map.
    bumpMap = texNormal.Sample(texSampler, input.TexCoord);

    // Expand the range of the normal value from (0, +1) to (-1, +1).
    bumpMap = (bumpMap * 2.0f) - 1.0f;

    // Calculate the normal from the data in the bump map.
    bumpNormal = (bumpMap.x * input.Tangent) + (bumpMap.y * input.Binormal) + (bumpMap.z * input.Normal);

    // Normalize the resulting bump normal.
    //bumpNormal = Normalize(bumpNormal;


    L = lightPos - input.PosWorld;
    //N = input.Normal;
    N = bumpNormal;
    V = cameraPos - input.PosWorld;
    R = reflect(-L, N);

    L = normalize(L);
    N = normalize(N);
    V = normalize(V);
    R = normalize(R);


    I = ambient.xyz + (textureColor * max(dot(L,N), 0) + blank.xyz * max(pow(dot(R,V), shininess), 0)); //Phong Shader Formula

    if(texNormal.Sample(texSampler, input.TexCoord).z <= 0) { //Prevent the objects that don't have bump map from being given the Normal Mapping effect
        return texDiffuse.Sample(texSampler, input.TexCoord);
    }

    if(dot(L, N) < 0) {
        return texDiffuse.Sample(texSampler, input.TexCoord);
    }

    return float4(I, 1);

}

And my Vertex Shader looks like this:

cbuffer MatrixBuffer : register(b0)
{
    matrix ModelToWorldMatrix;
    matrix WorldToViewMatrix;
    matrix ProjectionMatrix;
};


struct VSIn
{
    float3 Pos : POSITION;
    float3 Normal : NORMAL;
    float3 Tangent : TANGENT;
    float3 Binormal : BINORMAL;
    float2 TexCoord : TEX;
};

struct PSIn
{
    float4 Pos  : SV_Position;
    float3 Normal : NORMAL;
    float2 TexCoord : TEX;
    float4 PosWorld : VIEWPOSITION;
    float3 Tangent : TANGENT; //Added this
    float3 Binormal : BINORMAL; //Added this
};

//-----------------------------------------------------------------------------------------
// Vertex Shader
//-----------------------------------------------------------------------------------------

PSIn VS_main(VSIn input)
{
    PSIn output = (PSIn)0;

    // Model->View transformation
    matrix MV = mul(WorldToViewMatrix, ModelToWorldMatrix);

    // Model->View->Projection (clip space) transformation
    // SV_Position expects the output position to be in clip space
    matrix MVP = mul(ProjectionMatrix, MV);

    // Perform transformations and send to output
    output.Pos = mul(MVP, float4(input.Pos, 1));
    output.Normal = normalize( mul(ModelToWorldMatrix, float4(input.Normal,0)).xyz ); //Convert the Normal for the vertex to World Space
    output.TexCoord = input.TexCoord;
    output.PosWorld = mul(ModelToWorldMatrix, float4(input.Pos, 1)); //Convert to World Space
    output.Tangent = normalize( mul(ModelToWorldMatrix, float4(input.Tangent,0)).xyz ); //Convert the Tangent for the vertex to World Space
    output.Binormal = normalize( mul(ModelToWorldMatrix, float4(input.Binormal,0)).xyz ); //Convert the Binormal for the vertex to World Space


    return output;
}

The way I calculate the Binormal and Tangent for each vertex from the CPU side is using this method (taken from Rasterek Tutorial http://www.rastertek.com/dx11tut20.html):

    void OBJModel_t::CalculateModelVectors(std::vector<vertex_t> & vertices) {
    int faceCount, i, index;
    vertex_t vertex1, vertex2, vertex3;
    vec3f tangent, binormal, normal;
    int m_vertexCount = vertices.size();

    // Calculate the number of faces in the model.
    faceCount = m_vertexCount / 3;

    // Initialize the index to the model data.
    index = 0;

    // Go through all the faces and calculate the the tangent, binormal, and normal vectors.
    for (i = 0; i<faceCount; i++)
    {
        // Get the three vertices for this face from the model.
        vertex1.Pos.x = vertices[index].Pos.x;
        vertex1.Pos.y = vertices[index].Pos.y;
        vertex1.Pos.z = vertices[index].Pos.z;
        vertex1.TexCoord.x = vertices[index].TexCoord.x;
        vertex1.TexCoord.y = vertices[index].TexCoord.y;
        vertex1.Normal.x = vertices[index].Normal.x;
        vertex1.Normal.y = vertices[index].Normal.y;
        vertex1.Normal.z = vertices[index].Normal.z;
        index++;

        vertex2.Pos.x = vertices[index].Pos.x;
        vertex2.Pos.y = vertices[index].Pos.y;
        vertex2.Pos.z = vertices[index].Pos.z;
        vertex2.TexCoord.x = vertices[index].TexCoord.x;
        vertex2.TexCoord.y = vertices[index].TexCoord.y;
        vertex2.Normal.x = vertices[index].Normal.x;
        vertex2.Normal.y = vertices[index].Normal.y;
        vertex2.Normal.z = vertices[index].Normal.z;
        index++;

        vertex3.Pos.x = vertices[index].Pos.x;
        vertex3.Pos.y = vertices[index].Pos.y;
        vertex3.Pos.z = vertices[index].Pos.z;
        vertex3.TexCoord.x = vertices[index].TexCoord.x;
        vertex3.TexCoord.y = vertices[index].TexCoord.y;
        vertex3.Normal.x = vertices[index].Normal.x;
        vertex3.Normal.y = vertices[index].Normal.y;
        vertex3.Normal.z = vertices[index].Normal.z;
        index++;

        // Calculate the tangent and binormal of that face.
        CalculateTangentBinormal(vertex1, vertex2, vertex3, tangent, binormal);

        // Calculate the new normal using the tangent and binormal.
        CalculateNormal(tangent, binormal, normal);

        // Store the normal, tangent, and binormal for this face back in the model structure.
        vertices[index - 1].Normal.x = normal.x;
        vertices[index - 1].Normal.y = normal.y;
        vertices[index - 1].Normal.z = normal.z;
        vertices[index - 1].Tangent.x = tangent.x;
        vertices[index - 1].Tangent.y = tangent.y;
        vertices[index - 1].Tangent.z = tangent.z;
        vertices[index - 1].Binormal.x = binormal.x;
        vertices[index - 1].Binormal.y = binormal.y;
        vertices[index - 1].Binormal.z = binormal.z;

        vertices[index - 2].Normal.x = normal.x;
        vertices[index - 2].Normal.y = normal.y;
        vertices[index - 2].Normal.z = normal.z;
        vertices[index - 2].Tangent.x = tangent.x;
        vertices[index - 2].Tangent.y = tangent.y;
        vertices[index - 2].Tangent.z = tangent.z;
        vertices[index - 2].Binormal.x = binormal.x;
        vertices[index - 2].Binormal.y = binormal.y;
        vertices[index - 2].Binormal.z = binormal.z;

        vertices[index - 3].Normal.x = normal.x;
        vertices[index - 3].Normal.y = normal.y;
        vertices[index - 3].Normal.z = normal.z;
        vertices[index - 3].Tangent.x = tangent.x;
        vertices[index - 3].Tangent.y = tangent.y;
        vertices[index - 3].Tangent.z = tangent.z;
        vertices[index - 3].Binormal.x = binormal.x;
        vertices[index - 3].Binormal.y = binormal.y;
        vertices[index - 3].Binormal.z = binormal.z;
    }

    return;
}

void OBJModel_t::CalculateTangentBinormal(vertex_t vertex1, vertex_t vertex2, vertex_t vertex3, vec3f& tangent, vec3f& binormal) {

    float vector1[3], vector2[3];
    float tuVector[2], tvVector[2];
    float den;
    float length;


    // Calculate the two vectors for this face.
    vector1[0] = vertex2.Pos.x - vertex1.Pos.x;
    vector1[1] = vertex2.Pos.y - vertex1.Pos.y;
    vector1[2] = vertex2.Pos.z - vertex1.Pos.z;

    vector2[0] = vertex3.Pos.x - vertex1.Pos.x;
    vector2[1] = vertex3.Pos.y - vertex1.Pos.y;
    vector2[2] = vertex3.Pos.z - vertex1.Pos.z;

    // Calculate the tu and tv texture space vectors.
    tuVector[0] = vertex2.TexCoord.x - vertex1.TexCoord.x;
    tvVector[0] = vertex2.TexCoord.y - vertex1.TexCoord.y;

    tuVector[1] = vertex3.TexCoord.x - vertex1.TexCoord.x;
    tvVector[1] = vertex3.TexCoord.y - vertex1.TexCoord.y;

    // Calculate the denominator of the tangent/binormal equation.
    den = 1.0f / (tuVector[0] * tvVector[1] - tuVector[1] * tvVector[0]);

    // Calculate the cross products and multiply by the coefficient to get the tangent and binormal.
    tangent.x = (tvVector[1] * vector1[0] - tvVector[0] * vector2[0]) * den;
    tangent.y = (tvVector[1] * vector1[1] - tvVector[0] * vector2[1]) * den;
    tangent.z = (tvVector[1] * vector1[2] - tvVector[0] * vector2[2]) * den;

    binormal.x = (tuVector[0] * vector2[0] - tuVector[1] * vector1[0]) * den;
    binormal.y = (tuVector[0] * vector2[1] - tuVector[1] * vector1[1]) * den;
    binormal.z = (tuVector[0] * vector2[2] - tuVector[1] * vector1[2]) * den;

    // Calculate the length of this normal.
    length = sqrt((tangent.x * tangent.x) + (tangent.y * tangent.y) + (tangent.z * tangent.z));

    // Normalize the normal and then store it
    tangent.x = tangent.x / length;
    tangent.y = tangent.y / length;
    tangent.z = tangent.z / length;

    // Calculate the length of this normal.
    length = sqrt((binormal.x * binormal.x) + (binormal.y * binormal.y) + (binormal.z * binormal.z));

    // Normalize the normal and then store it
    binormal.x = binormal.x / length;
    binormal.y = binormal.y / length;
    binormal.z = binormal.z / length;

    return;
}

void OBJModel_t::CalculateNormal(vec3f tangent, vec3f binormal, vec3f& normal)
{
    float length;


    // Calculate the cross product of the tangent and binormal which will give the normal vector.
    normal.x = (tangent.y * binormal.z) - (tangent.z * binormal.y);
    normal.y = (tangent.z * binormal.x) - (tangent.x * binormal.z);
    normal.z = (tangent.x * binormal.y) - (tangent.y * binormal.x);


    // Calculate the length of the normal.
    length = sqrt((normal.x * normal.x) + (normal.y * normal.y) + (normal.z * normal.z));

    // Normalize the normal.
    normal.x = normal.x / length;
    normal.y = normal.y / length;
    normal.z = normal.z / length;

    return;
}
\$\endgroup\$
  • \$\begingroup\$ You might want to consider using RenderDoc to capture a frame of your application and looking into data you send through constant buffers and textures to diagnose the issue. Otherwise, Visual Studio has a Graphics Debugger under the Debug menu. You can step into your shader code and debug your shaders. \$\endgroup\$ – Varaquilex Mar 31 '18 at 4:47
  • \$\begingroup\$ I've gotten this before when I had a calculation that resulted in a NaN in my shader, usually due to a divide by zero from a normal that was (0, 0, 0). Might be worth looking into. \$\endgroup\$ – user1118321 Mar 31 '18 at 5:02
  • \$\begingroup\$ @Varaquilex Thanks for the tip and I'm thinking about using that debugger but I suspect the issue is coming before it gets sent to the shaders. I think it's coming from the CalculateModelVectors function but not sure :) \$\endgroup\$ – Awni Mar 31 '18 at 6:23
  • \$\begingroup\$ @user1118321 Yes that might be possibly why. I printed out the denominator in the CalculateTangentBinormal function since that's were at least one division happens and sometimes the denominator gives an inf value and not a number could that be why? \$\endgroup\$ – Awni Mar 31 '18 at 6:24
  • \$\begingroup\$ Yes, that could be why. Any Inf/NaN/-Inf will end up messing up the rest of the calculations. \$\endgroup\$ – user1118321 Mar 31 '18 at 15:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.