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

Started by
4 comments, last by RDragon1 14 years, 3 months ago
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.
Advertisement
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.
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
Normalize your normal in the pixel shader

Unsure why your negative directions don't work... Start debugging. Try outputting compress( NdotL ) to the screen to see what's happening
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.
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

This topic is closed to new replies.

Advertisement