Directional lighting problems with transforming

Started by
7 comments, last by rocklobster 10 years, 7 months ago

Hey guys,

I'm implementing deferred shading and I've decided to calculate shading for a single global light in the first pass, then do all the other lights in the second pass. I'm not sure If I've got my normal/light and vertex positions all in the correct space and the lighting effect does not look correct.

Vertex shader:


#version 400

layout (location = 0) in vec3 in_Position;
layout (location = 1) in vec3 in_Normal;
layout (location = 2) in vec3 in_TexCoord;
layout (location = 3) in vec3 in_Tangent;
layout (location = 4) in vec3 in_Binormal;

out vec3 PositionEye;
out vec3 NormalEye;
out vec3 TexCoord; 
out vec3 Tangent;
out vec3 Binormal;

out vec3 LightDirEye;

uniform vec3 GlobalLightPos;

uniform mat4 u_ModelMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_ModelViewMatrix;
uniform mat3 u_NormalMatrix;
uniform mat4 u_MVP;

void main()
{
	PositionEye = vec3(u_ModelViewMatrix * vec4(in_Position, 1.0));
	NormalEye =  u_NormalMatrix * in_Normal;
	TexCoord = in_TexCoord;
	
	vec3 lightDirWorld = normalize(GlobalLightPos - in_Position);
	LightDirEye = u_NormalMatrix * lightDirWorld;	
	
	gl_Position = u_MVP * vec4(in_Position, 1.0);
}

Fragment shader:


#version 400

in vec3 PositionEye;
in vec3 NormalEye;
in vec3 TexCoord;
in vec3 Tangent;
in vec3 Binormal;

in vec3 LightDirEye;

uniform sampler2D Texture;
uniform float TexRepeat;

layout (location = 0) out vec3 out_Position;
layout (location = 1) out vec3 out_Normal;
layout (location = 2) out vec3 out_Diffuse; 
layout (location = 3) out vec3 out_Tangent;
layout (location = 4) out vec3 out_Binormal;

vec3 shadePixel(vec3 diff)
{
	vec3 s = LightDirEye;
	float sDotN = max(dot(s, NormalEye), 0.0);
	vec3 diffuse = vec3(1.0, 1.0, 1.0) * diff * sDotN;

	return diffuse;
}

void main()
{
	vec3 diff = texture(Texture, TexCoord * TexRepeat);

	out_Position = PositionEye;
	out_Normal = NormalEye;
	out_Diffuse = shadePixel(texture(Texture, TexCoord * TexRepeat));
	out_Tangent = Tangent;
	out_Binormal = Binormal;
}

Uniforms:


glm::vec3 lightPos = m_sceneGraph->GetGlobalLightPos();
glm::mat4 viewMatrix = m_renderSystem->GetCamera()->GetViewMatrix();
glm::mat3 normalMat =  m_renderSystem->GetCamera()->GetNormalMatrix();

m_graphics->SetUniform(id, "GlobalLightPos", lightPos);
m_graphics->SetUniform(id, "u_ViewMatrix", 1, false, viewMatrix);
m_graphics->SetUniform(id, "u_NormalMatrix", 1, false, normalMat); 

glm::mat4 modelMatrix = modelNode->GetTransform();
glm::mat4 modelView = viewMatrix * modelMatrix;
glm::mat4 mvp = m_renderSystem->GetCamera()->GetProjectionMatrix() * modelView;

m_graphics->SetUniform(id, "u_ModelMatrix", 1, false, modelMatrix);
m_graphics->SetUniform(id, "u_ModelViewMatrix", 1, false, modelView);
m_graphics->SetUniform(id, "u_MVP", 1, false, mvp);

The light pos is just glm::vec3(0.0f, 50.0f, 0.0f)

and the normal matrix is:


glm::mat3 Camera::GetNormalMatrix()
{
	return glm::transpose(glm::inverse(glm::mat3(m_view)));
}

Can't seem to work out what is going wrong here.

Advertisement

and the normal matrix is:


glm::mat3 Camera::GetNormalMatrix()
{
	return glm::transpose(glm::inverse(glm::mat3(m_view)));
}

Assuming your model's normal are in object space you need to be using the product of the model view matrix here. That's one problem.

In the vertex shader you have this line:


vec3 lightDirWorld = normalize(GlobalLightPos - in_Position);

which is subtracting a world space position from a model space position. Transform in_Position into world space then do the subtraction. The resulting vector can then be transformed into view space.

Since your light has a position this suggest it's point light and therefore attenuation should be considered. That doesn't appear to be happening anywhere. If you meant for your light to simply be directional then things are easier. Just transform the light direction into view space on the CPU and store it in a uniform accessible to the pixel shader.

Thanks,

In the vertex shader you have this line:


vec3 lightDirWorld = normalize(GlobalLightPos - in_Position);

which is subtracting a world space position from a model space position. Transform in_Position into world space then do the subtraction. The resulting vector can then be transformed into view space.

Since your light has a position this suggest it's point light and therefore attenuation should be considered. That doesn't appear to be happening anywhere. If you meant for your light to simply be directional then things are easier. Just transform the light direction into view space on the CPU and store it in a uniform accessible to the pixel shader.

It was a point light previously (Sun) but I figured I could switch it easily to a directional light by just getting the direction from it's position to the vertex's position? To transform the in_Position into world space would just be:


vec3 PosWorld = vec3(ModelMatrix * vec4(in_Position, 1.0));

correct?

and the normal matrix is:


glm::mat3 Camera::GetNormalMatrix()
{
	return glm::transpose(glm::inverse(glm::mat3(m_view)));
}

Assuming your model's normal are in object space you need to be using the product of the model view matrix here. That's one problem.

I'm not sure what you mean by "product of the model view matrix"?

something like this:


glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::mat3(modelView)));

So it is the inverse of the ModelView rather than just the View?

I can't test this out at the moment but I'll post back with results later on.

It was a point light previously (Sun) but I figured I could switch it easily to a directional light by just getting the direction from it's position to the vertex's position? To transform the in_Position into world space would just be:


vec3 PosWorld = vec3(ModelMatrix * vec4(in_Position, 1.0));

correct?

[ ... ]

I'm not sure what you mean by "product of the model view matrix"?

something like this:


glm::mat3 normalMatrix = glm::transpose(glm::inverse(glm::mat3(modelView)));

So it is the inverse of the ModelView rather than just the View?

Correct on both counts.

Ok so this is what I've changed:


glm::mat4 modelMatrix = modelNode->GetTransform();
glm::mat4 modelView = viewMatrix * modelMatrix;
glm::mat4 mvp = m_renderSystem->GetCamera()->GetProjectionMatrix() * modelView;
glm::mat3 normalMat = glm::transpose(glm::inverse(glm::mat3(modelView)));

m_graphics->SetUniform(id, "u_NormalMatrix", 1, false, normalMat); 
m_graphics->SetUniform(id, "u_ModelMatrix", 1, false, modelMatrix);
m_graphics->SetUniform(id, "u_ModelViewMatrix", 1, false, modelView);
m_graphics->SetUniform(id, "u_MVP", 1, false, mvp); 

Vertex Shader:

Multiplying the in_Position by the u_ModelMatrix, getting the direction from the light position to the vertex in world space and then multiplying by normal matrix to get oriented in eye space.


void main()
{
	PositionEye = vec3(u_ModelViewMatrix * vec4(in_Position, 1.0));
	NormalEye =  u_NormalMatrix * in_Normal;
	TexCoord = in_TexCoord;
	
	vec3 PosWorld = vec3(u_ModelMatrix * vec4(in_Position, 1.0));
	vec3 lightDirWorld = normalize(GlobalLightPos - PosWorld);
	LightDirEye = u_NormalMatrix * lightDirWorld;	
	
	gl_Position = u_MVP * vec4(in_Position, 1.0);
}

Fragment Shader:

Basically the same, doing normalising here instead of vertex shader.


vec3 shadePixel(vec3 diff)
{
	vec3 s = normalize(LightDirEye);
	float sDotN = max(dot(s, normalize(NormalEye)), 0.0);
	vec3 diffuse = vec3(1.0, 1.0, 1.0) * diff * sDotN;

	return diffuse;
}

void main()
{
	vec3 diff = texture(Texture, TexCoord * TexRepeat);

	out_Position = PositionEye;
	out_Normal = normalize(NormalEye);
	out_Diffuse = shadePixel(texture(Texture, TexCoord * TexRepeat));
	out_Tangent = Tangent;
	out_Binormal = Binormal;
}

Now this seems to be creating a weird effect, half of my world is shaded which looks normal, the other half is just completely black!?!

Looks good except for this line:


vec3 lightDirWorld = normalize(GlobalLightPos - PosWorld);
LightDirEye = u_NormalMatrix * lightDirWorld;

Two problems:

1) Again, it doesn't make sense to have a direction light with a position.

2) The second line is taking a world space direction and transforming it into world space again and then into eye space.

You need to take you world space light DIRECTION and put into eye space and make it available to the pixel shader...


glm::mat4 modelMatrix = modelNode->GetTransform();
glm::mat4 modelView = viewMatrix * modelMatrix;
glm::mat4 mvp = m_renderSystem->GetCamera()->GetProjectionMatrix() * modelView;
glm::mat3 normalMat = glm::transpose(glm::inverse(glm::mat3(modelView)));
glm::vec3 lightDirEye = glm::transpose(glm::inverse(glm::mat3(viewMatrix))) * lightDirWorld;

m_graphics->SetUniform(id, "u_NormalMatrix", 1, false, normalMat); 
m_graphics->SetUniform(id, "u_ModelMatrix", 1, false, modelMatrix);
m_graphics->SetUniform(id, "u_ModelViewMatrix", 1, false, modelView);
m_graphics->SetUniform(id, "u_MVP", 1, false, mvp);
m_graphics->SetUniform(id, "u_LightDirEye", 1, false, lightDirEye);

// Note:  I am extrapolating the likely syntax here, I've never actually used OpenGL or glm.
// The intent should be obvious...get the world space light direction into eye space and
// send it off to the GPU.

New vertex shader:


void main()
{
	PositionEye = vec3(u_ModelViewMatrix * vec4(in_Position, 1.0));
	NormalEye =  u_NormalMatrix * in_Normal;
	TexCoord = in_TexCoord;	
	gl_Position = u_MVP * vec4(in_Position, 1.0);
}

New pixel shader:


vec3 shadePixel(vec3 diff)
{
	float sDotN = max(dot(u_LightDirEye, normalize(NormalEye)), 0.0);
	vec3 diffuse = vec3(1.0, 1.0, 1.0) * diff * sDotN;

	return diffuse;
}

void main()
{
	vec3 diff = texture(Texture, TexCoord * TexRepeat);

	out_Position = PositionEye;
	out_Normal = normalize(NormalEye);
	out_Diffuse = shadePixel(texture(Texture, TexCoord * TexRepeat));
	out_Tangent = Tangent;
	out_Binormal = Binormal;
}

That should do it. You will of course have to declare the appropriate uniform in the pixel shader for u_LightDirEye and make sure the CPU side code has a world space lighting direction to work with.

Cheers I'll give it a shot when I get home from work.

Awesome mate, seems to be working fine now! Cheers for the help.

This topic is closed to new replies.

Advertisement