Raytracer and Rasteriser

Projects

I wrote a raytracer and a rasteriser as part of my university course. The raytracer supported features such as indirect lighting, reflection, refraction, and a photon mapper capable of simulating the final positions of 60,000,000 photons in a few minutes (and quite a few GBs of RAM).

Raytracer

Features

Direct Illumination, Shadows, and Reflection

Once anti-aliasing is implemented, the triangle normal need only be randomised for each sample, in order to acquire roughness. For each sample, the rotation of the triangle normal - for the triangle which the ray intersects with - is given a random offset. The magnitude of this random offset is dictated by the roughness attribute of the triangle. This gives a rough, blurred, look to rough mirrors. It also makes specularity look more realistic.

Reflections can be implemented simply by firing another ray from the intersection position in the right direction, and using the colour it returns as the pixel colour. This colour is then blended with the diffuse color depending on material properties. You get rough mirrors for free by having roughness.

Direct illumination with shadows.
Direct illumination with shadows.
Rough reflection and soft shadows.
Rough reflection and soft shadows.

Refraction

Refraction is achieved by firing rays through the material, accounting for the difference in refractive index. The amount of light which is refracted vs diffused depends on the transparency of the material.

If a ray hits a transparent triangle on the opposite side to the normal, then the refractive index is 1/R, where R is the index of the material. Otherwise it’s just R.

Fresnel’s Law determines how much of a ray is reflected when it hits a change in refractive index. Above a certain angle - the critical angle - the rays totally reflect, causing Total Internal Reflection.

Beer’s Law describes a property of semi-transparent materials where colour is absorbed from light per length it passes through the material. The application of Beer’s law gives a jelly-like effect.

refractedColor = incomingColor * exp(-absorbedColor * lengthInMaterial)
Pure refraction with Snell's law, Fresnel's law, and TIR.
Pure refraction with Snell's law, Fresnel's law, and TIR.
Refraction with beers law - colour of material seeps in depending on length of ray.
Refraction with beers law - colour of material seeps in depending on length of ray.

Photon Mapper

Millions of photons are simulated after being emitted from the light, and then their final positions are recorded. The record of all the final positions is called a photon map. I used an OctTree (like a quad tree, but 2x2x2 rather than 2x2) to enable efficient querying of photon positions.

Hitting any surface will result in a certain percentage of the photon’s color being reflected, which results in indirect lighting. Photons will keep bouncing until their color is low enough to be negligible in which case they’ll stop. The percentage of color which isn’t reflected will be stored in the photon map as a final photon position (ie: diffuse) or sent split into colors and refracted if the triangle is transparent.

Warning: the default settings for the photon map require 4GB of free RAM. Reduce the photon count in drawRaytracer() if you don’t have that available.

Note the light from the sphere on the walls, and the double shadow from indirect lighting on the small cube.
Note the light from the sphere on the walls, and the double shadow from indirect lighting on the small cube.
A re-production of Newton’s light experiment.
A re-production of Newton’s light experiment.

Rasteriser

Features

Rasteriser with Shadows
Rasteriser with Shadows