homepage   gallery    guestbook  

                                                           The Cartman Renderer

                                                                                                                           by   Celu Ramasamy

 

 10/10/2007

        This is a Global Illumination ray tracer that I developed in C++. I have named it after my favorite south park character, Eric Cartman. I coded the entire project from scratch. Execpt for an image writing library, the ray tracer does not depend on any other third party libraries. This is the second ray tracer that I have implemented, here's the first one. The experience gained from implementing the first one has really helped me to design this ray tracer so that it's well structured and maintainable for long term use. I have been working on this project for about five months now. Its still very much under development, I try to keep adding new features every week. Heres some of the cool stuff I rendered using my renderer,

 

Updated on 2/1/2008

 

-> Rendered some animation clips for my Random Animation Project,

   

 

Heres the link to my Random Animation project page  www.fx.clemson.edu/~rramasa/RandomAnimaton.htm

 

-> Implemented a matrix based transformation stack, that automatically calculates the inverse for the current state of the transformation when requested. This avoids the costly operation of direct inverse calculation for matrices.

->I have also started work on implementing a tangent space normal map generator. Its about half way done. I was trying to learn how the tangent space normal map was implemented in zbrush and maya but I find no info on their implementation details. So I figured out my own implementation for generating tangent space Normal maps by using the UV map info on the high and low poly models. I map the tangent space normal direction on a per polygon local space co-ordinate system not on to the tangent-binormal co-ordinate system and also I store the actual normal direction and not the normal offset vector in the RGB channels. Hence the color mapping in the normal map is different. Currently I'm testing and debugging the implementation. Will have more updates soon, heres what I have so far.

Screen Shots of UV maps from Maya,

Low Poly Model UV

    

 

High Poly Model UV (Close up)

 

 

Heres the tangent space normal map generated in my renderer ( This normal map was generated for an object that had almost identical low poly and high poly models),

 

Heres a sample dent on the high poly model,

 

Heres how the dent shows up on the normal map,

 

Updated on 1/20/2008

New Features:

-> Implemented Poission Distribution for generating Random Numbers.

->Added Voronoi Cell Textures

->Implemented Gamma Correction and Tonal Remapping.

 

This is the same image as above with the intensity of the whites remapped to a higher value.

 

Updated on 1/1/2008

New features:

-> Designed and Implemented a custom file format (.celu) for importing maya scenes into my renderer. - Click Here For More Details

-> Implemented Normal and Texture co-ordinate interpolation across the polygonal mesh faces using bayer centric coordinates.

->Tweaked the photon map building process, the photon map building stage is now upto 4 times faster.

-> Made the NURBS animation more robust by implementing an 'Object factory' for recycling the KD Tree nodes. That resolved some of the stability issues and theres also some speed improvement during NURBS animation.

-> Fixed a lot of bugs that showed up when rendering scenes imported from maya. Still working on some, hope to get them all cleaned out pretty soon. Meanwhile, heres some pretty images that I managed to render after the integration with maya.. 

                                                                

 

 

 

 

The above image to the left is rendered using only final gathering without using global photon map, each pixel is super sampled at 4 samples per pixel. To the right is the same scene rendered using only a local illumination model.

 

The above three images are again rendered using only final gathering each with varying final gather intensity. The images are sampled at one sample per pixel.

                                                                                       

The above image is rendered using both final gathering and global photon map. Primary diffuse bounce is calculated by final gathering and secondary diffuse bounce is approximated using global photon map. Intensity contribution due to secondary diffuse bounce turned out to be more than what I would like it to be, resulting in the blocky white patches. May be increasing the photon count would also have helped.

 

                                                                                

 

The above is a series of images showing the direct rendering of the global photon map for the scene. This scene was rendered by tracing 100,000 photons from the light sources.Below is a single frame from the above clip.

 

                                                    

 

Below is a clip showing my ambient occlusion implementation in action. The entire clip was super sampled at 4 samples per pixel.                                                    

                                                                                

 

Here is a single frame from the above clip.

                                                    

 

All the models used in the above images were downloaded from www.3drender.com . Its a site hosted by Jeremy Birn, lighting TD at Pixar, its a great resource for people looking to get into 3D lighting and rendering. I learnt a lot from his book while implementing various features for my renderer.

 

                                                    

 

The above image shows my subsurface scattering implementation in action. Below is a series of images where the above image is broken down component wise.

                                                   

 The above image shows only the direct diffuse component.

 

                                              

 The above image shows the diffuse approximation component of BSSRDF.

 

                                              

The above image shows only the single scattering component of BSSRDF.

 

I noticed that my BSSRDF implementation works fine on simpler objects, like on the sphere below. But when  applied to complex models like the above model, its still not as smooth as I would like it to be, Im looking to work on testing and fixing the glitches. But this is what I have got as of now.

 

The above demon model was given to me by Zachariah Inks. Thanx Zak!

 

                                                               

  

Updated on 10/10/2007

       

                       Procedurally generated skyscape using fractals

 

 

This clip was rendered in passes in my renderer and composited in After Effects. More info on how this clip was rendered is available further down the page. If you are not able to view the clip, click here.

 

 

This is a rendering of a flowing water surface illuminated by evening sun. More info on how this scene was setup can be found further down this page.

 

Now before I continue, I would like to make a mention of the complete list of features that I have implemented.

Feature List:

• Mont Carlo based distributed Ray Tracing (Glossy Reflection and Transmission, Depth of Field and Super Sampling).

• Global Illumination using Photon Mapping,

i) Caustics                                                                                                                                                     
ii) Indirect Diffuse Illumination :- Final Gathering is used to model the primary diffuse inter reflection in a scene and it integrates with a diffuse Photon Map to simulate the secondary diffuse inter reflection.

• Implementation of irradiance caching to accelerate the indirect diffuse illumination calculations.

• Photon Emitting Light Sources like Point Lights, Area Lights and Spot Lights generate a projection map during the photon tracing stage to identify Objects of interest in the scene, there by making the photon tracing process more efficient.

• A Procedural Shading system that seamlessly integrates procedural shaders into the ray tracing engine. The procedural shaders are treated in the same way as any other image texture.

• A Ray Marcher Implementation that simulates both the primary and the secondary scattering by Participating Media. A Volume Photon Map is integrated into the Ray Marcher to model the Secondary Scattering in the Participating Media.

• Support for NURBS and trimmed NURBS surfaces.

• NURBS surfaces support true displacement mapping.

• Bump mapping is supported by all the primitives.

• An Animation System that allows you to manipulate almost any aspect of the ray tracer. It even allows multiple animated attributes to depend on each other for results.

• BSSRDF implementation for modeling subsurface scattering.

• Support for matte objects and alpha transparency to help with compositing.

• Automatic bilinear filtering of all image textures.

• Support for Specular and Transparency Map.

• Support for Matrix based transformations

• Ambient Occlusion

• Implementation of fresnel term for reflection and refraction.

• Implementation of Beer-Lambert’s law for simulating light traveling through participating media like glass and water.

• Texture mapping has support for both 2D and 3D texturing.

• Built in texture anti-aliasing where the pixel area is projected on to the object surface to estimate the sampling area on the texture, resulting in smoother texture look ups.

• K-D tree based scene rendering acceleration.

• A library of Procedural functions including 1D,2D and 3D noise functions, and a number of fractal functions derived from these noise functions using linear, cosine and cubic interpolation.

 

Sample Scenes

 

SkyScape

The skyscape is actually a procedural texture applied on to a vertical plane. I wanted to put together a quick shader that did a good job of mimicing the sky when applied to a background plane in a scene. I started with an fBm function and a gradient for the background that was a good approximation of the atmospheric scattering that results in a whitish hue near the horizon.

 

 

As a next step I transformed the U,V mapping of the shader to mimic perspective, to give an impression of the over head sky.

 

 

 

Here's the final texture in the transformed UV space.

 

 

God Rays

 


 

This is a clip of god rays shining through a stained glass window. I rendered this clip in passes. For this particular scene I tweaked the ray marcher so that it picks up the color from the stained glass while rendering the volumetric pass. I rendered the volumetric pass with only the primary scattering turned on. I rendered the entire clip on my laptop, so turning on the secondary scattering would have made it virtually impossible to put together this clip within a reasonable time. I added some glow during compositing to compensate for the secondary scattering, but the gain in rendering speed was worth the trade off.

Volumetric pass with only primary scattering, the color concentration on the volumetric fog was more than what I wanted. But instead of rerendering the entire volumetric pass, I corrected the color concentration during the compositing stage.

 

Volumetric pass desaturated a bit and glow added

 

In the background plate, the walls of the room was generated using a fBm based 3D procedural texture to give a 'paint peeling off wall' look. The floor is an image texture with a bump map applied on it. I turned on ambient occlusion to darken the edges of the floor and the wall.

 

I made the stained glass as a matte object for the background pass so that during compositing I could control it's intensity independent of the wall and floor intensity.

 

Heres the image used as the mosaic glass texture.

 

 

Here's a screen shot of the lighting pass. View the full clip at the top of the page.

 

Water Surface

 

 

The water surface is actually a transparent NURBS surface displaced with perlin noise. The fresnel term plays an important role in getting the right reflectivity on the surface and the beer-lambert law does a nice job of giving volumetric color to the water.

 

Some Interesting Algorithms

This ray tracer is my personal project. I implemented all the aspects of this ray tracer by reading through research papers and some great text books. In the process I encountered numerous technical issues. Most of these issues often occurred so deep within the implementation that it meant that I had to figure out a solution on my own.

I would like to make a mention some of these interesting solutions, in the hope that someone would find it useful. I'm not claiming these to be the most elegant solutions for the mentioned problems. But this is how I implemented them and they are working well for me. :)

 

Calculating the Jitter direction for glossy reflection and refraction

While implementing glossy reflection and refraction, I had to come up with a way to produce jittered rays around any arbitrary direction, that represented the reflected or the refracted direction.

To generate a random jitter vector with respect to any arbitrary direction in space, I started by generating a random jitter vector around the Y axis of the world space co-ordinate system. Next Calculate the polar co-ordinate values Theta (angle w.r.t X axis)  and Phi (angle w.r.t Y axis) that the arbitrary direction makes with the world space co-ordinate system. Using these two values I setup two rotation matrices that would transform any vector from the world space co-ordinate system to the local space of the arbitrary direction vector. So finally I pass the jitter vectors through the transformation matrices to get the corresponding local space jitter vectors for the arbitrary direction.

 

Ray K-d tree Intersection

While working on the ray K-d tree intersection routine, I implemented a simple test that would make the ray K-d tree intersection more efficient.

Initially in my implementation when a ray intersects with a non leaf K-d tree node, the two child nodes of the K-d tree were processed in any arbitrary order without any preference for whether the front child or the back child was processed first. This meant that each time both the nodes must be processed before the final intersection point is identified.

Later I modified the implementation such that when a ray intersects a K-d tree node, the order in which the child nodes were processed was determined by a dot product test between the nodes dividing plane normal and the ray. This meant that if a intersection was found in the first child node, then the second child node can be effectively ignored without any processing.

 

 

//    Algorithm

if( DotProduct(Ray,Normal) < 0.0)

{

    //     Process the front child first as the ray is coming from the front

}

else

{

               //    Process the back child first as the ray is coming from the back side

}

//    Note: If the ray is perpendicular to the normal then the order in which the nodes are processed becomes irrelevant as one of the child nodes will automatically get culled. A ray perpendicular to the dividing plane normal will always intersect only one of the two child nodes.

 

Sample Points generation during BSSRDF Calculation

In order to calculate the diffuse approximation component of the BSSRDF. I needed a way to generate sample points around the point of intersection of the incident light ray on the object surface. I was looking for a way to consistently generate these sample points irrespective of the surface topology  around the point of intersection.

I calculated a projection point at some distance moving outwards along the surface normal. Then generated a projection plane perpendicular to the surface normal. Next, generate random sample points on the projection plane within the required radius. Then trace out rays originating from the projection point passing through the random sample points on the projection plane. When these rays intersect with the object surface, the point of intersections gives the required sample points on the object surface.

Note: By keeping the projection plane as close to the object surface as possible and by moving the projection point as further back from the object surface as possible, we can ensure that there is minimum distortion between the radius of the sample points on the projection plane and the radius of the actual sample points on the object surface. 

 

 

 

To Do List

My work on this project followed a cyclic pattern where I would spend time on learning a particular feature, then implement it and move on to the next feature. So, I really didn't have much time to set up elaborate scenes. For some of them I had to move on once I got a few test renders done.

Here's some of the things I would like to comeback and spend more time on,

Photon Mapping : I would like to speed up my photon map build process. Right now, that part of the photon mapping process is really eating up my rendering time, particularly for diffuse photon maps.

Secondary Scattering & Anisotropic Scattering: I need to spend more time on fine tuning the secondary scattering as well as the anisotropic scattering aspect of my ray marcher. Turning on the secondary scattering makes my ray marcher really slow.

 

More Details on the Features & Images

 

NURBS and Trimmed NURBS:

As it has turned, I have been putting these features to great use in my random animation project. Take a look at clips below. These are some quick demo clips that I rendered to show my Professors. I apologize for the looping in the videos, these clips were put together in a single day. So not a lot of time for my laptop to render them. This should be a nice tech demo of my NURBS implementation as well. Hope you like the music! :)

 

Please be patient...Sometimes the visual starts only like 10 seconds into the clip. Just building up the tempo! :)

 

This clip contains a NURBS surface with a high frequency fractal function applied to it as a surface texture.

 

 

The two clips below have the same visuals but with music of different tempo. The clips contains a trimmed NURBS with some in between transitions showing the silhouette of the entire NURBS patch.

 

 

 

 

And here's a clip of a randomly deforming NURBS with its surface displaced by a fBm function. No music this time though!

 

 

 

Anti-Aliasing:

I have implemented two modes of anti aliasing in my renderer.

-> The first implementation is a part of the core ray tracing engine. In this implementation each pixel is super sampled using multiple rays and each ray is jittered randomly within the sub pixel. This implementation anti aliases both the geometry and the textures, but its the slower of the two implementation.

-> The second implementation is incorporated into the texture mapping engine, so it anti aliases only texture maps not geometry. This implementation works by projecting the area of a screen pixel on to the object surface and then by super sampling within that projected polygon on the object surface. In this implementation the projected polygon is super sampled on a regular grid as opposed to a jittered grid as with the previous implementation. This gives me an opportunity to compare regular and jittered grid super sampling mechanisms.

The first clip below shows an nasty un anti-aliased version of the grid pattern.

 

 

This clip below shows the same pattern anti aliased using 5 x 5 rays per pixel on a jittered grid. This clip uses the anti aliasing implementation that is part of the main ray tracing engine.

 

 

This clip below shows the same pattern anti aliased using 8 x 8 rays per pixel on a regular grid. This clip uses the second anti aliasing implementation that is part of my texture mapping engine.

 

Jittered grid seems to produce much improved results when compared to regular grid anti aliasing. When I tried to anti alias the above pattern using 5  x 5 samples per pixel on the regular grid super sampling implementation. The results were no where as pleasing as the jittered grid super sampling implementation using the same number of samples.

 

Caustics:

Caustics is one of the first GI features I implemented. Heres some of my early test renders.

                                                     

 

On the way to implementing the caustics, I had to do some really challenging debugging. The following series of images will give an idea about the problems I had to go through before I finally got my implementation right.

   

and finally....the first caustics!

 

 

Procedural Functions:

After I implemented the support for procedural shaders, I have steadily accumulated a collection of procedural shaders and functions.

 

This clip is a rendering of a 3D noise function.

 

The following images show a surface displaced with different fractal functions

    

 

Procedural Shaders can be linked to each other to form infinite shapes and patterns. For example, heres a surface displaced with only a fBm function,

 

Heres the same function with a sin function attached to the fBm function,

 

 

Glossy Reflection & Refraction:

            

 

This surface has glossy reflectance with a specular map applied on it.

 

 

Volumetric Rendering:

 

 

This clip shows off some volumetric shadows of a sphere illuminated by a spot light.

 

 

Indirect Diffuse Illumination:

My implementation of indirect diffuse illumination consists of two components,

-> the Single bounce component is modeled using Final Gathering.

->the multiple bounce component is approximated using a diffuse photon map.

 

Heres a simple test render showing the color bleeding from the walls

 

In the image below, a spot light is focused on to the top right corner of the room, the rest of the room is illuminated by the indirect light emanating from there. I scaled up the intensity of the light to make the indirect illumination more prominant, in the process the directly lit area looks blown up. Notice the soft shadow cast by the sphere due to the indirect illumination.

 

 

The next image below shows a direct visualization of the diffuse photon map with the final gathering turned off. Notice that the illumination looks quite blocky. The final gathering stage helps to smooth out the result from the diffuse photon map and also adds one more level of diffuse light bounce to the image.

  

 

Irradiance Caching:

I implemented irradiance caching to help speed up the insanely costly indirect diffuse illumination calculations. Instead of calculating the indirect diffuse illumination for every pixel, irradiance caching uses the geometry information from the scene to calculate samples that are spread out from each other. In the process reducing a bulk of the calculations.

 

This image shows a simple scene with irradiance caching turned on. The red points indicate the points where new indirect diffuse illumination values were calculated. By setting the threshold to a high value, new samples are generated very close to each other. Whereas by setting the threshold value to a lower value, new samples get generated further apart. But there is a noticeable degradation in the quality of the interpolation.

 

 

Heres a couple of images that went wrong during the implementation of diffuse illumination, but still looked good nonetheless.

 

 

 

Ambient Occlusion:

 

Heres a simple scene without ambient occlusion ,

 

Heres the same scene with Ambient Occlusion turned on,

 

 

Matte Objects and Alpha Channel:

The images rendered out from my renderer has full alpha channel support. Objects in the scene could be marked as place holder matte objects so that they dont show up in the final render. Matte objects burn a hole in the alpha channel so that later some other object can be composited into it's place.

 

Heres a test image with the sphere in the center marked as a matte object,

 

 

Heres the same image with the surrounding wall marked as a matte object (everything expect the sphere in the center is marked as matte),

 

 

 

Bump Mapping:

The following image shows a plane with a fractal function applied as bump map.

 

 

Depth of field:

Traditionally Computer Graphics images assume a pin hole camera model. But real world camera systems have a finite aperture and hence a finite Depth Of Field. This Depth of field is simulated in ray tracing by assuming a vertical plane that approximates a lens and another plane inside the scene that represents the focal plane. By distributing rays across the surface of the lens that pass through a single point on the focal plane, the objects close to the focal plane appear in focus and the objects that are further apart appear out of focus.

 

 

The above two renders show the depth of field in action, the first render has one of the spheres in focus. While the second render has all the three spheres out of focus.

 

 

Here are some of the books and technical publications that I used as reference for my implementation,

 

> "A Practical Model for Subsurface Light Transport" by Henrik Wann Jensen, Stephen R. Marschner, Marc Levoy and Pat Hanrahan

>"A Ray Tracing Solution to Diffuse Interreflection" by Greg Ward, Francis Rubinstein and Robert Clear

> "An Introduction To Raytracing" by Andrew.S.Glassneer.Morgan Kaufmann Publishers:

  http://www.amazon.com/gp/product/0122861604/102-5209661-6034558?v=glance&n=283155

>"Realistic Image Synthesis Using Photon Mapping" by Henrik Wann Jensen

http://www.amazon.com/exec/obidos/ASIN/1568811470/o/qid=992896893/sr=2-1/ref=aps_sr_b_1_1/107-1767647-8361347

>"Texturing and Modeling: A Procedural Approach" by David S. Ebert, F. Kenton Musgrave, Darwyn Peachey, Ken Perlin, Steven Worley

http://www.amazon.com/Texturing-Modeling-Procedural-David-Ebert/dp/0122287304

 

 

                   © 2004-7 Celambarasan Ramasamy. All rights reserved.