GLSL Core Tutorial – Lighting
|Prev: Color Example||Next: Dir Lights per Vertex I|
Lighting is essential in computer graphics. Scenes without lighting seem too flat, making it hard to perceive the shape of objects. In here we will explore the basic lighting and shading models.
A lighting model determines how light is reflected on a particular point. The perceived colour on that point depends on a number of parameters, for instance, the light direction, the viewer direction, the properties of the material, to name a few.
A shading model is related to how lighting models are used to lit a surface. For instance, we can compute a single colour value per triangle, Flat shading; compute the colour for the vertices of a triangle and interpolate the colour values for points inside the triangle, Gouraud shading; or even compute the colour for every surface point, Phong shading.
Lighting is deeply related to colour. When an object is lit we observe colour, otherwise, if no light reaches an object looks completely black.
Colour in CG is composed of several terms, namely:
- diffuse: light reflected by an object in every direction. This is what we commonly call the colour of an object.
- ambient: used to simulate bounced lighting. It fills the areas where direct light can’t be found, thereby preventing those areas from becoming too dark. Commonly this value is proportional to the diffuse colour.
- specular: this is light that gets reflected more strongly in a particular direction, commonly in the reflection of the light direction vector around the surface’s normal. This colour is not related to the diffuse colour.
- emissive: the object itself emits light.
The next figure shows the effect the first three color component when an object is lit. To define an object’s material we define values for each of the above components.
Figure: From left to right: diffuse; ambient; diffuse + ambient; diffuse+ambient+specular
Lights come in many packages as well. The most common light types, and easier to implement, are: directional, point, and spotlights.
In a directional light, we assume that all light rays are parallel, as if the light was placed infinitely far away, and distance implied no attenuation. For instance, for all practical purposes, for an observer on planet earth, the light that arrives from the sun is directional. This implies that the light direction is constant for all vertices and fragments, which makes this the easiest type of light to implement.
Point lights spread their rays in all directions, just like an ordinary lamp, or even the sun if we were to model the solar system.
Spotlights are point lights that only emit light in a particular set of directions. A common approach is to consider that the light volume is a cone, with its apex at the light’s position. Hence, an object will only be lit if it is inside the cone.
Figure: Light types. From left to right: directional, point, and spotlights.
There are many approaches to compute the light that is reflected by an object towards the camera, or eye. To compute this value for a particular point we need to know where does the light come from, what is the angle between the light vector and the surface normal (a vector perpendicular to the surface), where is the camera, and some other settings. The equation that allows us to compute the colour, or reflected light for a particular point dictates which settings will be used and how.
As mentioned before our options are not limited to the lighting equation. Considering a triangle we can compute the lighting for each vertex, and then use interpolation for each fragment inside the triangle. Or we can interpolate all the available vertex data, send it to the fragment shader and then compute the colour per fragment. These shading models are orthogonal to the lighting equations, in the sense that we can combine any equation with any of the above shading models to lit our scene.
When fixed functionality was available, no need to write shaders, lighting was computed per vertex. The colour of each vertex was then send to the rasterization and interpolation phases where the colour was interpolated for each computed fragment. The fixed functionality was an implementation of the Gouraud shading model. The results were not that great since interpolation is not the best way to compute lighting which can vary in a multitude of ways inside a polygon. Imagine a spotlight hitting the centre of a triangle, but none of its vertices. The result would be a unlit triangle, which is clearly incorrect.
Gouraud shading made sense back when we typically had a lot more pixels than vertices, and way less computing power. Nowadays we have a lot more computing power, which in turn allows us to use larger and larger numbers of polygons in a scene. These two features combined with the poor results of lighting per vertex led to the current prevalent trend of performing lighting per pixel, i.e. the Phong shading model.
All the above concentrates on the point being lit, the light sources and the camera position. But what about shadows? That would imply testing if any other object is in the path from the point being lit to each light source. And why can’t other objects act as light sources themselves, reflecting the light that hits them towards our point, thereby contributing to its final colour? And refractions? Lighting is a very complex issue, some may even say that lighting is THE issue in computer graphics.
Anyway, we have to start somewhere, so we are sticking with the simple assumption that we are lighting a point, without considering any interaction with the remaining geometry, only the camera and the light sources. In the next sections we’re going to see how to write shaders to simulate all the above mentioned types of lights, both per vertex and per pixel.
Source code for all the light types, and shading models, is available: L3DLighting. The zip file includes a Visual Studio 2010 project. To try the different light/shading model combinations see function “setupShaders”.
|Prev: Color Example||Next: Dir Lights per Vertex I|