Modelling Multiple Light Sources –
Realistically Rendering Multiple Lights
with Variable Intensities.
Submitted in partial fulfillment of the requirements for the Bachelor of Science (Honours) degree of Rhodes University.
To my supervisor, Shaun Bangay, for showing me very simple solutions to very complex problems.
To my boyfriend Ivan, for providing support, for solving my Linux problems and for pointing out bugs in my code.
To Grant Miller, for listening to me complain, and for extensive advice and proof reading of my write-up.
Lighting is a significant factor in rendering a realistic 3D scene to a 2D screen. Standard techniques for lighting, however, restrict the number of lights available to eight, which is sufficient for most situations. In some cases, however, a scene will require more lights to be rendered correctly.
In this paper, we explore the techniques used for lighting a scene, and the solutions proposed for handling multiple light sources. We restrict our solutions to providing the correct intensity of light, shading and shadowing. We implement a standard solution, virtualised light sources (VLS), and an application-computing solution based on a Binary Space Partition Trees (BSP).
The VLS system is relatively simple to implement and provides the expected results – correct intensity and shading but low quality shadows, with short start-up times and reasonable frame rates. The BSP system is more complicated and cannot cope with very complex scenes. It provides excellent shadowing as well as the correct intensity and shading. The start up times are far longer, but the frame rate is comparable.
Multiple lighting can be handled in a variety of ways, with different trade-offs. The optimal solution would be application specific.
Computer graphics has come a long way from ping-pong and logo. Now we have immersive three-dimensional games, hyper-real teapots, and full-length computer animated movies. The drive in computer graphics has always been for better quality graphics at greater speeds. Pac-man was perfectly adequate a few frames a second, but connoisseurs of 3-D games such as quake or unreal tournament are now unsatisfied with refresh rates of 60 frames. We are fast approaching a requirement for accuracy and speeds beyond the threshold of human perception. Hardware has taken great leaps forward to support this demand, moving much of the processing to the graphics card. Hardware acceleration is a pre-requisite for many recent games. In fact, 3-D chips frequently use these 3-D games as benchmarks. Alongside the improvement of hardware, extensive research has been performed in developing and improving algorithms to render graphics.
One of the prevalent tendencies has been to remove the responsibility of calculation from the programmer to the package, and then from the package to the hardware. This simplifies application development, and more importantly, speeds up run times. But hardware cannot support infinite operations and some rule-of-thumb restrictions have developed. One of these is that cards are only required to support eight lights. Graphic APIs such as Open-GL echo this restriction in the interest of maintaining high speeds. There is nothing stopping a manufacturer from supporting more than eight lights, but this may well be a wasted resource – in the vast majority of cases, far fewer lights than that are needed. Clusters of lights – multiple bulbs in one fitting – can generally be approximated as a single light source with no loss of quality. In addition, brighter lights tend to drown out the effects of others nearby, and these secondary lights can often be safely ignored.
However there are cases where more than eight lights are genuinely needed. For instance, in a theatre we have as many as a hundred lights in a limited space and with comparable intensities, all of them with a significant effect on an object. Using this scenario as motivation, this project investigates methods of rendering scenes with multiple light sources. Open-GL, a popular graphics API, had been used throughout. We compare the results from the standard virtualised light source technique (explained in detail below) with those from a system that takes the effect of all lights into account. The scenario is limited to a static scene, but is required to cope with user manipulated changing intensities of lights. Both systems are investigated with regard to the accuracy of the shading and shadowing, the start up time and the frame rate.
Chapter 2 introduces the basic theory of rendering three-dimensional scenes. We investigate some of the mathematics used in three-dimensional scenes. We then explore the options available for manipulating and lighting objects in three dimensions and rendering them to a two-dimensional screen, with specific focus on the techniques used in the two systems.
Chapter 3 discusses various solutions for multiple light source scenarios. We determine various design considerations, and explore the design of aspects that are common to both systems.
Chapter 4 explores the specific design and implementation of a standard technique – virtualised light sources.
Chapter 5 explores the specific design and implementation of the new system developed using BSP Trees.
Chapter 6 presents the output of the two systems. We examine the intensity of light and shading produced and the quality of the shadowing. We compare the time taken in each case, and have a look at a few scenes produced by the BSP scene.
Chapter 7 draws some conclusions about systems with multiple light sources, and indicates areas for further study.
Any three-dimensional scene must be rendered to the screen in such a way that the user will find it satisfyingly true to life. This involves various conceptual steps. Firstly, we consider what exists in a scene. Objects have to be placed in their correct positions with the correct dimensions. Secondly, we consider what we see of a scene. The further surfaces that are obscured by the closer surfaces need to be removed from the view plane – a process known as hidden surface removal. Lastly we consider how we see it. This is the process this project focuses on – the scene needs to be lit.
Lighting plays a very large part in making a two dimensional representation appear truly three-dimensional. There are many different factors that contribute to creating a correctly lit scene. They can be broadly divided into local lighting concerns – the effect of a light on an object – and global lighting concerns – the effect of objects on other objects in the presence of a light.
- Local lighting concerns include the intensity of the light when it reaches the surface of the object, the correct shading of the object to represent its geometrical structure, and specular highlights – the reflection of the light off smooth surfaces.
- Global lighting concerns include shadowing of one object by another, reflections of other objects, and refraction of light through semi-transparent objects.
We first look at various geometrical structures present in a three-dimensional scene. We then consider some techniques used to realise realistic real time systems, including hidden surface removal, shading techniques and shadow generation techniques. We conclude this chapter with a discussion of non-real time systems.
Polygonal representation is the most popular method for representing objects in computer graphics. It is the method supported by Open-GL, and as such will be the only representation considered here. Polygonal representation consists of a structure of vertices, each indicating a point in three-dimensional space. These vertices define polygons. Open-GL requires polygons to be concave and planar in order to render them correctly, and therefore triangles are commonly used – they meet these restrictions automatically. This form obviously matches angular objects better than curved objects, but the ease of manipulation outweighs any drawbacks. In addition, by using interpolative shading and increasing the number of polygons, a polygon mesh can become indistinguishable from a curved surface. The basic manipulations of polygons, such as translation, scaling, rotation, etc. are well understood, and can be found in any book on three-dimensional graphics, such as Alan Watt’s Fundamentals of Three-Dimensional Computer Graphics [Watt]. Some of the calculations used in the systems implemented in this project are less common, and have been included here for clarity.
This is the basic unit of an object. A point is usually a vertex of a more complex polygon. It comprises of x, y and z components, specifying its relative distance from each of these three axes. Many graphic systems define x and y as the screen co-ordinates, and z as the depth, but the axes can be completely arbitrary without affecting manipulations. Manipulations of a polygon can be done on each vertex independently.
A line (or more accurately, a line segment) is a polygon of two vertices, but the term is often used in the sense of an edge – the line joining two adjacent vertices of a larger polygon. A line can be fully described by two end points; a ray (a line with a starting point that continues into infinity) is defined as a starting point and the direction of the line.
The point of intersection between two line segments is frequently needed. Determining this in three dimensions is only slightly more complex than the two dimensional equivalent. We outline this algorithm with reference to the above diagram (Figure 2-1):
- Let P1, P2, Q1, and Q2 denote the end points of the two line segments.
- Then vectors u = P1-P0, v = Q1-Q0, and w = P0-Q0.
- If the lines parallel (u and v in same or opposite directions), no intersection occurs.
- Let p, q, and r indicate arbitrary x, y or z-axes such that the two lines are not coplanar parallel to the p-r or q-r planes.
- Calculate the intersection parameters:
o Let d = u.p*v.q – u.q*v.p
o s = (v.p*w.q – v.q*w.p)/d
o t = (u.p*w.q – u.q*w.p)/d
- Calculate the intersection point, inter:
o Inter = P0 + s*u, or Q0 +t*v.
- If (P0.r + s*u.r) – (Q0.r + t*v.r) is not equal to zero – no intersection occurs in the third dimension.
- If the intersection is not within the line segments, no intersection occurs.
We will also often need to find the angle between two lines. We discuss a simplified algorithm used to accomplish this:
- let a be the normalised vector of one line, and b be the normalised vector of the second line.
- then anglebetween = arccos(dot product of a and b);
Infinite planes are flat surfaces that extend in all directions. While rarely present in a scene, they are invaluable for various calculations. A plane can be fully described by a point on a plane, and a vector perpendicular to the plane. This can be compactly expressed in the form a, b, c, d, where a, b, and c are the components of the normalised normal vector, and a*x + b*y + c*z + d = 0. This form allows for other values to be easily calculated.
We now outline some of the more pertinent calculations that are performed on planes. We use these algorithms extensively in this project, primarily in constructing a Binary Space Partition (BSP) tree and in determining whether an object is exposed to a light source or whether it is obscured.
This important fact is needed frequently. To determine where a point P is in relation to the plane, find:
- u = a*P.x + b*P.y +c*P.z + d
- If u > 0
o The point lies in front of the plane.
- If u = 0
o The point is in the plane
- If u < 0
o The point is behind the plane
Another vital piece of information in splitting polygons is the distance to the plane. This is therefore another significant algorithm. The shortest distance between a point P and the plane is simply:
- Abs (a*P.x + b*P.y + c*P.z + d)
The intersection of a line and a plane is important not only in creating a BSP Tree, but also in determining whether segments are shadowed.
With reference to the above diagram (Figure 2‑2) we discuss the algorithm necessary to find the point of intersection between a line segment and a plane:
- If the line is parallel to the plane, no intersection occurs.
- If the end points q and r do not lie on either side of the plane (or ray points away from plane), no intersection occurs.
- Calculate the intersection point, inter:
o Let direction = q – r.
o Let nDotD = dot product of normal and direction.
o Let nDotQ = dot product of normal and point q.
o Intersection = q + direction*(-(d + nDotL)/nDotD) (note that intersection, q and direction are vectors, d, nDotL, and nDotD are scalars).
Cones are the next geometrical primitives we discuss. These are pertinent to our project because they are used to model the effect of the light source.
A 3D cone, starting from a point, tapers outwards regularly, into infinity. It is defined by a starting point, a direction, and the angle of spread (see figure 2-3).
We now discuss the operations that we perform on cones and make use of in our project. Our discussion is centred on the fact that we consider the cone to be well suited to representing a light source.
From the perspective of our project, we need to determine whether an object is within the cone of a light source. To determine whether a point is within a cone:
- Determine the vector between the point and the starting point of the cone.
- Determine the angle between this vector and the direction of the cone.
- If this angle is less than the spread angle, the point is within the cone. If it is greater, then it is not.
This algorithm is relevant when we need to consider how close a ray is to the edge of the cone, in order to determine how much fall-off has occurred.
To find the angle between the edge of the cone and a ray from the starting point through a given point:
- Check that the point is in front of the cone, else return an error.
- Determine direction - the vector between the point and the starting point of the cone.
- Determine the angle between the direction and the direction of the cone.
- Return the difference between this angle and the spread angle.
When a three dimensional surface is projected onto a two dimensional screen, care must be taken to ensure that closer objects obscure objects that are further away, and not the other way round. This process is known as hidden surface removal. It is a well-explored field, and some of the major solutions are described here.
The algorithms that we discuss here are significant in creating real time systems, and present the alternatives available for a modified hidden surface removal algorithm to generate shadows, discussed in section 2.5.2.
This is the practice of removing all faces on an object with normals pointing away from the viewer – the ‘back’ faces of the object. It is not a complete solution in itself, but is an easy test to perform and can speed up another algorithm significantly.
This is conceptually the simplest of the algorithms, and paradoxically one of the hardest to implement. The idea is to draw the objects furthest from view first, and then the objects closer by – like an oil painter on a canvas. One simply overwrites the obscured sections of the old polygon with the new polygon; no calculations are necessary. In order to do this, the polygons must first be sorted in a back-to-front depth order. However, if polygons are naively sorted by an average or farthest depth, there will be cases when the representation will be incorrect – an example where this becomes strikingly apparent is the case where two objects that are both partially in front of and behind each other. [Hill, pg 592]
These problems can be solved by using octrees or BSP Trees to sort the polygons. Octrees are hierarchical data structures, specifying occupancy of cubic volumes of space (also known as voxels). When projected to 2D screen space, a quad tree is generated. Binary Space Partition (BSP) trees divide the space adaptively and are thus potentially more efficient than octrees. In these systems, the ‘problem polygons’ will automatically be subdivided in the creation of the trees, and painter’s algorithm will yield the correct results in all cases. [Watt, pg 190]
Also known as the depth buffer algorithm, this technique is very popular. The technique can be inefficient and is very memory intensive, but makes up for these concerns in its lack of complexity – it is simple and fast, and requires no sorting of polygons.
If the scene is regarded as being a three-dimensional space, with the x and y co-ordinates making up the screen face, the equivalent z value indicates the depth of the object from the screen. For every pixel to be rendered, the z-value is interpreted and compared to the value in the z-buffer. If the value is less than the stored value, the new object in closer to the viewer than the old object. The pixel is overwritten with the new value appropriate to the polygon, and the new z-value is entered into the buffer. [Watt, pg 22]
The amount of memory dedicated to each pixel in the buffer limits the accuracy with which depth can be determined – if 8 bits are used, surfaces closer than 1/256 of the total range to each other may not be distinguished, and the wrong face may be drawn to screen. One solution for reducing memory requirements involves limiting the size of the buffer to a fraction of the screen, and rendering each fraction separately. [Hill, pg 594-596]
The Z-buffer is often supported in hardware, making this a very attractive option for rendering packages, and is the technique used by Open-GL.
This algorithm is adapted from a rendering technique that takes advantage of linear spatial coherence to reduce memory traffic. The next pixel to be rendered is very often the same colour as the pixel that has just been rendered. A colour is loaded, and each subsequent pixel is flooded with that colour until the run of the polygon ends. In the standard form, when a pixel that is contained by more than one polygon is encountered, a user-defined priority level is used to determine which polygon is rendered. When being used as a hidden surface removal technique, this priority reflects the relative depth of the two polygons at that pixel. [Hill, pg 597]
Also known as Warnock algorithms, these methods take advantage of area coherence rather than simply linear coherence. The visible area is subdivided until each unit of area is ‘simple’ to render [Hearn & Baker, pg 268]. Each region is tested in turn to see if it meets the requirements for simplicity. If it is simple, it is drawn immediately. If not it is subdivided, and the process is repeated.
The various algorithms are variations on how ‘simple’ is defined and how the regions are subdivided. Quadrant subdivision, for example, defines ‘simple’ as an region with one face or no faces involved, or an region less than one pixel in area, and subdivides a problem region into four equal quadrants. Other definitions of ‘simple’ include:
- A region that is empty or less than one pixel in area – this simplifies the testing, but leads to very high levels of subdivision.
- A region with one dominant face (a face that is everywhere closer than other faces), or an empty region, or a region less than one pixel in area – testing for simplicity is much more complex, but subdivisions are limited.
In this section we discuss the various categories of light that combine to determine how an object is lit in a scene. We consider the appearance of an object in a rendered scene to be produced by the light that it emits, ambient light in the scene, diffuse light (diffuse reflection of light from a light source) and specular light (specular reflection of light from a light source).
Here, we provide a short introduction to this common taxonomy.
This is the light that is actually being emitted by the object. The object appears as a light source (although most systems allow the user to define emitters that will not be considered as light sources for other objects in the scene, and do not count towards the eight light maximum). An emitter is not affected by any other lights in the scene.
Ambient light is a catchall term for the amount of light that is present in the scene that comes from no particular direction. Every lit surface reflects and scatters the light in all directions, in turn illuminating other surfaces. Each light that is added to a scene will therefore increase the ambient light of the scene. This chaotic light affects all surfaces roughly equally, and thus is simply added to each object. [Bangay, pg 130]
Diffuse light is the light that comes from a particular source and is then reflected in all directions. The intensity of diffuse light is a function of the relative angles of the surface and the direction of the light to the surface – a surface that is face on to the light will be very well illuminated, while a surface that is edge on may not be illuminated at all. It is independent of the viewpoint.
This is light that is reflected off a surface in a particular direction. The shinier the surface, the more apparent this effect is. It is a function of the angle of the viewer to the surface, as well as the surface to the light, and is thus viewpoint dependent.
Shading indicates the difference in the effect of a light over the surface of an object. The use of shading in our project is vitally important in rendering objects in the presence of light sources realistically.
Constant shading calculates a single intensity for each polygon in the polygon mesh, and applies this to the whole polygon, creating a very artificial effect [Bangay, pg 141]. More popular techniques, such as Gouraud and Phong shading use interpolative methods. We consider these methods in greater detail below.
The intensity of light at a vertex is a function of the orientation of the vertex normal with respect to the light source and the eye. These values are interpolated linearly across the polygon interior. Specular highlights cannot be handled properly, and Mach bands can result because of discontinuities in derivatives across polygon boundaries. The quality of the image can be improved by increasing the number of polygons used to represent an object. [Watt & Watt, pg 23-24]
This interpolates the normal vectors across the interior of the polygon, and calculates the lighting equation at each pixel. This gives accurate highlights, and reduces the Mach banding effect, but is much slower than Gouraud shading. [Watt, pg 24]
Tesselation is the process of adaptive subdivision in order to refine the image. Instead of requiring the user to divide the object sufficient for lighting to be accurate, the algorithm tessellates problem areas itself. [Andrews]
The next consideration in creating realistic lighting is that of shadows. Every object in a scene that is lit will cast a shadow. With this in mind we need to critically evaluate the various techniques involved in their generation.
Rather than attempting a true global light solution, we consider performance-enhancing techniques in which shadows are added to a scene algorithmically. Some major techniques that attempt this include:
This technique treats shadows as if they are themselves polygons. The silhouette edge of the object is projected from the light source onto another object. This is relatively easy to do if the second object is a flat surface, and becomes rapidly more complex with increasing complexity of the second object. The colour of this shadow polygon is also difficult to determine. [Bangay, pg 188]
The light point is treated as if it were the viewpoint. Standard hidden surface removal algorithms are then applied. All surfaces that are not visible to that light are marked as being shadowed by that light.
A popular version of this is the shadow z-buffer - the depth information is calculated as with normal z-buffer techniques, taking the light as viewpoint. During rendering, if the equivalent z value in the lighting space is not visible, the object is rendered with diminished intensity. [Watt & Watt, pg166-172]
In a scan line algorithm, all polygons in the scene are projected onto a sphere surrounding the point light. If a polygon does not overlap with any other polygon, it is rendered as normal. If another polygon completely covers it, the polygon is rendered with appropriately reduced intensity. If it is partly covered, the polygon is subdivided, and tested each subdivision is tested.
Lights in these algorithms are treated as ideal points, and thus the shadows produced have hard edges. [Watt & Watt, pg 158-160].
This considers the volume of space that is shadowed, and tests objects to see whether they fall within this volume. A shadow volume is projected from the silhouette edge of an object and forms a semi-infinite cone pointing away from the light. Finding the silhouette edge of the object can only be easily done with polygonal objects that have not been generated parametrically, and therefore this technique is usually only applied in these cases. By generating a separate shadow volume from each vertex of an area light source, soft shadows can be reasonably well approximated. [Watt & Watt, pg 160-166].
Our discussion up until now has centred on techniques that are designed to render a scene in real time. Here, we change the focus of our attention to models that are more realistic. They are consequently more resource intensive than their real time counterparts.
These are systems that try to approximate full global lighting solutions. They attempt to render scenes using physical models of how light works. They are distinguished by their very long rendering times – usually hours, and some-times days, for complex scenes. The results are generally of far superior quality to the real-time approaches discussed in this paper.
We now examine two of the most significant algorithms used to generate realistic and accurately lit scenes.
A ray is projected from every pixel on the display screen into the picture to see where it collides with objects. At a collision, two light rays are generated - one reflected off the object and one transmitted through the object. Further rays are generated from the point to every light source to see whether that point is visible to the light, with only the visible lights used to calculate colour of the object effectively creating shadows. This process is iterated to a certain depth, and the pixel colour is generated as a combination of the colours of the objects encountered by the rays. This method is used by POV-ray and Radience.
This method treats surfaces as perfectly smooth, and thus generates unnaturally sharp reflections. [Watt & Watt, pg 219-222]
The term radiosity refers to energy leaving a surface per unit area per unit time. The radiosity technique iterates through each patch (surface element), calculating energy received and emitted, effectively tracing the path of a light ray as it bounces around the world [Hall, pg 70-71] until the system reaches equilibrium. Light sources are simply surfaces with an initial non-zero radiosity. This method replaces the ambient factor with an accurate calculation of diffuse lighting. It handles colour bleeding – when a surface is lit indirectly, it tends to take on some of the colour of the objects the light is reflecting off. It renders shadows in a true and accurate manner, and is view independent. However, because it treats light intensity as being constant over the area of the patch, it handles specular highlights badly. It is also massively time intensive, often an order of magnitude greater than ray tracing. It works best in generating man-made environments where the view-independence is a significant advantage – a walk through of a proposed building for example. [Watt & Watt, pg 266-268]
A large quantity of vector mathematics is used in manipulating 3-D objects. Structures such as points, lines, planes and cones are used to represent the data. Information such as intersections and the position of a point to another structure can be determined. Hidden surface removal techniques are used to ensure that polygons are rendered on the correct order with respect to the viewpoint. Many algorithms exist, of which the z-buffer is the most popular and is supported in hardware.
Lighting concerns include local effects – intensity, shading, and highlights – and global effects – shadowing, reflection and refraction. The intensity of light can be divided into emitted light, ambient light, diffuse light and specular light, each of which is handled separately by a lighting engine. Shading can be either flat, Gouraud or Phong. Gouraud is most commonly used because it is more realistic than flat, but not as time intensive as Phong. Phong handles highlights better than Gouraud. Shadows can be generated by creating new polygons that indicate the shadow, by using a hidden surface removal algorithm with the light source as origin to indicate the shadowed areas, or by testing each polygon to se whether it falls within the shadow of another polygon. Reflection and refraction, in addition to the other effects mentioned above, are handled by non real time systems. Ray-tracing tends to produce unnaturally sharp reflections. Radiosity is the only technique to handle colour-bleeding, but does not handle highlights well. Both are extremely time-intensive.
Having introduced the fundamental principles involved in rendering a three-dimensional scene, we now continue on to formulate the design for multiple light source solutions.
In this chapter we discuss our approach to the problem of modelling multiple light sources. By necessity, we introduce this chapter with a discussion of the minimum requirements for such a system. Once we have defined our problem domain, we consider possible solutions. This allows us to fully explain the systems we chose to implement in this project. We chose to implement two solutions, our custom BSP Tree solution and the VLS solution as our control. We continue our discussion by considering common design elements in these systems. We then conclude this chapter by discussing the generic multiple light source class structure used.
We start this section with a discussion of the requirements for a lighting system, taking into consideration local and global lighting aspects.
A complete lighting system would handle all the local and global concerns discussed in the previous chapter, namely:
1. Intensity of light at a point
However, even non-real time systems can rarely cope with all these requirements – radiosity cannot handle highlights, ray-tracing cannot handle diffuse reflections. In the interest of keeping the lighting solution close to real-time, only the most relevant features are implemented. The intensity of the light as it reaches the surface is obviously a pre-requisite for any lighting solution. Shading demonstrates the 3-D nature of the objects, and is therefore also required. Shadowing provides valuable verisimilitude. If we restrict the system to only containing matt, opaque objects, features 3, 5, and 6 can be safely ignored without much loss of image quality. Further, by not considering these issues, the system becomes view-independent.
Now that we have defined our problem domain, we can consider possible solutions.
True multiple lighting situations are rare. In the cases where more than eight lights exist are usually solved by using lightmaps or virtualised light sources. Recently, there have been attempts to modify global lighting solutions to produce real-time results.
Our investigation into this problem revealed five possible solutions. We discuss each of these in some detail before explaining our decisions about which to implement.
The effect of the light is computed by the application and applied to the vertices as colours. An advantage of this scheme is that unlimited lights can be introduced. The main disadvantage is that hardware acceleration cannot be used.
This approach offers quality at the cost of speed.
Lightmaps are texture maps that contain information about the light pattern cast by a particular light. This creates an intensity scale, which is then transformed to match the object/scene and blended with the existing textures. Lightmaps can provide smoother light solutions than Gouraud shading and there is no limit as to how many can be added, although each one does introduce an extra time penalty. They are most effective in fairly static scenes. [Johansson]
This is a per-object calculation of the eight brightest or closest lights. The lights that most affect each object are enabled dynamically as the scene is rendered. There is extra overhead time introduced by this, but the technique takes advantage of any hardware acceleration. The technique is only successful if eight or less lights truly do make a significant contribution to the object being rendered. In actual practice, the number of effective lights in a scene is usually significantly lower than eight. [Kilgard]
Radiosity has no restrictions as to number of lights, and thus a real-time version would provide a complete solution for a multiple light source scene. Radiosity is well known for being time-consuming. By reducing the accuracy of the radiosity calculations, the process can be dramatically sped up. The complexity used would become a trade-off between a solution that is fast and a solution that is satisfyingly ‘real enough’ to the viewer. A modification of environmental mapping techniques has been proposed, but not implemented:
- Place a cube around the object origin, and render the scene onto each surface (ignoring the presence of the object itself).
- Blur the cube view to take into account distortion, fading, and angle.
- Map to UV co-ordinates, as in normal environmental mapping.
- Use this to texture map the cube view onto the object, treating pixel colour values as lighting values, adding lighting values from direct light sources.
- Render the scene.
Vertex ray tracing differs from ray tracing in that instead of tracing rays from every point on an object, the algorithm only trace rays from the vertices. If the number of polygons in the scene is reasonably limited, this can speed up the ray-tracing process to the point that it becomes real time.
We implement two solutions. The first is chosen as a control, and the second is chosen as one that offers the greatest capacity for us to innovatively solve the problem of multiple light sources.
As virtualised light sources is the most common solution, we implement it as a standard for comparison. This system automatically handles intensity, shading and highlights, and simply needs to be modified to include shadowing.
We also chose an application-computing solution to generate high-quality images, as this solution offers the most scope for innovation. The intensity and shading can be handled with an appropriate lighting model. A modified hidden surface removal algorithm is chosen as the most elegant method of shadowing. The z-buffer algorithm and the scan-line algorithm need to be supported in hardware to be effective, and are therefore inappropriate. Painter’s algorithm is finally decided on as being potentially faster than area subdivision algorithms. The application-computing system is therefore based on a BSP Tree structure in order to shadow using this method.
Our decision to ignore lightmaps, real-time radiosity and vertex ray tracing is largely based on the inflexibility of the algorithms. These algorithms offer little in the way of creative development, and we felt it more relevant to the development of computer graphics to focus our attention on a relatively uncharted solution.
We now discuss the design of the common elements within the VLS system and the custom application-computing (BSP) solution.
The graphic primitives, and the techniques of storing them, are common to both of the systems chosen and are briefly detailed here. We have categorized these common elements into three parts - primitives, subdividing polygons and storing polygon meshes. We now discuss these in greater detail.
The primitives used in the systems compose of:
- Geometrical structures: Point3D, Vector3D, Plane, and Cone, explored in detail in section 2-1 above.
- Storage structures: OneVertex, Triangle, Colour, and LightDescription, explored below.
This simply stores the position and the colour of a vertex.
This is an index to the vertices making up a triangle – see section 3.4.3 below.
This stores the r,g,b, and a values that specify a colour, and provides some additional functionality.
This contains some values that are chosen to reflect the nature of spotlights.
One polygon may have very different light patterns across its full area, and thus to render a more accurate scene, the polygons may have to be subdivided into segments.
To create segments:
If (polygon is a triangle)
Add to segment list
// Divide polygon into triangles
For i= 0 to number of vertices –2
New triangle has vertices 0, i+1, i+2
Add triangle to segment list
While (still subdividing triangles)
For all triangles
If area > maximum area
// Subdivide triangle – see figure 3-5
Original vertices = one,two,three
Halfway points between vertices = four, five, six
Triangle 1 has vertices one, four, five
Triangle 2 has vertices two, four, six
Triangle 1 has vertices three, six, five Triangle 1 has vertices four, five, six
Add triangles to segment list
The colour information also has to be carried across, and scaled to reflect the position of the new vertices. By converting all polygons into triangles, splitting polygons becomes simpler, and the definition of points is automatically cyclic, convex and planar.
The aim of any storage structure is to maximise speed of access and minimise space taken.
Many different formats exist for the storage of 3D objects. The simplest, like the POVray RAW triangle format, store the position of every vertex in every triangle [Anger]. However, the vast majority of structures store vertices separately, and then reference them when specifying the triangle. Describing polygons as an index into a vertex list has a number of advantages:
1. Less memory is used.
2. Vertex transformations occur only once.
3. Shared vertices are easier to find.
This is the representation used by OFF, VRML and other 3D formats [Bangay, pg 233].
Most structures include some colour or texture information, support other primitives (polygons, lines), and may also include layer information, moveable parts, and so on. Most of this provided functionality that is superfluous for these lighting systems – the systems are restricted to static scenes. Triangles are indicated for other reasons mentioned above, as well as simplicity of manipulating them in this format. The format that is therefore chosen for the segment list is:
A vector of triangles:
- Three integer values indexing the appropriate vertices
A vector of vertices:
- The position and colour information of that vertex.
This section shows the abstract structure of our system. We outline the major classes and their interaction.
The object model of a multiple lighting solution reflects the physical objects in the scene – the lights and the polygons. Figure 3-6 shows a simplified diagram indicating the various classes used to render a scene – LightSceneVisualRepresentation renders the LightScene. The LightScene is composed of lights and polygons. The polygons have been subdivided into segments that are stored as a vector of vertices and a vector of triangles.
local and global lighting effects are implemented, the resulting solution would
be far from real-time. However, if we only consider matt opaque objects, then
we only need to consider intensity, shading and shadowing to create a
satisfying scene. Many possible solutions exist to render multiple light source
scenes, of which virtualised light sources is the most commonly used. We chose
this solution to implement as a standard of comparison. We also chose an
application-computing solution to generate high quality images. This system is
based on BSP Trees in order to facilitate shadowing. Some design elements are
common to both systems – the primitives used and the methods for subdividing
polygons. An object model for any multiple light source solution shows a light
scene as an aggregate of lights and polygons, subdivided into triangles.
This concludes our discussion of the problem of modelling multiple light sources and our approach to its solution. We have outlined the elements common to the systems we chose to implement, and discussed the structure of the test program, also common to both solutions.
In the following two chapters, we explore in detail each of our selected implementations. We start with the standard solution (VLS) and continue with the custom-application solution (BSP).
In this chapter we discuss the implementation of a Virtualised Light System (VLS). This algorithm is a per-object calculation of the eight brightest or closest lights. We give a more detailed overview of the technique, and discuss standard algorithms used to implement this technique. We explain adaptations we have made to the system to handle variable intensities and we discuss how to add shadows. Following this we give a synopsis of the final system, discussing the various problems we encountered, and conclude with possible extensions to the system.
The steps involved in creating a scene using virtualised light sources are outlined below.
- Enter all polygons and lights.
- Divide the polygons into segments.
- For each segment, calculate the eight closest lights that can illuminate that surface.
At run time:
- For each segment, enable the relevant lights with the corresponding intensity value.
The open-GL supported z-buffer is used for hidden surface removal, the open-GL lighting model for intensity and shading (described in section 4.3.1), and shadow feelers (a ray cast from the point to the light) for determining shadowing.
The eight closest lights are calculated prior to the actual running of the system.
For all segments
For all lights
Find distance from segment to light
Until eight lights found
Find closest remaining light
If not shadowed
Set as next closest light
The triangle class is extended to index the eight closest lights to the segment, substantially increasing the size of this structure.
Because intensity values are variable, it is impossible to determine which are the brightest lights at a point in the pre-calculation. Therefore, we simply determined the eight closest lights.
The method used by OpenGl defines:
Vertex colour = light emitted + global ambient scaled by ambient property + ambient, diffuse and specular contributions from all light sources, attenuated as necessary.
The attenuation factor is a quadratic expression that expresses how rapidly light intensity falls off over distance away from the light. The contribution is further modified by a spotlight effect – the fall off over distance from the centre of the light beam. [Woo et al., pg 211-241]
When calculating the eight closest lights, the light is checked to see if it is visible from that segment.
To determine if the segment is shadowed:
For all other polygons in the scene
Generate the plane of the other polygon
Find the intersection between the plane and the line joining the point to the light
If the point is within the polygon
The polygon is shadowed.
By not considering those lights that are not visible to the polygon, the light will not be activated when rendering segments that are shadowed. This technique is inaccurate in that it treats the segment as a point rather than an area, and therefore a segment that is actually partly shadowed will either be rendered as fully lit or fully shadowed. However, the quality of the resulting image can be improved increasing the number of segments in an object.
When a shadow feeler is cast from the segment to the light, we need to test every other polygon to see whether it lies between the segment and the light source. After we have determined the intersection of the feeler and the plane of the polygon, we need to find out whether this point lies within the polygon.
To determine if the intersection point lies within the polygon:
Find the largest component of the polygon
Project to the plane of the other two components
Place point at origin
For each edge
If edge crosses the positive axis of the second component
// That is, if the two end points lie on either side of the axis
Increment count of intersections
If intersections =1
The point lies within the polygon
The point lies outside the polygon
In general, a point is within a polygon if the number of intersections is odd – by the Jordan curve theorem (see figure 4-1 above) [Bangay, pg 167]. In this system, however, polygons are restricted to being concave, so only one intersection is possible if the point is within the polygon.
Our final system implements the algorithm to calculate the eight closest lights with the adaptation for variable intensities and shadowing. We now discuss some of the more important problems encountered in creating this system, and examine the resulting class structure of this solution.
The VLS system is relatively simple to implement, and translated well from theory. It took some trial and error to convert the LightDescription to the appropriate Open-GL equivalents. The most problematic part is to calculate the fall-off – this is eventually approximated as a scaled ratio of the null radius and the full radius.
A further problem is that at run-time, random segments light up at full intensity or go black for a frame. This was never tracked down, but as the pattern altered every frame, it was assumed the problem did not lie within the application code.
As it stands, the system will already produce specular highlights.
Transparency, without refraction, would simply involve making use of the alpha values already supported in Open-GL. The object would, however, still cast a full shadow. Adding refraction could be done by blending the appropriate colour values of the polygons that are seen through the object with the values of the object itself at set-up time.
Reflection could be handled with a simplified ray-tracing algorithm – a ray is shot out from every segment and tested to see whether it intersects another polygon. The colour values could then be blended at the appropriate ratio. For refraction and reflection, the viewpoint must be known at start-up.
The VLS system extends the segment list to include information about the eight closest lights. Shadow feelers – a ray that determines whether any polygons lie between the point and the light source – are used to exclude lights that do not light that segment. The system is generally easy to implement, although some unexplained hardware errors occurred. It could possibly be extended to include refraction and reflection, at the cost of view independence.
concludes our discussion of the VLS. We move on to our custom-application or
The Binary Space Partition (BSP) System algorithm is the custom solution we chose to implement because of the wealth of possibilities available in its customisation.
We follow much the same approach in this discussion as we followed with the VLS solution. We start by giving an overview of our solution, and discuss the standard algorithms involved. Following that, we discuss adapting our solution to handle variable intensities and consider how to add shadows. Additionally, we discuss some time optimisations. In our discussion of the final system we consider the problems we encountered and outline the class structure. Lastly, we discuss possible extensions.
The steps involved in creating a scene using Binary Space Partition Trees are outlined below.
- Enter all polygons and lights.
- Sort polygons into a BSP Tree.
- Divide polygons into segments.
- For each light, clone the BSP Tree:
o Modify colour values to reflect effect of that light at full intensity.
o Use Painter’s algorithm to indicate shadows, and modify colour values.
At run time:
- Scale light BSP Trees by light intensities.
- Combine all light trees and render.
The lighting model used is described in 5.3.1, a BSP Tree is used for hidden surface removal, and painter’s algorithm used to find shadows.
The most substantial data structure used in the implementation of this system is the Binary Space Partition (BSP) tree. The BSP Tree is a hierarchical structure of recursive subdivisions. A three dimensional space is divided into two by a plane (a two dimensional space by a line, etc.) and the resulting tree is a powerful sorting and classification structure.
BSP Trees provide an elegant method for hidden surface removal. Polygons can be returned in a back to front order to be used in a Painter’s algorithm (see section 2.2.2 above). Polygons can also be returned in a front-to-back order for scan-line algorithms (each pixel may only be written to once). BSP Trees can also be used to compute analytic visibility, accelerate ray tracing, perform collision detection and compute shadows. [Wade et al.]
Constructing a BSP Tree consists of
- Selecting a partitioning plane
- Partitioning the polygons with the plane
- Recursing with both sets.
The stopping point for recursion is application specific, either to a maximum depth, or a certain maximum number of polygons in each leaf node. [Wade et al.]
For this system, the first polygon of the set has been used as the partitioning plane. For the most effective use of the tree, the end point is chosen as when all polygons are within internal nodes.
To construct a tree:
Initialise a node with the first polygon as the partitioning plane
Add first polygon to coincident polygon list
For all remaining polygons
If polygon coincident
Add to coincident polygons list
If polygon completely in front of plane
Add to front list
If polygon completely behind
Add to back list
If polygon spans plane
Add the front piece to the front list
Add the back piece to the back list.
Create front node with front list
Create back node with back list.
This algorithm does a large amount of list handling, and ignores the end case. In practice, it is easier to create a node with only the partitioning polygon, and default front and back nodes to null. Each subsequent polygon is then added to the tree in a separate method.
To split the polygon across the plane:
For all vertices
If point in front of the plane
Add to front piece
Add to back piece
If next point and current on opposite sides of the plane
Find intersection of the line joining current and next with the plane.
Add to front piece
Add to back piece
If the original polygon is specified cyclically, then so will the two resulting two polygons.
Polygons can be returned in a back to front or front to back order from a certain viewpoint. The following algorithm is for returning polygons in a back to front order:
If the viewpoint is in front of the plane
Return polygons from back node
Return polygons from this node
Return polygons from front node
Return polygons from front node
Return polygons from this node
Return polygons from back node
The algorithm for front to back order simply reverses this if statement.
A very simple lighting model is set up to light the system. Lights are assumed to be spotlights, defined by the position, the direction, two angles – the radius within which the light is at full intensity, and the radius without which the light is at null intensity – and the colour.
The lighting equation:
If point outside the null cone
intensity = 0
If point within the null cone, but outside of the full cone
intensity = angle between vertex and null edge / angle between full edge and null edge
If point within full cone
L = vector from vertex to light source
N = normal of the surface
Dist = distance between point and light source
Modifier = intensity *reflection coefficient* n Dot L / (distance/arbitrary constant).
The BSP Tree that has been generated for the scene is cloned for every light in the scene. The effect of this light at full intensity on each segment in the scene is calculated and stored in the tree. Shadows are then added (see below). At run time, this tree is then scaled by the intensity of the light. All light trees are then combined, and the scene is rendered.
The next step in our implementation is the inclusion of shadows. Our implementation is outlined as follows: the polygons are retrieved from the BSP Tree in a back to front order, with the light treated as the viewpoint. Each polygon is then projected from the light to the plane of each polygon behind it. The overlap indicates the shadow cast by the front polygon on the back polygon. The colour values of the segments within this overlap are set to zero, for that particular light BSP Tree.
Various algorithms were attempted for projecting the polygon to the plane of the shadowed polygon. The first attempt makes extensive use of quaternions. The area shadowed is approximated as the area covered by dropping the polygon perpendicular to the target plane:
Move polygon such that target point becomes the origin
For each point in original polygon
Rotate point such that target plane becomes parallel to x-y plane // using quaternions
Project point onto x-y plane (set z values to zero)
Move polygon back
The second attempt produced exactly the same results, but some-what more elegantly:
For each point on original polygon
Generate ray from the point along the polygon normal
Find intersection between ray and target plane
Set corresponding point of target polygon as intersection
This algorithm, however, can be easily modified to eliminate the approximation. By generating the ray from the point to the light source (instead of the ray along the polygon normal) the projection can be made accurately. This is the final algorithm used in the system.
The polygon being shadowed is passed the area that is shadowed. It then iterates through each segment, testing whether that segment falls within the shadowed area (in fact, each vertex is tested separately). Originally, an attempt was made to pass only the overlap between the projected polygon and the shadowed polygon. The major difficulty occurred in trying to generate an overlap polygon that was cyclic in definition. The algorithm as it stood before it was finally abandoned was:
For each edge (a, b) of first polygon
For each edge (c,d) of second polygon
If the two lines intersect and at least one point lies within the other polygon
If one point of each line lies within the other polygon //that is, corners overlap
If a not in overlap vector
Else if b not in overlap vector
If intersection not in overlap vector
If c not in overlap vector
Else if d not in overlap vector
Else if both a and b lie within other polygon
And so on for another hundred lines, but it still did not result in a cyclic definition of points in all cases. However, it was found that since the algorithm to create shadows tests only points that are within that polygon, if the entire shadowing polygon is passed, the same effect is achieved.
Rather than storing and transferring the full BSP Tree for each light, each light BSP Tree is immediately converted to a vector of polygons in a predetermined order. This list takes less time to pass between classes than the full BSP Tree.
When the polygon is scaled by the intensity of the light, it alters the values of the polygon. The polygon must therefore be cloned as many times as there are active lights every frame. This is a very time intensive process, and therefore a modified clone technique is used that provided the required functionality but does not take as long.
The following modifications are used:
1. Vertex positions of polygon were not cloned, but passed as references.
2. In the segment list, the elements in the triangle list are passed as references.
3. In the segment list, in the elements of the vertex list, positions are passed as references.
In effect, only the colour information of each polygon is in fact cloned. This speeds up the process significantly.
Lights whose intensities are very low (taken as less than 5%) will have no significant effect on the scene. By not scaling and adding the light BSP Tree for these lights, the frame rate of a sparsely lit scene is dramatically improved. Obviously, if all lights are in use, no speed up occurs.
When a polygon is passed to a node, we first have to check whether the node equals null and has to be initialised, or if it already exists. Because polygons are passed down frequently in various parts of the construction code, this functionality is removed to a separate method. In the interests of code protection, however, Java does not allow direct manipulation of pointers. A pointer passed as a parameter cannot be changed. A work around involving an integer key and a switch statement had to be used.
Because of floating point inaccuracies, polygons that just touch are sometimes subdivided into the full polygon and a second polygon of zero width. Also, polygons that are projected to another plane and then back again are slightly misplaced. A partial solution to this is to limit the accuracy to significantly below the available accuracy. All double values are rounded off to four decimal places where appropriate, and if the point is very close to the plane, it is considered to be in the plane.
Java passes all values by reference rather than by value. Therefore, in the instances where values have to be changed temporarily, it is necessary to pass a clone of the object. To implement the clone class, a new object has to be created, and then the values of that class have to be entered into the new object. Of course, these values themselves need to be cloned – only simple primitives such as integers and doubles are passed by value. Some care had to be taken for each ‘layer’ to ensure it is cloned correctly. We need to create an object, clone it, change the values of the clone, and then compare the two to ensure the original remains unchanged.
The system is very memory intensive, and cannot handle very complex scenes on an average user computer. The original goal of one hundred lights cannot be met. However, we have successfully rendered a simple scene with up to fifty lights.
To implement the other lighting effects, the view-independent nature of the system must be sacrificed. If the position of the viewer is passed to the segment list in the method initialiseLitSegments, the lighting model could easily be adjusted to handle specular highlights.
Transparency, without any refraction, can be handled by adding alpha values to the Colour class. The shadowing method could be modified to allow for partial shadows – instead of reducing those values to zero, shadowed segments could be scaled by an appropriate factor. To create refraction, the polygons behind the transparent object (from the point of view of the user) would have to be shifted by the appropriate amount and then blended with the object colour. This would have to be done after all the polygons are entered into the scene, but before lighting is considered.
Reflections could be handled in a similar way to shadowing. Along the correct angle (see figure 5-2), polygons could be projected onto the plane of the reflective polygon. Those that overlap can be blended with the existing polygon at a suitable ratio, depending on the reflectiveness of the material.
The BSP System uses a BSP structure to sort the polygons, and then uses Painter’s algorithm to set the appropriate segments as shadowed. Some time optimisations are used to speed up the system, such as a modified clone method and not considering low intensity lights. Some complications were found due to the restrictions and limitations of Java, but could generally be worked around. The system could possibly be extended to include highlights, refraction and reflection, at the cost of view-independence.
This concludes the discussion of the implementation of the two systems we chose to implement, VLS and BSP Systems. We now take a step back from these low level discussions and critically consider the results of our project.
This chapter discusses the results of our work on this project. We contrast our two systems when we investigate the success of the shading and shadowing techniques employed. We then evaluate the time taken to generate render a scene, and give of few examples of scenes rendered by the BSP system.
To test whether different faces of a surface are rendered correctly, both systems were tested with a box. To avoid floating point errors, the box floats slightly above the surface of the plane, rather than sitting directly on top of it.
The box in figure 6-1 is rendered correctly – surfaces perpendicular to the direction of the light (the floor and the top of the box) are brightly lit, while the surface parallel to the direction of the light (the front face of the box) is dimly lit.
In figure 6-2, we can see that the box is rendered correctly, in the same way as the VLS system, as expected.
This is a scene composing simply of one polygon raised above a plane, with different coloured light sources from different directions. It is used to test whether shadowing works correctly.
First, what the scene looks like without shadowing. In figure 6-3 we can see that areas of low intensity lighting not rendered correctly – it is clear which segments have not employed which lights, for example, on the top of the polygon.
In figure 6-4 shadowing is in evidence – the full shadow slanting to the left and the blue shadow slanting to the right. Only a faint pink shadow occurs because few of the segments in that area that are not shadowed employ the blue light anyway. The shadow is irregular, but the quality (at the cost of extra time) could be improved by segmenting the polygons further.
Again, we demonstrate what the scene looks like without shadows. In figure 6-5 we can see lighting is smooth, with low intensity areas lit correctly.
Shadowing occurs correctly in figure 6-6 – the full shadow slanting left, the blue shadow slanting right, and the pink shadow slanting forwards. Edges are smooth and sharp, as desired.
In this section we discuss the time needed to render a scene. Our discussion starts with the time taken to initialise the system, and progresses to discuss the frame rate achieved by our systems
The VLS System takes ten seconds to start-up. The BSP System takes three minutes, six seconds to start up, which is more than eighteen times longer than the VLS System. The VLS system may be altered with reasonable time costs, but changing the scene in the BSP system is prohibitively expensive.
The VLS Systems advances fairly regularly – lights 5 and 14 are the two centre lights that affect proportionally more segments than the other lights, explaining the jumps. It takes approximately 0.12 extra seconds for each extra light. By extrapolation, 100 lights would take 12.5 seconds per frame.
The BSP System starts at a higher frame rate than the VLS system, but is more affected by an increasing number of lights. It takes approximately 0.15 extra seconds for each extra light. 100 lights would take 15 seconds per frame.
Both time dependencies are roughly linear, which is what is expected.
Figure 6-8 demonstrates intersecting polygons. The polygons are lit by ten sidelights, blue from the left and purple from the right.
Figure 6-9 demonstrates a multi-coloured polygon lying at a slant. It is lit by twenty-five white lights from directly above.
Both systems handle the requirements of intensity and shading correctly. Shadowing is handled with limited success by the VLS system, and with more success by the BSP system. The start-up time of the VLS System is significantly less than that of the BSP System, but their frame rates were of comparable orders.
Now that we have completed our discussion on the findings of this project, we draw some conclusions about the project.
To represent a 3-D scene, the objects must be placed in their correct positions, rendered in the correct order to a two-dimensional screen, and lit correctly. There are six major categories of lighting effects – the intensity of a light, shading, highlights, shadowing, refractions and reflections. A full solution would implement all these effects, but if only matt, opaque objects are considered, then highlights, refractions and reflections can be ignored.
The range of solutions to these problems is vast. There is a trade-off not only in quality versus speed, but also in the type of quality – real-time radiosity offers colour bleeding but no specular highlights, light maps potentially offer both, but no reflections or refractions. Some solutions, such as light maps and the BSP solution described here, are static. Virtualised light sources is the most commonly used solution and is implemented as a standard for comparison. A better quality image can be created with an application-computing, and a system using Binary Space Partition Trees to create shadows is implemented. The optimum solution will clearly be application specific.
In comparing the two systems, we are presented with the classic quality versus speed trade-off. The BSP system provides better quality and more accurate shadowing, but the VLS system is marginally faster. Neither system is truly real-time, but a few seconds to render a scene would not be too much of a delay for a user whose only other option is ray-tracing.
Future projects could implement the multiple light solutions not investigated here - lightmaps, real-time radiosity and vertex ray tracing.
The current systems could be expanded to deal with the other lighting concerns – specular highlights, semi-transparent objects and reflections, as described in sections 4.6 and 5.7.
Andrews, Jeff. Light Based Tessellation: Application Note http://www.gamedev.net/reference/articles/article1504.asp.
Anger, Steve. POVray raw triangle format http://astronomy.swin.edu.au/~pbourke/geomformats/pov/povraw.html, 1996.
Bangay, Shaun Computer Graphics Course notes, 2001.
Hall, Roy Illumination and Color in Computer Generated Imagery. New York: Springer-Verlag, 1988.
[Hearn & Baker]
Hearn, D. & Baker, M.P. Computer Graphics. New Jersey: Prentice-Hall, 1986.
Hill, F.S. Computer Graphics. New York: Macmillan Publishing Company, 1990.
Johansson, Tobias Lightmaps http://polygone.flipcode.com/tut_lightmap.htm, 2000.
Kilgard, Mark Virtualized OpenGL Light Sources for Efficient Multi-Light Source Lighting http://www.opengl.org/developers/code/mjktips/VirtualizedLights/VirtualizedLights.html.
Vegdahl, Realtime Radiosity http://www.gamedev.net/reference/programming/features/rtradiosity/
[Wade et al.]
Wade et al., Binary Space Partitioning Trees FAQ http://www.faqs.org/faqs/graphics/bsptree-faq.
[Watt & Watt]
Watt, Alan & Watt, Mark. Advanced Animation and Rendering Techniques: Theory and Practice. New York: ACM Press, 1992.
Watt, Alan. Fundamentals of Three-Dimensional Computer Graphics. London: Addison-Wesley Publishing Company,1989.
[Woo et al.]
Woo, M., Neider, J., Davis, T. & Shreiner,D. OpenGL Programming Guide: the Official Guide to Learning OpenGl,Version 1.2 , 3rd Ed. Indianapolis: Silicon Grahics, Inc., 1999.
Number of Lights
VLS System / [s]
BSP System / [s]