Jump to content

  • Log In with Google      Sign In   
  • Create Account

Directional lighting problems with transforming


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
8 replies to this topic

#1 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 18 August 2013 - 06:19 AM

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.



Sponsor:

#2 nonoptimalrobot   Members   -  Reputation: 416

Like
1Likes
Like

Posted 18 August 2013 - 10:01 AM

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.



#3 nonoptimalrobot   Members   -  Reputation: 416

Like
1Likes
Like

Posted 18 August 2013 - 10:07 AM

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.



#4 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 18 August 2013 - 05:41 PM

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.



#5 nonoptimalrobot   Members   -  Reputation: 416

Like
0Likes
Like

Posted 18 August 2013 - 06:37 PM

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.



#6 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 19 August 2013 - 12:35 AM

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!?!


Edited by rocklobster, 19 August 2013 - 12:44 AM.


#7 nonoptimalrobot   Members   -  Reputation: 416

Like
1Likes
Like

Posted 19 August 2013 - 11:04 AM

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.


Edited by nonoptimalrobot, 19 August 2013 - 11:05 AM.


#8 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 19 August 2013 - 07:43 PM

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



#9 rocklobster   Members   -  Reputation: 415

Like
0Likes
Like

Posted 20 August 2013 - 04:29 AM

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






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS