Radiosity project

First deliverable

For the first deliverable, we've completed all the most basic features of the of the program, providing a working foundation on which we can add the more complex features. As it exists now, our program can compute the radiosity in a scene, and display that scene to the user. To simplify things, however, we've implemented this as simply as possible. In particular, our code doesn't yet check the visibility relationship between patches, so it only works correctly in scenes that are convex, without any shadows. Our code also doesn't yet do any automatic subdivision of polygons; it treats each triangle in the input as an indivisible entity.

So, what does our code do? It reads triangles from an input file in a simple plain text format, each with a surface reflectance (color), and perhaps emitted light. For each pair of triangles in the scene, it computes the form factor between them (the fraction of the light from the first that reaches the second), by projecting the triangle onto an imaginary hemisphere around the receiving patch. It then uses these form factors to iteratively distribute light, starting at the patches that emit light and on subsequent iterations bouncing on to other patches. Once it has computed the amount of light bouncing off each patch, it uses these values to render the scene in OpenGL. Each triangle is filled with a solid color based on its radiosity (no Gouraud shading is done yet). Finally, we've included a simple OpenGL interface to rotate the scene, viewing it from different angles.

The snapshots above show the program running on our first test case, which is a box with a rectangular area light on the ceiling, a grey floor, two grey walls, a bright red wall, and a bright blue wall. (Each of these has been uniformly subdivided into triangles). Notice the gradient of light on the different surfaces, darker in the corners, and that the blue and red colors from the walls bleed a bit onto the grey surfaces.

Second deliverable

For the second deliverable, we've completed most of the important features of our program, including visibility (shadow) calculations, multiple light sources, Gouraud shading, and automatic subdivision, and we've modeled some simple everyday objects, and placed them in our previously empty box of a scene.

The images below show a single scene, which includes the same walls, light source, floors and ceiling as last week's scene, but with the addition of a coffee table with a CRT monitor sitting on top. To render this scene correctly, we needed to compute the visibility relationship between patches, to create shadows. Also quite evident in this scene is our addition of Gouraud shading, which computes the illumination at the corners of each patch, and then interpolates the color smoothly between them. This almost completely eliminates the faceted appearance of the walls that was visible last week.

This second image shows our ability to turn light sources on and off individually. Here, we've turned off the main overhead light, but turned on a small red at the bottom of the CRT, which weakly illuminates the whole room (but note that it does not reflect from the blue wall). Last week's images were a little dark, but for this week we've also enhanced our program so we can adjust the gamma of images; this image and the next were rendered at a higher than normal brightness.

An unconventional view of the same scene, this view shows that the coffee table isn't really sitting on the floor, but hovering a few inches off the ground (we don't simulate gravity). Other things to notice include the indirect illumination of the underside of the table, and that the shadow and the edge of the CRT still show their triangular subdivision; though our program automatically divides patches based on their size, it doesn't yet adapt to abrupt changes in illumination by further subdividing; we plan to work on this next week.

Final deliverable

For the final deadline, we've worked on some fairly small enhancements, improved our program's speed and interface, and created more scenes to demonstrate our program's capabilities.

One of the improvements we made this week can logically be viewed as part of the process of merging two changes that were made last week. For the second deliverable, we added a visibility calculation to determine which patches should be in shadow with respect to other patches, and we also added smooth shading by separately computing the radiosity at each vertex of our patches, and using OpenGL to interpolate the color in between. However, as of last week we still only did visibility for the centers of each patch, which is why the shadow of the table in last week's images still looks blocky rather than smooth. For this deliverable, we enhanced our visibility detection code and our visibility cache to compute and remember the visibility between patch corners and patch centers, so that shadows are more smoothly shaded, as illustrated above.

A related enhancement we made to the program's basic algorithms was adaptive subdivision, in which the program checks the radiosity gradient across all the patches in a rendered scene, and decides based on that which patches to divide into smaller patches for increased accuracy. We implemented a fairly simplistic approach to adaptive subdivision, which makes the division determination just based on the absolute difference between radiosities at the patch corners, and restarts the entire calculation from the beginning. The result of this extra subdivision can often be subtle after the program has done its smooth shading, but the image above shows a scene in the middle of the radiosity calculation, after a subdivision but before smooth shading; the different-sized triangles are easy to see, say on the walls.

This image shows the same scene after all the smooth shading has been done. The effects of adaptive subdivision are most visible in the increased definition of the shadow under the table.

This image is similar to the last, but it is rendered using indirect illumination -- the light source is actually hidden behind the square on the ceiling, and the whole scene is illuminated by light that reflects off the ceiling. This is an example of the sort of effect that would be almost impossible to produce with most other rendering methods. (This is also an example of adaptive subdivision; the central area of the ceiling has been extensively divided, which is necessary for the round appearance of the pool of light. It also shows some of the limitations of our adaptive subdivision algorithm; the flat triangles in the corners and under the near right corner of the table represent places where the algorithm missed a shading change that was too subtle for the arbitrary cutoff we picked.)

We've also done some more extensive modeling of objects; unfortunately, more complicated scenes take much longer to render, so we can't use such small patches. This scene shows a variety of light sources; indirect lighting from the ceiling, a small light shining from behind and to the left, and the small LED on the CRT. (New this week, we also have an interface that allows us to adjust the brightness of lights, as well as turning them on and off).

Can I play with this myself?

Yes, you can. Solaris x86 (torus.cs) binaries and our data files are in this directory; you might also be interested in the README.