# OpenGL Yet another OpenGL Shading/Lighting Question

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

## Recommended Posts

Hello all.

I'm having huge difficulties working shadows and lighting into my engine. I've been working on it for a few weeks and having some difficulties understanding why it's not working as expected. I'm just trying to get lighting working first -- then I'll worry about shadows. I'm just trying to get the preliminary question for lighting out there before I start building out questions on shadows (though, it seems there are a myriad of options with shadow maps, stencil, GLSL, and I know a few others I probably haven't mentioned).

Mainly, my problem is that with directional lighting, the infinite light source always seems to focus at 0,0,0 in space... I'd like to translate the light, such that I can emulate something like sunlight coming from the different directions, based on the hour of the day.

Here's the general "rendering" that I'm doing (C# / OpenGL wrappers. I'd prefer to avoid the entire "C# sucks as a language" dialog -- I've been getting very good benchmarks out of it, even on a netbook without a graphics card (60fps).

 Graphics.BeginRenderingLayer(); { Video.MapRenderingMode(); Graphics.BeginLightingLayer( Graphics.AmbientR, Graphics.AmbientG, Graphics.AmbientB, Graphics.DiffuseR, Graphics.DiffuseG, Graphics.DiffuseB, pCenter.X, pCenter.Y, pCenter.Z ); { Graphics.BeginRenderingLayer(); { Graphics.CenterCamera( pCenter.X, pCenter.Y, pCenter.Z ); RenderMap( pWorld, pCenter, pCoordinate ); } Graphics.EndRenderingLayer(); Graphics.BeginRenderingLayer(); { Graphics.DrawMan( pCenter ); //TODO: Character Rendering (by layer) } Graphics.EndRenderingLayer(); Graphics.BeginRenderingLayer(); { Video.FlatRenderingMode(); //TODO: Render animation effects } Graphics.EndRenderingLayer(); } Graphics.EndLightingLayer(); } Graphics.EndRenderingLayer();

Begin/EndRenderlingLayer is Push/Pop Matrix. CenterCamera is a Translate on -x, -y, -z (to push the center into the viewing area). DrawMan just draws a billboarded dude to traverse my terrain. RenderMap draws the terrain with

Here's the relevant code for lighting (Begin Lighting layer is the call used to create it.)

 public static void BeginLightingLayer( float pAmbientRed, float pAmbientGreen, float pAmbientBlue, float pDiffuseRed, float pDiffuseGreen, float pDiffuseBlue, float pX, float pY, float pZ ) { Gl.glEnable( Gl.GL_LIGHTING ); Gl.glEnable( Gl.GL_NORMALIZE ); Gl.glEnable( Gl.GL_RESCALE_NORMAL ); Gl.glEnable( Gl.GL_LIGHT0 ); Gl.glShadeModel( Gl.GL_SMOOTH ); float[] AmbientLight = new float[4] { pAmbientRed, pAmbientGreen, pAmbientBlue, 1.0f }; float[] DiffuseLight = new float[4] { pDiffuseRed, pDiffuseGreen, pDiffuseBlue, 1.0f }; float[] PositionLight = new float[4] { pX + 5.0f, pY, -5.0f, 0.0f }; BeginRenderingLayer(); Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_AMBIENT, AmbientLight ); Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_DIFFUSE, DiffuseLight ); Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_POSITION, PositionLight ); //This doesn't help. //CenterCamera( pX, pY, pZ ); EndRenderingLayer(); Gl.glEnable( Gl.GL_COLOR_MATERIAL ); Gl.glColorMaterial( Gl.GL_FRONT_AND_BACK, Gl.GL_AMBIENT_AND_DIFFUSE ); }

The light always pivots around the 0,0,0 point -- so if the hero is to the right, it'll be on the right of it. If to the left, it'll be to the left. If I head infinitely left, the light is bright all the way there -- still focusing on lighting around 0,0,0 as the "center" of the scene, rather than the new center that was translated to.

Thoughts?

##### Share on other sites
Hi!

The variable pCenter is the position of your player, right?
You used a directional light, since your w-component is 0. This means what was set in
Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_POSITION, PositionLight )
is the direction of the light. If you want the light direction independent from your player’s world position, you shouldn’t let the direction depend on the position of the player. Here is your problem:
float[] PositionLight = new float[4] { pX + 5.0f, pY, -5.0f, 0.0f };
pX and pY are coming from pCenter.

You want to emulate sun light, right? So, perhaps something close to this will be what you want:
float[] PositionLight = new float[4] { cos(alpha)*sin(beta), cos(beta), sin(alpha)*sin(beta), 0 };
This little code converts the spherical coordinates of the sun (alpha = azimuth angle, beta = elevation angle) to Cartesian coordinates, which we use as direction. (Maybe you have to negate all 3 components, not sure which way around it is defined.)

Cheers!

##### Share on other sites
Hi Tsus,

Thank you for the help.

I'll test it out and let you know. The only thing that I'm worried about is that, again, the directional light seems to focus on 0,0,0 as the center of the screen. Does that make sense?

Let me show you a few pictures to explain.

If you notice in the first image, the light seems fine -- it's relative to the player and it's on one side. The second part -- the light stays in the same place, even though it's directional. The player just moved northwest a little bit. I've added the yellow line to help show where the light's gradient starts.

I will definitely be putting in the calculations for the azimuth, etc -- but in the very least, it shouldn't be so "set" in stone. Because if the character keeps walking west, the light will always make that side bright, regardless of time of day.

I'm almost wondering -- does light always center around 0,0,0 -- so rather than translating to the negative of the character to draw (saving having to do subtraction on each tile/etc relative to the player), should I be always adjusting it so that my scene is at 0,0,0 in the center, rather than the player's position = center of the screen?

Thanks! Hopefully that helps explain my dilemma!

##### Share on other sites
Hi again!

I'll test it out and let you know. The only thing that I'm worried about is that, again, the directional light seems to focus on 0,0,0 as the center of the screen. Does that make sense?

Directional light is independent from the position of the fragment/vertex being shaded, because the light itself doesn’t have a position. It only has a direction. OpenGL simply computes the dot product of the normal and the light direction, which is the cosine of the enclosed angle between the two, in order to compute the brightness. If the normal faces in the same direction as the light (enclosed angle=0) it is fully lit (1) and if it is perpendicular to the light direction (enclosed angle=Pi) it is dark (0). (If it faces away, it is clamped to zero.)

Let me show you a few pictures to explain.
If you notice in the first image, the light seems fine -- it's relative to the player and it's on one side. The second part -- the light stays in the same place, even though it's directional. The player just moved northwest a little bit. I've added the yellow line to help show where the light's gradient starts.

Your pictures look fine to me. They meet my expectation. I can’t see anything that’s wrong with them. The gradient only comes from the orientation of the normals. Your player position doesn’t change them, so walking around shouldn’t change the lighting.

I will definitely be putting in the calculations for the azimuth, etc -- but in the very least, it shouldn't be so "set" in stone. Because if the character keeps walking west, the light will always make that side bright, regardless of time of day.

Why is that? The lighting shouldn’t depend on the position of the tiles (being more west for instance). It also doesn't have a falloff, since it has no position and thus no distance to the shaded surface.

I'm almost wondering -- does light always center around 0,0,0 -- so rather than translating to the negative of the character to draw (saving having to do subtraction on each tile/etc relative to the player), should I be always adjusting it so that my scene is at 0,0,0 in the center, rather than the player's position = center of the screen?

Both ways should yield the same. (Directional lighting is independent from the position.)

Hopefully that helps explain my dilemma!

I rather think that I’m missing to see your problem. Since, obviously something isn’t working as you expect. Could you somehow rephrase the problem or show me more pictures? Thanks!

Cheers!

##### Share on other sites
Again, thank you for the response, Tsus.

So, my problem is that I want that lighting to stay the same each time -- like an overlay, I guess. So in the second picture, I'm expecting the light to lighten up the same relative area of tiles, compared to the character (i.e. to the right of him). Think like an overlay -- in a way, I was considering having the sunlight be an actual light that was always around relative to the player and adjusted based on the time of day and their location (morning = to the west, evening to the east, x += 20 in the morning; x -= 20 in the evening, or something similar).

Since you're a bit more familiar with how lighting works (and OpenGL -- thank goodness, I'm constantly learning), is there a good formula for calculating point-based normals -- I know that OpenGL has a few "surface" normal formulas that are public knowledge. I read a few forum posts where they suggested setting each vertex's normal to be the entire triangle (or geometry's)'s surface normal. I did this and had really funky results. My understanding is that normals always face the camera, correct? (they will be in my case, but they're supposed to be perpendicular to the plane they're on) Right now, my normals are just equivalent to the "height", i.e. normal = point; so x,y,z (tile x, tile y, height) = normal x, y, z. Maybe not correct, but it *seems* to work. I've considered that this may actually be the problem that I have. Perhaps normals need to be relative to the center of the screen 0,0,0 center, instead of conveying the x/y/z that I've been doing such, so far?

Hope that helps communicate it a little more clearly. I owe you a beer, sir.

##### Share on other sites

Again, thank you for the response, Tsus.

No problem.

So, my problem is that I want that lighting to stay the same each time -- like an overlay, I guess. So in the second picture, I'm expecting the light to lighten up the same relative area of tiles, compared to the character (i.e. to the right of him). Think like an overlay -- in a way, I was considering having the sunlight be an actual light that was always around relative to the player and adjusted based on the time of day and their location (morning = to the west, evening to the east, x += 20 in the morning; x -= 20 in the evening, or something similar).

Mhm, so the light should be relative to the player. You mean, so that it fades out to the borders of the screen? That would be doable with a spot light. You could places the light relative to the player like this:

float[] PositionLight = new float[4] { r*cos(alpha)*sin(beta) + pX, r*cos(beta) + pY, r*sin(alpha)*sin(beta) + pZ, 1 }; Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_POSITION, PositionLight );
r is the distance to the player and the addition of the players position takes care for the light being relative to the player position. (Note we use w=1). So we the light is placed on a sphere with radius r around the player.

float[] SpotDirectionLight = new float[4] { -cos(alpha)*sin(beta), -cos(beta), -sin(alpha)*sin(beta), 1 }; Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_SPOT_DIRECTION, SpotDirectionLight);
This sets the direction of the spot. It points from the point on the sphere towards (0,0,0). So, the player is in the center of the spot light.

Additionally we have to set the opening angle of the spot light (cutoff) and a parameter controlling how smoothly the light fades out towards the boundary of the spot cone (exponent).
Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_SPOT_CUTOFF, 10.0); Gl.glLightfv( Gl.GL_LIGHT0, Gl.GL_SPOT_EXPONENT, 50.0);

Is this what you're looking for?

is there a good formula for calculating point-based normals -- I know that OpenGL has a few "surface" normal formulas that are public knowledge. I read a few forum posts where they suggested setting each vertex's normal to be the entire triangle (or geometry's)'s surface normal. I did this and had really funky results.

Yes, that’s called a face normal. What do you mean with funky results? When you use faces normals you should get a constant lighting over an entire triangle. If you want to have smoother results (i.e. the lighting being interpolated across the triangle) you should use vertex normals. You can compute those by averaging the normals of the four or eight adjacent vertices.

My understanding is that normals always face the camera, correct?

They don’t automatically do that. Normals always stay the same. If you move behind a triangle the normal faces away from the viewer.

Right now, my normals are just equivalent to the "height", i.e. normal = point; so x,y,z (tile x, tile y, height) = normal x, y, z. Maybe not correct, but it *seems* to work. I've considered that this may actually be the problem that I have. Perhaps normals need to be relative to the center of the screen 0,0,0 center, instead of conveying the x/y/z that I've been doing such, so far?

Hm, this doesn’t sound right. As you already said, normals should be perpendicular to the surface that the triangle mesh is approximating. So, computing vertex normals as I explained above would be a good way to go. You are working with a height map, right? Then you could also use the cross product of its partial derivatives to estimate a normal.
First the partial derivatives:
height_x = (height[i+1, j] – height[i-1, j])/2; // Central difference of the height in x direction. height_y = (height[i, j+1] – height[i, j-1])/2; // Central difference of the height in y direction.
If your height map is a little noisy, you can use the Sobel operator to compute the derivatives. The Sobel operator does also compute central differences, but it blurs the result afterwards. On the boundaries you have to use forward/backward differences (see Finite Difference if you're not familiar with them).

Then create the tangent and bitangent vectors (heightScale scales your height map):
vec3 tangent = vec3 (1, heightScale * height_x, 0); vec3 bitangent = vec3 (0, heightScale * height_y, 1);
The normal is perpendicular to them (so we take the cross product) and afterwards we normalize it.
Normal[i,j] = normalize( cross( tangent, bitangent) );
If you expand the cross product it becomes even simpler:
Normal[i,j] = normalize( vec3( heightScale * height_x, 1, heightScale * height_y) );

Hope that helps you a little.

Cheers!

##### Share on other sites
You have been immensely helpful, thank you. Let me play with this and see if I can get the results I want. I'm assuming the smartest thing to do with normals, is to cache them in the engine to save calculation time, right?

Let me see if I can get a result I want from the spotlight and/or the normals, and then I'll definitely be hitting you up with questions about an efficient shadow-rendering method, hehe.

Thank you *so* much again!

##### Share on other sites
Tsus, again, THANK YOU. I've been playing with the code that you posted earlier and managed to alter/get it working for my uses (nothing wrong with your code, obviously, but have to tailor it to my engine).

My question at this point revolves around actually writing some shadow code. I've seen a *lot* of stuff circulating -- most of it fairly old (circa 2000 or 2002 at best). I'm looking to do very simple shadows -- nothing too crazy with multiple light sources -- just the sun. Is the most efficient way to go about this by rendering the scene again from the light's viewpoint into the stencil buffer and then rendering it on top of the scene? I'm trying to go for as efficient and lightweight as possible, even if the shadows aren't *completely* perfect. Just so long as they're not glaringly wrong.

##### Share on other sites
You’re welcome.

With stencil shadows the scene is rendered three times from the viewer’s point of view. First you write the depth and only do ambient lighting. Second you fill with two-sided stencil mode your stencil buffer by rendering a shadow volume that was extruded from the objects silhouettes. (If your viewer can be placed inside a shadow volume, you have to use Carmack’s “z-fail shadow volumes”.) And finally you render the lit areas, based on the values in your stencil buffer. Stencil shadows are a little expensive since you have to build up the geometry for the shadow volumes each time an object or the light moves.

With shadow maps you render a depth map from the sun and later compare this depth to the actual distance of an object to the light. If the objects distance is bigger than something is blocking the light. In your case this might be easier and more suited, since your view is limited to a small depth range. For the rendering from the sun’s view you need to create a view and projection matrix. Usually you try to place its frustum as tight around the visible scene as possible (aka pan-caking). You could have a look at the slides and the audio-track of David Tuft’s talk at Gamesfest in 2010. He explains shadow mapping (and many of its extensions) nicely. I would recommend implementing it with GLSL, but it is also possible with the fixed function pipeline. The GLSL implementation here directly extends from the fixed function implementation. Though it does not apply a depth-bias. The necessity for the depth-bias is explained in the slides above and I posted a bit on that a few weeks back here.

Cheers!

##### Share on other sites
Hey Tsus,

I'm having a hard time finding some good examples on the web. A lot of the stuff I'm finding is based on using GLUT and GLSL. I think I can pull off using GLSL, but trying to avoid GLUT since it has become deprecated. The stuff you have posted is poorly organized (at least the actual implementations). The slides and theory behind it all makes sense -- essentially we render the depth buffer (looking from the light's point of view) to an image, and then project the image back onto the resulting image. A lot of stuff I've found has Peter Panning, fragments, and other crap, unfortunately.

I'm not doing any Frustrum projections -- just Ortho.

Are there any good examples with decent amounts of documentation? Most stuff is so old, I can't even run it and the libraries are difficult to find!

At the moment, the best example I've seen is:

But the code requires Glut32.dll and I'm still trying to understand which of the extensions he's including are common now... Since the code was written in 2002 and he's referencing geForce 6x cards at best, I believe. Was hoping to just have a shadow projection in it's own texture/layer and only render as much as necessary (i.e. like a rasterization). Not sure if this is the optimal way of doing it either... any thoughts or pointers?

Thank you, so much!

• ### Game Developer Survey

We are looking for qualified game developers to participate in a 10-minute online survey. Qualified participants will be offered a \$15 incentive for your time and insights. Click here to start!

• 16
• 11
• 24
• 43
• 75