Sign in to follow this  
Medo Mex

Light doesn't work without bump map

Recommended Posts

Medo Mex    891

I created a point light on the terrain and it works perfectly, however when I remove the bump map, it doesn't work anymore.

 

When I do the following I don't see the light on the terrain:

float3 n = normalize(IN.Normal);

When I do the following I see the light:

float3 n = normalize(tex2D(normalMap, IN.UV).rgb * 2.0f - 1.0f);

I checked to see if the mesh has normal and found that it has normal.

Share this post


Link to post
Share on other sites
DwarvesH    510

Are you multiplying the normal with the world inverse transposed inside the vertex shader?

OUT.normal = mul(IN.normal, (float3x3)worldInverseTransposeMatrix);

Share this post


Link to post
Share on other sites
gfxgangsta    806

3 suggestions:

 

1. Even though you know the mesh has normals, check that the normals are passed all the way from your Direct3D code (C/C++?) to the shader where you're doing the lighting (HLSL). Does your vertex declaration include a normal? If you're doing per pixel lighting, perhaps the normal is available in the vertex shader, but it's not being passed to the pixel shader?

2. You can use PIX to debug graphics issues (just google "pix debug tutorial")

3. Post the full shader code here

Share this post


Link to post
Share on other sites
Medo Mex    891

Here is the shader code:

float4x4 World;
float4x4 View;
float4x4 Projection;
float4x4 worldInverseTransposeMatrix;

float3 cameraPos;
float materialPower;
float4 materialDiffuse;
float4 materialAmbient;
float4 materialSpecular;

float3 lightPosition;
float4 lightDiffuse;
float4 lightSpecular;
float lightRange;

float4 globalAmbient;

texture colorMapTexture;
sampler2D colorMap = sampler_state
{
    Texture = <colorMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

// Bump map
texture normalMapTexture;
sampler2D normalMap = sampler_state
{
    Texture = <normalMapTexture>;
    MagFilter = Linear;
    MinFilter = Anisotropic;
    MipFilter = Linear;
    MaxAnisotropy = 16;
};

struct VERTEX
{
    float3 Pos : POSITION;
    float3 Normal : NORMAL;
    float2 UV : TEXCOORD0;
};

struct VS_OUTPUT
{
   float4 Pos : POSITION;
   float2 UV : TEXCOORD0;
   float3 Normal : TEXCOORD1;
   float4 worldPos : TEXCOORD2;
};

VS_OUTPUT VS( VERTEX IN )
{
     VS_OUTPUT OUT;
     OUT = (VS_OUTPUT)0;
     float4x4 WVP = mul(World, mul(View, Projection));
     OUT.Pos = mul(float4(IN.Pos, 1.0f), WVP);
     OUT.Normal = mul(IN.Normal, (float3x3)worldInverseTransposeMatrix);
     OUT.UV = IN.UV;
     OUT.worldPos = mul(float4(IN.Pos, 1.0f), World);
     return OUT;
}

float4 PS( VS_OUTPUT IN ) : COLOR
{
    float3 viewDir = cameraPos - IN.worldPos;
    float3 lightDir = (lightPosition - IN.worldPos) / lightRange;
    float atten = saturate(1.0f - dot(lightDir, lightDir));


    float3 n =  normalize(IN.Normal); 
    // *** LIGHT ONLY WORKS WHEN I REPLACE THE ABOVE LINE WITH THIS LINE --> normalize(tex2D(normalMap, IN.UV).rgb * 2.0f - 1.0f); ***
    float3 l = normalize(lightDir);
    float3 v = normalize(viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = (nDotL == 0.0f) ? 0.0f : pow(nDotH, materialPower);


    float4 diffuse = materialDiffuse * lightDiffuse;
    float4 specular = materialSpecular * lightSpecular;
    float4 lightColor = (materialAmbient + globalAmbient) + 
                             (diffuse * nDotL * atten) + 
                             (specular * power * atten);
    return tex2D(colorMap, IN.UV) * lightColor;
}

technique TerrainTech
{
    pass p0
    {
        VertexShader = compile vs_3_0 VS();
        PixelShader  = compile ps_3_0 PS();
    }
}

Yes, the vertex declaration include Normal.

Share this post


Link to post
Share on other sites
Migi0027    4630

I agree with gfxgansta, even though you have the normals in the vertex declarations, it may be possible that they are not correctly passed from the cpu to the gpu.

 

Try visualising the normals, like: PS: return float4(IN.Normal, 1.0f);

Edited by Migi0027

Share this post


Link to post
Share on other sites
Medo Mex    891

I'm sure this problem is related to the light shader itself.

 

The light DOES work on some meshes, while it doesn't work on the terrain, also I THINK it's not working correctly on other meshes, the mesh sometimes is lighting from one side even the mesh is over the light (it should light from other sides as well) then when I move it it get some light from sides.

 

I believe the shader light is not correct, can someone check the shader and let me know if it's a VALID light shader?

 

@Migi0027: Tried that, it doesn't work.

Edited by Medo3337

Share this post


Link to post
Share on other sites
Pink Horror    2459

I created a point light on the terrain and it works perfectly, however when I remove the bump map, it doesn't work anymore.

 

When I do the following I don't see the light on the terrain:

float3 n = normalize(IN.Normal);

When I do the following I see the light:

float3 n = normalize(tex2D(normalMap, IN.UV).rgb * 2.0f - 1.0f);

I checked to see if the mesh has normal and found that it has normal.

 

The first line depends on IN.Normal. The second line only depends on IN.UV. I'm no lighting expert, but I'm fairly sure that the normals from your texture are supposed to be transformed in some way.

 

How does your bump map work perfectly? You are calculating a normal for each vertex and then throwing them away. That cannot be intentional.

Edited by Pink Horror

Share this post


Link to post
Share on other sites
Medo Mex    891

@Pink Horror: Check out my shader above, when I apply bump map it DOES look very well.

 

When I remove the bump map I don't see the light, I tried to move the terrain upwards, now I get to see the light, I also get to see the light when I set large amount of "point light radius".

 

I guess the light get positioned incorrectly when I don't use bump map.

 

In Vertex Shader, I'm transforming the normal as the following:

OUT.Normal = mul(IN.Normal, (float3x3)worldInverseTransposeMatrix);
Edited by Medo3337

Share this post


Link to post
Share on other sites
Migi0027    4630

What do you mean by, it does not work? Is it black?

 

Try normalizing them and then visualize them, if there is literally no color change around the terrain, with the normal visualisation, that means that the normals are not passed to the shader.

Share this post


Link to post
Share on other sites
Medo Mex    891

@Migi0027: The light EXISTS, but it's not on the valid position (on the terrain), I can only see it when I move the terrain up or increase the light radius.

 

Though, the light is affecting other objects over the terrain.

 

EDIT: After testing, I notice that the light ONLY affect the terrain when its position is colliding with the terrain, however you know that point light position doesn't necessarily have to collide with the mesh, it could affect the mesh from some distance (according to the light radius/range).

 

I also tested to make sure that the normals are passed and found that the program pass it to the shader, however, still the same issue.

Edited by Medo3337

Share this post


Link to post
Share on other sites
Pink Horror    2459

 

@Pink Horror: Check out my shader above, when I apply bump map it DOES look very well.

 

When I remove the bump map I don't see the light, I tried to move the terrain upwards, now I get to see the light, I also get to see the light when I set large amount of "point light radius".

 

I guess the light get positioned incorrectly when I don't use bump map.

 

In Vertex Shader, I'm transforming the normal as the following:

OUT.Normal = mul(IN.Normal, (float3x3)worldInverseTransposeMatrix);

 

Why are you computing this normal?

Share this post


Link to post
Share on other sites
belfegor    2835

Of course you need to transform the normal because it depends on object orientation.

 

Can you create minimal project, zip it and share a link here so it will be easier to help you.

Edited by belfegor

Share this post


Link to post
Share on other sites
Medo Mex    891

@belfegor: I created a new project, in this project I have a plane and a box

 

When I set:

Light position to (XYZ: 0.0f, 0.0f, 0.0f)

Plane position to (XYZ: 0.0f, 0.0f, 0.0f)

Light range: 5000.0f

 

I don't see the light

 

BUT, when I move the light up, I start to see the light:

Light position to (XYZ: 0.0f, 10.0f, 0.0f)

Plane position to (XYZ: 0.0f, 0.0f, 0.0f)

Light range: 5000.0f

 

Project: http://www.2shared.com/file/F-SF9SLo/Light_Problem_Sample.html

 

EDIT:

Another problem I noticed now, when I apply bump map, the light get positioned incorrectly.

 

I do that by replacing float3 n = normalize(IN.Normal); to normalize(tex2D(normalMap, IN.UV).rgb * 2.0f - 1.0f); and passing a bump map texture to the shader.

Edited by Medo3337

Share this post


Link to post
Share on other sites
belfegor    2835

Try the project from attachment.

 

edit: Forgat to note about camera controls: w-s-a-d movement, left mouse press and move to rotate.

Edited by belfegor

Share this post


Link to post
Share on other sites
Medo Mex    891

@belfegor: When I apply bump map, the light change its position to invalid position.

 

I modified your project to support bump mapping and I can notice the problem:

[attachment=17043:Terrain modified.rar]

 

After running the program hold 'B' to enable bump mapping and see that the light position is not the same.

Edited by Medo3337

Share this post


Link to post
Share on other sites
DwarvesH    510

Well you can always compute them, with something similar to this:

/// <summary>
        /// Given the 3 vertices (position and texture coordinate) and the
        /// face normal of a triangle calculate and return the triangle's
        /// tangent vector. This method is designed to work with XNA's default
        /// right handed coordinate system and clockwise triangle winding order.
        /// Undefined behavior will result if any other coordinate system
        /// and/or winding order is used. The handedness of the local tangent
        /// space coordinate system is stored in the tangent's w component.
        /// </summary>
        /// <param name="pos1">Triangle vertex 1 position</param>
        /// <param name="pos2">Triangle vertex 2 position</param>
        /// <param name="pos3">Triangle vertex 3 position</param>
        /// <param name="texCoord1">Triangle vertex 1 texture coordinate</param>
        /// <param name="texCoord2">Triangle vertex 2 texture coordinate</param>
        /// <param name="texCoord3">Triangle vertex 3 texture coordinate</param>
        /// <param name="normal">Triangle face normal</param>
        /// <param name="tangent">Calculated tangent vector</param>
        public static void CalcTangent(ref Vector3 pos1,
                                       ref Vector3 pos2,
                                       ref Vector3 pos3,
                                       ref Vector2 texCoord1,
                                       ref Vector2 texCoord2,
                                       ref Vector2 texCoord3,
                                       ref Vector3 normal,                                       
                                       out Vector4 tangent)
        {
            // Create 2 vectors in object space.
            // edge1 is the vector from vertex positions pos1 to pos3.
            // edge2 is the vector from vertex positions pos1 to pos2.
            Vector3 edge1 = pos3 - pos1;
            Vector3 edge2 = pos2 - pos1;
            
            edge1.Normalize();
            edge2.Normalize();

            // Create 2 vectors in tangent (texture) space that point in the
            // same direction as edge1 and edge2 (in object space).
            // texEdge1 is the vector from texture coordinates texCoord1 to texCoord3.
            // texEdge2 is the vector from texture coordinates texCoord1 to texCoord2.
            Vector2 texEdge1 = texCoord3 - texCoord1;
            Vector2 texEdge2 = texCoord2 - texCoord1;

            texEdge1.Normalize();
            texEdge2.Normalize();

            // These 2 sets of vectors form the following system of equations:
            //
            //  edge1 = (texEdge1.x * tangent) + (texEdge1.y * bitangent)
            //  edge2 = (texEdge2.x * tangent) + (texEdge2.y * bitangent)
            //
            // Using matrix notation this system looks like:
            //
            //  [ edge1 ]     [ texEdge1.x  texEdge1.y ]  [ tangent   ]
            //  [       ]  =  [                        ]  [           ]
            //  [ edge2 ]     [ texEdge2.x  texEdge2.y ]  [ bitangent ]
            //
            // The solution is:
            //
            //  [ tangent   ]        1     [ texEdge2.y  -texEdge1.y ]  [ edge1 ]
            //  [           ]  =  -------  [                         ]  [       ]
            //  [ bitangent ]      det A   [-texEdge2.x   texEdge1.x ]  [ edge2 ]
            //
            //  where:
            //        [ texEdge1.x  texEdge1.y ]
            //    A = [                        ]
            //        [ texEdge2.x  texEdge2.y ]
            //
            //    det A = (texEdge1.x * texEdge2.y) - (texEdge1.y * texEdge2.x)
            //
            // From this solution the tangent space basis vectors are:
            //
            //    tangent = (1 / det A) * ( texEdge2.y * edge1 - texEdge1.y * edge2)
            //  bitangent = (1 / det A) * (-texEdge2.x * edge1 + texEdge1.x * edge2)
            //     normal = cross(tangent, bitangent)

            Vector3 t;
            Vector3 b;
            float det = (texEdge1.X * texEdge2.Y) - (texEdge1.Y * texEdge2.X);

            if ((float)Math.Abs(det) < 1e-6f)    // almost equal to zero
            {
                t = Vector3.UnitX;
                b = Vector3.UnitY;
            }
            else
            {
                det = 1.0f / det;

                t.X = (texEdge2.Y * edge1.X - texEdge1.Y * edge2.X) * det;
                t.Y = (texEdge2.Y * edge1.Y - texEdge1.Y * edge2.Y) * det;
                t.Z = (texEdge2.Y * edge1.Z - texEdge1.Y * edge2.Z) * det;

                b.X = (-texEdge2.X * edge1.X + texEdge1.X * edge2.X) * det;
                b.Y = (-texEdge2.X * edge1.Y + texEdge1.X * edge2.Y) * det;
                b.Z = (-texEdge2.X * edge1.Z + texEdge1.X * edge2.Z) * det;

                t.Normalize();
                b.Normalize();
            }

            // Calculate the handedness of the local tangent space.
            // The bitangent vector is the cross product between the triangle face
            // normal vector and the calculated tangent vector. The resulting bitangent
            // vector should be the same as the bitangent vector calculated from the
            // set of linear equations above. If they point in different directions
            // then we need to invert the cross product calculated bitangent vector. We
            // store this scalar multiplier in the tangent vector's 'w' component so
            // that the correct bitangent vector can be generated in the normal mapping
            // shader's vertex shader.

            Vector3 bitangent = Vector3.Cross(normal, t);
            float handedness = (Vector3.Dot(bitangent, b) < 0.0f) ? -1.0f : 1.0f;

            tangent.X = t.X;
            tangent.Y = t.Y;
            tangent.Z = t.Z;
            tangent.W = handedness;
        }

foe each vertex in your mesh, figure out the other two vertices that make up the triangle using the index array, and call this method.

Share this post


Link to post
Share on other sites
belfegor    2835

@belfegor: I'm using Panda Exporter in 3Ds Max, it's not exporting the mesh with tangent and binormal...

 

You can calculate them as DwarvesH showed or use D3DXComputeTangentFrame as i have written in the demo since you didn't spot it.

Share this post


Link to post
Share on other sites
Medo Mex    891

@belfegor: After checking for awhile, I notice that when I set the light color to a certain color (for example: red) and set material ambient to something like 0.8

 

The whole terrain become reddish

 

Light gLight = { D3DXVECTOR3(0.0f, 2.0f, 0.0f), 5.0f, D3DXVECTOR3(1.0f, 0.0f, 0.0f) };
Material gMaterial = { D3DXVECTOR3(1.0f, 1.0f, 1.0f), 80.0f, 0.8f };

 

Share this post


Link to post
Share on other sites
DwarvesH    510

Well, yes. You are using your point light as a normal point light with attenuation, but it also provides the ambient lighting to your terrain. So the terrain ambient lighting and the light color are the same. You only have one light, so what is not touched by the light is black.

 

You should provide the terrain ambient lighting by a different light source, probably directional. Alternatively, you could add a flat ambient lighting to the terrain, independent on any light source.

Share this post


Link to post
Share on other sites
Medo Mex    891

@DwarvesH: I don't want the terrain to be black when there is no light in certain places.

 

If there is no light source on certain parts, it should be affected by global ambient.

Edited by Medo3337

Share this post


Link to post
Share on other sites
DwarvesH    510

I would recommend that you first have a go at playing around with the shader and make sure you understand what is what and what it does. This way you will be able to build upon the foundation shader on your own.

 

For starters, you could go int you bump mapped pixel shader, and replace:

float atten     = saturate(1.0f - dot(lightDir, lightDir)) + MaterialAmbient;

with:


float atten     = saturate(1.0f - dot(lightDir, lightDir));

Now you can see the actual extents of a classical point light. You can then use the controls of your demo to change position and radius and see how the light looks in isolation.

 

Then you can try removing the attenuation and see how the shading looks only with direction being important:

float atten     = 1;

Then you can try a very simple and very ugly basic ambient coefficient:

float atten     = saturate(1.0f - dot(lightDir, lightDir));

...

float3 diffuse  = LightDiffuse * MaterialDiffuse * texDiffuse * (nDotL * atten );
float3 specular = LightDiffuse * power * atten;
float3 ambient = float3(0.4f, 0.4f, 0.4f) * texDiffuse;

OUT.Color.rgb = diffuse + specular + ambient;
OUT.Color.a   = 1.0f;

This will look like crap and you will only have bump mapping and specular effect in the area lit by the point light.

 

Now that you have a global ambient component, you can replace the values that give ugly results with better one. Like in the step where we set atten to 1:

float3x3 tangentBasis = float3x3(normalize(IN.TangentW), normalize(IN.BinormalW), normalize(IN.NormalW));
	float3 n              = normalize(mul(bump.xyz, tangentBasis));

    float3 l = normalize(lightDir);
    float3 v = normalize(viewDir);
    float3 h = normalize(l + v);
    
    float nDotL = saturate(dot(n, l));
    float nDotH = saturate(dot(n, h));
    float power = pow(nDotH, MaterialPower);

	float3 diffuse  = LightDiffuse * MaterialDiffuse * texDiffuse * (nDotL * atten );
	float3 specular = LightDiffuse * power * atten;
	float3 ambient = LightDiffuse * MaterialAmbient * texDiffuse * nDotL;

	OUT.Color.rgb = diffuse + specular + ambient;
	OUT.Color.a   = 1.0f;

This will give a very dark result. The material properties are not set up correctly for these equations, especially since I changed the interpretation of the values.

 

I'm not saying that things look good, or are correct. There are a lot of ways to do what you want and first you need to decide exactly on the lighting scheme you want. For interior scenes, especially with a lot of rooms, you can use multiple point lights. Exterior scenes, especially terrain are well served by one master directional light, which does a fair job of approximating the Sun. A point light is not well suited for approximating the sun because it has a radius and the intensity of the light changes rapidly when moving in that radius. If you place the point light as a sun too far, one corner of you map will be brighter than the others. If you place it in a central position, the problem repeats. If you lower it too much, the middle of the map will be shinny while the corners will be just right.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this