Sign in to follow this  
Spa8nky

[HLSL] [XNA] Diffuse light direction doesn't work correctly.

Recommended Posts

Hi folks, I am currently trying to implement some simple diffuse lighting into my game and have come across a odd problem. The direction of the light produces incorrect results. Currently I have the following inputs, along with my vertex and pixel shader:
struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 TexCoord	: TEXCOORD0;
    float3 N		: NORMAL0;
    float4 Color	: COLOR0;   
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 TexCoord	: TEXCOORD0;
    float3 L		: TEXCOORD1;
    float3 N		: TEXCOORD2;
    float4 Color	: COLOR0;   
};

struct PixelShaderOutput
{
	float4 Color	: COLOR0;
};

//=============================================
//------------ Technique: Default -------------
//=============================================

VertexShaderOutput VertexShader(VertexShaderInput input)
{
    VertexShaderOutput output;

    output.Position = mul(input.Position, xWorldViewProjection);
    output.TexCoord = input.TexCoord;
    output.Color = input.Color;
    
    // Will be moved into PixelShader for deferred rendering
    float3 lightDirection = float3(0.0f, 1.0f, 0.0f);
    output.L = normalize(lightDirection);			// Light Direction (normalised)
	output.N = normalize(mul(input.N, xWorld));		// Surface Normal (normalised) [World Coordinates]
	
    return output;
}

PixelShaderOutput PixelShader(VertexShaderOutput input)
{
	PixelShaderOutput output;
	
	// [Ambient Light] I = Ai * Ac
	float Ambient_Intensity = 0.1f;
	float4 Ambient_Colour = float4(1.0f, 1.0f, 1.0f, 1.0f);
	float4 Ambient_Light = Ambient_Intensity * Ambient_Colour;
	
	// [Diffuse Light] I = Di * Dc * N.L
	float Diffuse_Intensity = 1.0f;
	float4 Diffuse_Colour = float4(0.0f, 0.0f, 1.0f, 1.0f);
	float NdotL = dot(input.N, input.L);
	float4 Diffuse_Light = Diffuse_Intensity * Diffuse_Colour * saturate(NdotL);
	
	output.Color = (Ambient_Light + Diffuse_Light) * tex2D(TextureSampler0, input.TexCoord);
	
    return output;
}


A light direction of (1, 0, 0) produces the following incorrect result: Direction = (1,0,0) however, the opposite direction (-1, 0, 0) produces no result: Direction = (-1,0,0) The same occurs for (0, 1, 0) Direction = (0,1,0) with a negative y direction (0, -1, 0) producing no result: Direction = (0,-1,0) In all cases the original texture is chequered white/grey and the diffuse light is blue. Can anyone please tell me what I have done wrong? Thank you.

Share this post


Link to post
Share on other sites
Well the "L" vector in your typical diffuse lighting calculations actually refers to a vector that points from your surface towards the light...this is because your normal also points away from the surface. So for a directional light you should actually use the opposite direction you want the light to face. Also on a side note...if you pass along direction vectors from your vertex shader to your pixel shader, you'll want to re-normalize them in the pixel shader. This is because linear interpolation between two normalized vectors will not result in a normalized vector...draw it on a piece of paper if you want to see why.

As for why your light isn't forking for negative directions...I'm not sure. I'm not seeing anything in your shader code that would explain it. I would double-check that your vertex normals are correct for your mesh.

Share this post


Link to post
Share on other sites
hi,
I cannot really help with the problem you have asked for although i notice your program solves a problem that i have been having. I notice you have used TexCoord to texture you sphere but where or how have u defined what TexCoords are for each point.
i am currently using a mesh and D3DXCreateSphere.
Thanks in advance
Hellkite

Share this post


Link to post
Share on other sites
Quote:
Original post by MJP
As for why your light isn't working for negative directions...I'm not sure. I'm not seeing anything in your shader code that would explain it. I would double-check that your vertex normals are correct for your mesh.


Now that I have normalised the directions in the pixel shader, like you suggested, the negative directions work with no problems. I have left the vectors non normalised in the vertex shader though as they are not used, is this ok?

Quote:
Original post by MJP
Well the "L" vector in your typical diffuse lighting calculations actually refers to a vector that points from your surface towards the light...this is because your normal also points away from the surface. So for a directional light you should actually use the opposite direction you want the light to face.


Should the diffuse light equation then become:

float NdotL = dot(input.N, -input.L);

or should I negate the input light direction instead?

The vertex/pixel shaders now look like the following:


VertexShaderOutput VertexShader(VertexShaderInput input)
{
VertexShaderOutput output;

output.Position = mul(input.Position, xWorldViewProjection);
output.TexCoord = input.TexCoord;
output.Color = input.Color;

// Will be moved into PixelShader for deferred rendering
float3 lightDirection = float3(1.0f, 0.0f, 0.0f);
output.L = -lightDirection; // Light Direction
output.N = mul(input.N, xWorld); // Surface Normal [World Coordinates]

return output;
}

PixelShaderOutput PixelShader(VertexShaderOutput input)
{
// If you pass along direction vectors from your vertex shader to your pixel shader, you'll want to re-normalize them in the pixel shader.
// This is because linear interpolation between two normalized vectors will not result in a normalized vector...

PixelShaderOutput output;

// [Ambient Light] I = Ai * Ac
float Ambient_Intensity = 0.1f;
float4 Ambient_Colour = float4(1.0f, 1.0f, 1.0f, 1.0f);
float4 Ambient_Light = Ambient_Intensity * Ambient_Colour;

// [Diffuse Light] I = Di * Dc * N.L
float Diffuse_Intensity = 1.0f;
float4 Diffuse_Colour = float4(0.0f, 0.0f, 1.0f, 1.0f);
float NdotL = dot(normalize(input.N), normalize(input.L));
float4 Diffuse_Light = Diffuse_Intensity * Diffuse_Colour * saturate(NdotL);

output.Color = (Ambient_Light + Diffuse_Light) * tex2D(TextureSampler0, input.TexCoord);

return output;
}






Thanks for your help Matt.

Share this post


Link to post
Share on other sites
I would negate the light direction up in the application code somewhere so the right thing is set to the shader

As well as setting it as a pixel shader parameter so you don't waste an interpolator sending a constant

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