Ok, I’ve got most of what I want done on the Ray Tracing project — enough at least to publish some code and do some blogging. This first article describes the overall coding structure, and how to use the project. I’ll get into some of the more difficult aspects of ray tracingÂ in future posts.
Before going any further, here’s the source code.
Running the Application
After extracting the zip file, you’ll find two directories: Executable and Source Code. Pre-compiled binaries are in the Executable directory, and the source code is in the, errr… you get the picture.
Again, this project was built against XNA, so to run the executable you’ll need to install the following software:
To compile the project, you’ll need:
You can use the keyboard and/or an Xbox 360 Game Pad to control the application. The controls are as follows:
|W, A, S, D||Keyboard||Move the camera Forward, Left, Back, and Right respectively.|
|Print Screen||Keyboard||Take a screen shot.|
|R||Keyboard||Reload the scene from an Xml file (if the scene was initially loaded with an Xml file). Note: this version of the project auto loads changes to the Xml file. As such, you don’t really need ‘R’, but it can be used as a ‘Reset’ feature.|
|Left Thumbstick||Game Pad||Move the position of the camera along the X and Y axes (target stays fixed).|
|Triggers||Game Pad||Move the position of the camera along the Z axis.|
|Right Thumbstick||Game Pad||Move the camera’s target along the X and Y axes.|
|X Button||Game Pad||Toggle the ray tracing coloring methods. Theses methods are WithoutLighting, WithLighting, WithHardShadows, WithReflection, and WithRefraction.|
The are three assemblies that make up the application. These are:
|Bespoke.Games.Framework||This is a set of basic classes I use when working in XNA.|
|Bespoke.Games.Framework.RayTracing||Class libraries specific to the ray tracing project. This is where the bulk of code resides for this project.|
|BasicRayTracing||The executable and host of the Game-derived class. There’s very little actual code here — just initialization and the main update and rendering loops.|
|RayCamera||The camera within a ray tracing scene. The Initialize() method generates and caches a set of direction vectors for casting rays into the scene.|
|Maintains the list of shapes and actors within the scene and implements the Ray Tracing algorithm within its Draw method.|
|SceneLoading\*.*||All of these classes support the Xml scene loading. The entry point to Xml scene loading is the class RaySceneLoader.|
IShape / Sphere / Plane / Triangle / ShapeBase
|Procedurally created “simple” shapes.|
|“Complex” shapes made from 3D models. Actors use the XNA content pipeline to extract the index and vertex buffers to produce a set of Triangle objects.|
So now to the code itself. Of the above significant classes, the RaySceneManager is where the ray tracing is actually happening — starting with the Draw() method. Essentially, with each pass through the Draw() method we build up a Texture2D objectÂ by casting a set of rays into the scene, collectingÂ aÂ color atÂ the closestÂ object intersection (or the backgroundÂ color if there is no intersection). We then draw the resulting texture to the screen. Here’s that code (with some timing code removed):
TheÂ ray direction vectors are precalculated in the RayCamera.Initialize() method. Most of the work here is handed off toÂ the TraceRay() method, and it’s this methodÂ that returns a color to be stored in the texture to be rendered. The TraceRay() method casts a ray into the scene and determines a resulting color using a variety of methods. I’ve used a delegate to allow for different color retrieval methods within the same coding architecture (for effects such as shadows and refraction).Â Here’s the general TraceRay() algorithm:
- Find the closest “simple” shape object that the ray intersects.
- Find any closer shapes contained within “complex” objects (Actor instances) that the ray intersects.
- With the closest intersected shape in hand, get a color at that intersection point and return it to the calling method.
This third step looks something like:
Where sCurrentColorMethodHandler is a delegate pointing to the currently selected color method.
I’ve been typing on this for awhile now, so I thinkÂ I’ll call it quits forÂ the momentÂ and let you experiment with the code. I’ve included aÂ set of scenes you can get started with, and you’re welcome to use this code in any way that you see fit.
Please note,Â there are a number of known deficiencies with this implementation. These include: no refraction for solid objects (e.g. spheres), slow uniform grid initialization forÂ the uniform spatial subdivision algorithm, ray initialization could be improved, and in-code documentation. And I’m certain there are dozens upon dozens of other problems that I don’t know about. Please go easy on me if you find these. This project has been about learning ray tracing, and I’m very happy with what I’ve produced thus far. But as with everything, this is a work-in-progress and is far from a ‘finished’ state.
Here’s a screenshot from a fairly complex scene (17,000 shapes) using uniform spatial subdivision:
If you have any questions please let me know. Here’s a link to the code again.