Greg Dolley’s Weblog

A Blog about Graphics Programming, Game Programming, Tips and Tricks

Archive for December 21st, 2007

There’s Nothing Wrong with OpenGL’s Specular Lighting!

Posted by gregd1024 on December 21, 2007

Most beginners to OpenGL make a common mistake implementing specular lighting when the camera can move around freely in a first-person perspective. What happens is this: a light beam illuminating some surface will move across that surface in the exact opposite direction of the camera’s viewing vector. I’ve written this OpenGL app to illustrate:

engine_screenshot_9

Figure 1: green light beam shines across middle of left wall. Camera is looking straight ahead. Click for larger image.

engine_screenshot_8

Figure 2: now, when the camera is tilted up, the green light beam shines across the bottom of the left wall. Top beam on ceiling gets brighter while floor beam disappears. Click for larger image.

engine_screenshot_10

Figure 3: now the camera is tilted down and the green light beam shines across the top of left wall. Ceiling beam disappears and floor beam reappears brighter than in Figure 1. Click for larger image.

Keep in mind, the camera’s x, y, and z coordinates did not change in any of these shots, only the viewing angles changed. Therefore it’s quite obvious that this is not how things should work. The light beam should not move regardless of where I’m looking in the 3D world.

So why does this happen? Well, think about it – am I really “looking” around in the 3D world? Is the camera’s position changing when I go forward, backwards, left, or right as far as OpenGL is concerned? If you answered yes, think again. Remember, OpenGL has no concept of a “camera” – we just track the camera’s location internally in our code and then transform every polygon in the opposite direction. If the camera moves forward, we transform all polygons backward. If the camera looks 20 degrees to the right, we transform all polygons 20 degrees to the left. In other words, the camera really stays at (0, 0, 0) while the polygons are transformed around it. Now here’s the kicker – OpenGL transforms the polygons and light positions around the camera, but by default does not rotate the specular reflection angles to match the current view transform (i.e. the light vectors are not transformed into camera/eye coordinates like everything else). When this happens the light vectors (not light positions) are just like the camera – they don’t move with the rest of the world, the world moves around them. It is like having a flashlight floating in the middle of a room pointing in one direction while the room rotates around it; naturally, the light’s beam would slide across the wall surfaces depending on how they were moving. On the other hand, if the flashlight rotates with the room then the beam of light would always be pointing to the same spot. It is the first scenario (where the light can’t rotate) that makes this OpenGL effect occur.

Why specular angles are not transformed by default, I don’t know, but if you do, please leave a comment on this post. Although I suspect it has something to do with the fact that after OpenGL’s inception in 1992 and up until the late 90’s, all of the OpenGL programs I saw were demos of object modeling where the camera’s viewpoint never changed. In this case, if you transformed the light vectors to match the object’s orientation you’re going to get the wrong effect – you want those vectors to stay static looking straight ahead (directly down the -z axis).

So how do you tell OpenGL to transform the specular angles along with everything else? Simple – you must add another lighting model by calling one of the glLightModel functions. I prefer glLightModelf() simply because floating points are OpenGL’s default data type. Set the first parameter to GL_LIGHT_MODEL_LOCAL_VIEWER, and the second parameter to “1” (or anything non-zero) like this:

glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0f);

I place this call whenever I need to initialize or reinitialize lighting in the engine. For the screenshots in this article I used the following lighting models:

GLfloat dim_light[] = {0.0f, 0.0f, 0.0f, 1.0f};

 

glLightModelfv(GL_LIGHT_MODEL_AMBIENT, dim_light);

glLightModelf(GL_LIGHT_MODEL_LOCAL_VIEWER, 1.0f);

glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR);

The following screenshots show how the rendering looks with the new lighting model added.

engine_screenshot_17

Figure 4: Now when looking upward notice how the green light beam on the left wall stays centered. Click for larger image.

engine_screenshot_18

Figure 5: just like Figure 4 but looking downward. The green light beam is still centered on the left wall. Click for larger image.

That’s it! One small change does it all. 🙂

However, there’s one big disadvantage of this method that you may have noticed from the previous two screenshots. The polygon tessellation becomes much more noticeable depending on what angle a light hits a surface – even if you’re not that close to a polygon. And believe me, these last two screens hide the problem pretty well. Solution? Write a shader so that we can have complete control of the rendering pipeline. That’s one of the things I’ve been wanting to improve on this engine but haven’t got around to it yet. I took a detour doing the Quake 2 Port which took a lot of time and have recently been doing intense research on graphics programming for the Windows Mobile 6 OS (i.e. Pocket PC and SmartPhone development). So I’ll write a separate article on the shader solution later on.

As always, you can get automatic updates whenever I post new articles via the RSS feed (subscribe here), or if you prefer email updates, click here.

Thanks for reading! 😉

-Greg Dolley

*Special thanks goes out to Luke Ahearn for those nice looking textures! 🙂

Advertisement

Posted in 3D Graphics | 9 Comments »

FYI: OpenGL and 3D Graphics Programming Categories

Posted by gregd1024 on December 21, 2007

I was recently asked what things would go under the “OpenGL” category instead of “3D Graphics Programming” since OpenGL is used for 3D. That’s a good question, so here are the guidelines – if I write something that deals with using OpenGL for programming, it will go under the “3D Graphics Programming” category. If I write something concerning OpenGL in and of itself (i.e. OpenGL driver installs, OpenGL hardware support, etc.), it will go under the “OpenGL” category.

Update (1/9/2008): I’ve decided to change one of the rules. If there’s a post regarding 3D graphics programming and the program utilizes OpenGL, that post will be filed under both the “OpenGL” and “3D Graphics Programming” categories.

-Greg Dolley

*Get new posts automatically! Subscribe via RSS here . Want email updates instead? Click here.

Posted in Miscellaneous | Leave a Comment »