Home   Donators   Contact Us       

<< Previous Tutorial     Next Tutorial >>

Tutorial 4 - Switch on the lights


Theory

The art of good 3D lighting effects are achieved by making use of vectors. A vector can be defined as a quantity that has a direction and a magnitude. When vectors are used in 3D lighting effects, we are really only concerned with the direction of vectors and not so much with the size of the vector.

Each polygon you create for your scene or model has a normal vector. The normal vector of your polygon, is the direction perpendicular to the surface of the polygon (the direction that your polygon is facing). For example, if you look towards a cube, the direction of the front polygon's normal is directed towards you.

To make use of OpenGL's lighting features, you add "lights" (or light sources) to your scene. Lights has a position and ambient, diffuse and specular properties.

OpenGL makes it super easy to add light sources to your scene. To add a light source to your scene, all you need to do is to give it a position; configure a few settings such as the ambient, specular and diffuse values; set up the normals for your polygons; and OpenGL will automatically perform all the complex maths required to make your model shine.

Ambient light, is the light that is every where present in your scene, and is distributed equally to all polygons. The direction of your polygon normal does not affect the ambience. The brighter the ambient light, the brighter ALL polygons and models will appear to be in your scene.

Diffuse light comes from a particular point source, such as the sun. It determines how bright your polygon will be, based on the direction of your polygon's normal vector. You can imagine for yourself that if your polygon is facing towards a diffuse light source it will appear to be brighter than when it is facing away from the light source.

Specular light also comes from a single point, but reflects differently than diffuse light. Specular light uses the properties of a material to determine brightness. For example, a smooth surface such as a metal will reflect specular light better than a dull surface such as plaster on a brick wall.

Light and color, when applied correctly, can really give your models and scenes exceptional beauty. As a habit, learn to experiment a lot with light and colors, for it will help you become a great 3D programming artist.

Tutorial Steps
Tutorial created with Real Studio 2011 Release 4.3.
1. Open Real Studio.
2. Choose the "Desktop" project template.
3. Add an OpenGLSurface control to Window1.
4. Resize and position OpenGLSurface1 to fill the whole form.
5. Tick the LockRight and LockBottom properties of OpenGLSurface1. This is required to raise the Resized event and set up the perspective projection settings.
6. Add the following code to the Open event of OpenGLSurface1 (You can access the Open event by double clicking on OpenGLSurface1 and then selecting the event from the list):
' make sure only back faces are culled and enable culling

OpenGL.glCullFace OpenGL.GL_BACK
OpenGL.glEnable OpenGL.GL_CULL_FACE

' instantiate memory blocks used to configure our light with

Dim light_position As new MemoryBlock(16)
Dim light_ambience As new MemoryBlock(16)
Dim light_diffANDspec As new MemoryBlock(16)

' define position of the light

light_position.SingleValue(0) = 0.0
light_position.SingleValue(4) = 0.0
light_position.SingleValue(8) = 1.0
light_position.SingleValue(12) = 0.0

' define ambience of the light

light_ambience.SingleValue(0) = 0.0
light_ambience.SingleValue(4) = 0.0
light_ambience.SingleValue(8) = 0.0
light_ambience.SingleValue(12) = 1.0

' define diffuse and specular of the light

light_diffANDspec.SingleValue(0) = 1.0
light_diffANDspec.SingleValue(4) = 1.0
light_diffANDspec.SingleValue(8) = 1.0
light_diffANDspec.SingleValue(12) = 1.0

' apply light settings

OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_POSITION, light_position ' set position
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_AMBIENT, light_ambience ' set ambience
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_DIFFUSE, light_diffANDspec ' set diffuse
OpenGL.glLightfv OpenGL.GL_LIGHT0, OpenGL.GL_SPECULAR, light_diffANDspec ' set specular

' enable our light

OpenGL.glEnable OpenGL.GL_LIGHT0

' enable lighing

OpenGL.glEnable OpenGL.GL_LIGHTING

' enable overwriting of material properties with vertex colors

OpenGL.glEnable OpenGL.GL_COLOR_MATERIAL
7. Add the following code to the Render event of OpenGLSurface1 (You can access the Render event by double clicking on OpenGLSurface1 and then selecting the event from the list):
OpenGL.glPushMatrix ' save matrix

' clear the background

OpenGL.glClearColor(0, 0, 0, 1)
OpenGL.glClear(OpenGL.GL_COLOR_BUFFER_BIT)

' move back a bit so that we can see the object

OpenGL.glTranslatef 0.0, 0.0, -5.0

' rotate our model

OpenGL.glRotated(30, 1, 0, 0) ' 30 degrees around x-axis
OpenGL.glRotated(30, 0, 1, 0) ' and then 30 degrees around y-axis

' set color to green

OpenGL.glColor3d 0, 1, 0

' draw green cube

OpenGL.glNormal3d 0, 0, 1 ' front polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' front polygon
OpenGL.glVertex3d 1, 1, 1
OpenGL.glVertex3d -1, 1, 1
OpenGL.glVertex3d -1, -1, 1
OpenGL.glVertex3d 1, -1, 1
OpenGL.glEnd

OpenGL.glNormal3d 1, 0, 0 ' right polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' right polygon
OpenGL.glVertex3d 1, 1, 1
OpenGL.glVertex3d 1, -1, 1
OpenGL.glVertex3d 1, -1, -1
OpenGL.glVertex3d 1, 1, -1
OpenGL.glEnd

OpenGL.glNormal3d -1, 0, 0 ' left polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' left polygon
OpenGL.glVertex3d -1, 1, 1
OpenGL.glVertex3d -1, 1, -1
OpenGL.glVertex3d -1, -1, -1
OpenGL.glVertex3d -1, -1, 1
OpenGL.glEnd

OpenGL.glNormal3d 0, 0, -1 ' back polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' back polygon
OpenGL.glVertex3d 1, 1, -1
OpenGL.glVertex3d 1, -1, -1
OpenGL.glVertex3d -1, -1, -1
OpenGL.glVertex3d -1, 1, -1
OpenGL.glEnd

OpenGL.glNormal3d 0, 1, 0 ' top polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' top polygon
OpenGL.glVertex3d 1, 1, 1
OpenGL.glVertex3d 1, 1, -1
OpenGL.glVertex3d -1, 1, -1
OpenGL.glVertex3d -1, 1, 1
OpenGL.glEnd

OpenGL.glNormal3d 0, -1, 0 ' bottom polygon normal

OpenGL.glBegin OpenGL.GL_POLYGON ' bottom polygon
OpenGL.glVertex3d 1, -1, 1
OpenGL.glVertex3d -1, -1, 1
OpenGL.glVertex3d -1, -1, -1
OpenGL.glVertex3d 1, -1, -1
OpenGL.glEnd

OpenGL.glPopMatrix() ' restore matrix
8. Add the following code to the Resized event of OpenGLSurface1 (You can access the Resized event by double clicking on OpenGLSurface1 and then selecting the event from the list):
' set the viewport rectangle

OpenGL.glViewport 0, 0, OpenGLSurface1.Width, OpenGLSurface1.Height

' set up the perspective projection settings

OpenGL.glMatrixMode OpenGL.GL_PROJECTION
OpenGL.glLoadIdentity
OpenGL.gluPerspective 60.0, OpenGLSurface1.Width / OpenGLSurface1.Height, 1, 100.0

' select and reset the modelview matrix

OpenGL.glMatrixMode OpenGL.GL_MODELVIEW
OpenGL.glLoadIdentity
9. Add the following code to the Paint event of Window1 (You can access the Paint event by double clicking on the titlebar of Window1 and then selecting the event from the list):
' refresh the OpenGL surface

OpenGLSurface1.Render
10. Save and run the project.

Code Analysis

In the OpenGLSurface1.Open event we make a call to glCullFace using the GL_BACK parameter. This instructs OpenGL to only draw polygons that faces toward us. Polygons facing away from us can be ignored since they are not visible. Passing GL_CULL_FACE to the glEnable function call, enables the feature of OpenGL to ignore away-facing polygons. Enabling this culling method saves a lot of processing time.

Next we instantiate three MemoryBlocks that will be used to set up our lighting settings. light_position will store the 3D position of our light in our 3D world. light_ambience stores the ambient properties of our light and light_diffANDspec stores the diffuse and specular values of our light. We use one MemoryBlock for both the diffuse and specular values, since the values for these two settings are exactly the same.

We assign the position of out light to the light_position MemoryBlock, with SingleValue(0) representing the x-coordinate, SingleValue(4) the y-coordinate and SingleValue(8) the z-coordinate. Our light will therefore therefore be positioned at (0, 0, 1).

In the light_ambience MemoryBlock, SingleValue(0) represents the red ambient value, SingleValue(4) the green value, SingleValue(8) the blue value and SingleValue(12) the alpha value. This gives us the black color (0,0,0,1). We will get into alpha values in a later tutorial. For a desciption on color values have a look at Tutorial 3 - A splash of color. In a similar fashion we assign a pure white color, (1, 1, 1, 1) to the light_diffANDspec MemoryBlock.

Once we've assigned our preferred position, ambient, diffuse and specular values to our MemoryBlocks, we apply these settings to our light with the calls to glLightfv. OpenGL allows you to configure up to 8 lights. The GL_LIGHT0 parameter indicates to OpenGL that we want to apply the settings to Light 0.

Now we can enable our light (light 0) by calling glEnable with the GL_LIGHT0 parameter. Then we enable OpenGL's lighting features with a call to glEnable, using the GL_LIGHTING parameter. To add a second light you simply repeat the process, but use the GL_LIGHT1 parameter instead of the GL_LIGHT0 parameter. Note that you only have to call glEnable with the GL_LIGHTING parameter once, and that it is not necessary to call it for each light you set up in your 3D world.

For our model to reflect its colors correctly, we need to enable GL_COLOR_MATERIAL with a call to glEnable. For now you don't have to worry about why this is required, but just take note that colors and materials are two different approaches to filling your polygons with some kind of a texture.

Experiment with the position and light settings. What happens when you set the ambient light to a pure white (1,1,1,1) or even a light red (1,0,0,1)? or change the position of your light to (2,0,1) and see what happens to the colors of the cube.

Project Downloads

<< Previous Tutorial     Next Tutorial >>


 
All the content on Real 3D Tutorials, with the exception of the SyntaxHighlighter which is licensed under the MIT License, is provided to the public domain and everyone is free to use, modify, republish, sell or give away this work without prior consent from anybody. Content is provided without warranty of any kind. Under no circumstances shall the author(s) or contributor(s) be liable for damages resulting directly or indirectly from the use or non-use of the content.
Should you find the content useful and would like to make a contribution, you can show your support by making a donation.