Jump to content
  • Advertisement
Sign in to follow this  
Eldritch

OpenGL Overcoming the limitations of OpenGL lights, second coming

This topic is 4411 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

With apparent acts of crime against the crown, my last inn was closed down, so here goes again, and I will try my outmost to not anger her Majesty this time ;) I wrote a couple of shaders that should light my scene with diffuse and specular lighting after the initial ambient pass where only ambient lighting is used. But, as can be seen below in the screenshot, the lighting is quite off the chart. Here are my shaders: Vertex
varying vec3 normal;
varying vec3 lightDir;

uniform vec4 LightPosition;

void main()
{
	normal = gl_NormalMatrix * gl_Normal;
	
	vec4 eyePos = gl_ModelViewMatrix * gl_Vertex;
	
	lightDir = normalize(vec3(LightPosition - eyePos));
	
	gl_Position = ftransform();
	
	gl_TexCoord[0] = gl_MultiTexCoord0;
}


Fragment
uniform sampler2D TexMap1;
uniform vec4 LightDiffuse;
uniform vec4 LightSpecular;

varying vec3 normal;
varying vec3 lightDir;

void main()
{
	vec4 diffuse = LightDiffuse * gl_FrontMaterial.diffuse;
	vec4 specular = LightSpecular * gl_FrontMaterial.specular;
	
	vec4 texel = texture2D(TexMap1, gl_TexCoord[0].st);
	
	vec4 color = diffuse * texel * max(dot(normal, lightDir), 0.0) + specular;
	
	gl_FragColor = color;
}


The rendering loop looks like this:
void CRenderer::Render3D()
{
	// Render ambient pass.
	glDisable(GL_BLEND);
	iActiveLight = -1;
    iAmbientPass = true;
	float col[4] = {0, 0, 0, 1};
	glLightfv(GL_LIGHT1, GL_DIFFUSE, col);
	glLightfv(GL_LIGHT1, GL_SPECULAR, col);
	float amb[4] = {0.1f, 0.1f, 0.1f, 1};
	glLightfv(GL_LIGHT1, GL_AMBIENT, amb);
	for (unsigned int i = 0; i < i3DObjects.size(); i++)
	{
		i3DObjects->Draw();
	}

	// Render light passes.
    iAmbientPass = false;
	glDepthFunc(GL_LEQUAL);
	glBlendFunc(GL_ONE, GL_ONE);
    glEnable(GL_BLEND);
	//for (unsigned int i = 0; i < i3DObjects.size(); i++)
	{
		for (int j = 0; j < 4; j++)
		{
			CLight* L = CScenegraph::Instance()->GetLight(j);
			if (L != NULL)
			{
				iActiveLight = j;
				glLightfv(GL_LIGHT1, GL_POSITION, L->pos);
				glLightfv(GL_LIGHT1, GL_DIFFUSE, L->diff);
				glLightfv(GL_LIGHT1, GL_AMBIENT, col);
				glLightfv(GL_LIGHT1, GL_SPECULAR, L->spec);
				glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, L->att[0]);
				glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, L->att[1]);
				glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, L->att[2]);

				for (unsigned int i = 0; i < i3DObjects.size(); i++)
				{
					i3DObjects->Draw();
				}
			}
		}
	}
	glBlendFunc(GL_SRC_ALPHA, GL_ZERO);
	glDepthFunc(GL_LESS);
}


The rendering of objects looks like this:
void CMesh::Draw()
{
	for (unsigned int i = 0; i < iMeshObj.size(); i++)
	{
		float zero[4] = {0, 0, 0, 1};
		if (CRenderer::Instance()->IsInAmbientPass() == true)
		{
			glMaterialfv(GL_FRONT, GL_DIFFUSE, zero);
			glMaterialfv(GL_FRONT, GL_AMBIENT, iMeshObj->iAmbient);
			glMaterialfv(GL_FRONT, GL_SPECULAR, zero);
		}
		else
		{
			glMaterialfv(GL_FRONT, GL_DIFFUSE, iMeshObj->iDiffuse);
			glMaterialfv(GL_FRONT, GL_AMBIENT, zero);
			glMaterialfv(GL_FRONT, GL_SPECULAR, iMeshObj->iSpecular);
			glMaterialf(GL_FRONT, GL_SHININESS, iMeshObj->iShininess);
		}

		// Check for frustum containment on the object bounding box.
		if (CFrustum::Instance()->CubeIsInFrustum(iMeshObj->iBBox.GetCenter(),
												  iMeshObj->iBBox.GetExtents().GetX()) == true)
		{
			// Check for and enable possible shader and/or multi-texture.
			if (iMeshObj->iMultiTexturing == true)
			{
				CRenderer::Instance()->EnableMultiTexturing(iMeshObj->iMaterial, iMeshObj->iMultiMaterial);
			}
			else
			{
				glActiveTextureARB(GL_TEXTURE0_ARB);
				glBindTexture(GL_TEXTURE_2D, CRenderer::Instance()->GetMaterial(iMeshObj->iMaterial));
			}

			if (CRenderer::Instance()->IsInAmbientPass() == false)
			{
				CShaderManager::Instance()->UseShader(iMeshObj->iShader);
			}
			else
			{
				CShaderManager::Instance()->UseShader("Ambient");
			}

			CShaderManager::Instance()->SetShaderParams(iMeshObj);

			// Check for frustum containment on the partition nodes.
			for (unsigned int j = 0; j < iMeshObj->iPartition.iNodes.size(); j++)
			{
				if (CFrustum::Instance()->CubeIsInFrustum(iMeshObj->iPartition.iNodes[j]->iBBox.GetCenter(),
														  iMeshObj->iPartition.iNodes[j]->iBBox.GetExtents().GetX()) == true)
				{
					glCallList(iMeshObj->iPartition.iNodes[j]->iDisplayList);
				}
			}

			CShaderManager::Instance()->NoShader();

			if (iMeshObj->iMultiTexturing == true)
			{
				CRenderer::Instance()->DisableMultiTexturing();
			}
		}
	}
}


And now, ladies and gentlemen, the scene... Notice the "+" the lights seem to be causing with the brightest part in the middle? Also, the lighting looks very much like some form of per-vertex lighting, which I find peculiar enough. I am not over-using any of the light's properties nor any of the material properties, as to cause some form of over-brightening effect, which can be seen in the screenshot. However, I am not reluctant to point out that the multiplication is probably off somehow, causing a sort of over-exposure effect. I am begging for help. I cannot figure out why the lighting would be cracking up like this. Rendering only 1 light pass works, but as soon as I go into more light passes, it starts to look like something out of this world :)

Share this post


Link to post
Share on other sites
Advertisement
My suggestion is most likely useless (they always are), but what happens if you do the calculations without the specular part?

Share this post


Link to post
Share on other sites
No suggestions are useless and all are welcome :)

Without the specular component in the shader, the lighting becomes less bright. There are still traces of the "+" shape of the lighting, and the light gets brighter the closer the light gets to the center of the screen, meaning that if I turn away from the light, everything becomes dark.

Share this post


Link to post
Share on other sites
Your specular calculation looks way off to me, all you're doing is passing in a uniform and multiplying it by another uniform. Your specular will just end up being constant for the whole scene. Then you just add this (probably quite large) constant onto all your lighting.

First, as mrbig suggested, remove the specular and check everything works. Then you'll have to write some proper specular calculations (you have to take light position and eye position into account, plenty of info on the internet if you look).

Share this post


Link to post
Share on other sites
Okey. I noticed that the problem with the "light getting darker the more I turn away from the light" exists even with the specularity on.

Share this post


Link to post
Share on other sites
Maybe you could try out the shaders in this tutorial: http://www.clockworkcoders.com/oglsl/tutorial5.htm
They worked fine for me.

Share this post


Link to post
Share on other sites
Hehehe, the story of my life. Things that work for others, simply don't work for me :)

With the clockworkcoders' tutorial, I got a lot of z-fighting (even with the depth-func at GL_LEQUAL as suggested yesterday), but the other anomalies remain.

Share this post


Link to post
Share on other sites
Looking through you're code I have a few suggestion and comments. Using

normal = normalize(gl_NormalMatrix * gl_Normal);

in your vertex code removes the possiblilty your vertex normals are too long. Similarly, using normalize(normal) in your fragment shader gets rid of problems with large polys. I'll ignore the specular term as it's just completely wrong (as already mentioned by OrangyTang). btw all the attenuation stuff for lights needs to be handled in your shaders as well if you expect that.

You use the parameter LightPosition to get your eye space light coord into your shader. I guess you are setting this in CShaderManager::Instance()->SetShaderParams(iMeshObj)? The openGl light positions are in gl_LightSource.position which aren't used by your shaders. Personally, if I write a shader that is supposeed to be a drop-in replacement for openGL fixed functionality I try to reuse the fixed functionality parameters as much as possible, i.e. get all the light parameters from the fixed func.

Another helpful link

http://www.lighthouse3d.com/opengl/glsl/index.php?pointlight

Share this post


Link to post
Share on other sites
I took the opportunity to actually sit down with pen and paper, and came up with a shader that handles three lights at any given time, and it looks beautiful! I am going to share them with the rest of you so you can avoid having to go through the hell I have been in these past days.

What you need to complement the shaders (notice, I am not claiming them to be optimal in any way, but they do their job, so I am happy) is a system to calculate the three lights in the scene that are closest to your object and set the parameters of GL_LIGHT1 to GL_LIGHT3 of those three lights.

Anyways, here are the shaders:

Vertex Shader

varying vec3 normal;
varying vec4 ecPos;

void main()
{
// Calculate normal space.
normal = normalize(gl_NormalMatrix * gl_Normal);

// Calculate view space.
ecPos = gl_ModelViewMatrix * gl_Vertex;

// Set vertex position.
gl_Position = ftransform();

// Set texture coord.
gl_TexCoord[0] = gl_TextureMatrix[0] * gl_MultiTexCoord0;
}




Fragment Shader

uniform sampler2D TexMap1;

varying vec3 normal;
varying vec4 ecPos;

void main()
{
// Calculate global ambience.
vec4 ambientGlobal = gl_LightModel.ambient * gl_FrontMaterial.ambient;

// Calculate normal.
vec3 n = normalize(normal);

// Initiate color to global ambience.
vec4 color = ambientGlobal;

// For each of the 3 lights affecting this object, calculate their color addition.
for (int i = 1; i < 4; i++)
{
// Calculate light's direction.
vec3 vec = vec3(gl_LightSource.position - ecPos);
vec3 lightDir = normalize(vec);

// Calculate light distance.
float dist = length(vec);

// Calculate half vector.
vec3 halfVector = normalize(gl_LightSource.halfVector.xyz);

// Calculate diffuse component.
vec4 diffuse = gl_FrontMaterial.diffuse * gl_LightSource.diffuse;

// Calculate ambient component.
vec4 ambient = gl_FrontMaterial.ambient * gl_LightSource.ambient;

// Calculate dot product between normal and light.
float NdotL = max(dot(n, normalize(lightDir)), 0.0);

// Calculate attenuation.
float att = 1.0 / (gl_LightSource.constantAttenuation +
gl_LightSource.linearAttenuation * dist +
gl_LightSource.quadraticAttenuation * dist * dist);

// Add attenuation and diffuse and ambient components.
color += att * (diffuse * NdotL + ambient);

// Calculate dot product between normal and half vector.
vec3 halfV = normalize(halfVector);
float NdotHV = max(dot(n, halfV), 0.0);

// Add specular component and calculate highlight.
color += att * gl_FrontMaterial.specular * gl_LightSource.specular *
pow(NdotHV, gl_FrontMaterial.shininess);
}

// Multiply color with texture color.
color = color * texture2D(TexMap1, gl_TexCoord[0].st);

// Set fragment color.
gl_FragColor = color;
}




And here is how the scene looks now, as it should :)



Finally! :) Thanks to all.

Share this post


Link to post
Share on other sites
A good idea is to divide the final color before multiplication with the texture color by the number of lights you are using the shader (in my case, by 3). It averages the values and makes it look more smooth and less washed out.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!