In this assignment you will modify your ray tracer to use the grid from the previous assignment for fast ray casting. You will use your ray marching code and intersect all the objects stored in each traversed cell. You must pay attention to intersections outside the cell and implement early rejection to stop marching when you have found an appropriate intersection. To measure the performance improvement, you will analyze various statistics about your raytracer.
Finally, you will add new Material effects where the color of the material varies spatially using procedural solid texturing. This will allow you to render checkerboard planes and truly satisfy the historical rules of ray-tracing. Solid textures are a simple way to obtain material variation over an object. Different material properties (color, specular coefficients, etc.) vary as a function of spatial location.
Before beginning computation, call:
RayTracingStats::Initialize(int _width, int _height, BoundingBox *_bbox, int nx, int ny, int nz);
For each non-shadow ray cast (a call to RayTracer::TraceRay()), call:
RayTracingStats::IncrementNumNonShadowRays();
For each shadow ray cast, call:
RayTracingStats::IncrementNumShadowRays();
For each ray/primitive intersection operation (each call to intersect for non-group and non-transform objects), call:
RayTracingStats::IncrementNumIntersections();
For each cell traversed (a call to MarchingInfo::nextCell()), call:
RayTracingStats::IncrementNumGridCellsTraversed();
From these numbers we can compute the average number of rays per pixel, primitive intersection calls per ray, grid cells per ray, rays per second, etc. Add support for an additional command line argument, -stats. If this option is specified, at the end of your rendering loop, print the various statistics by calling:
RayTracingStats::PrintStatistics();
Verify that the statistics are reasonable for simple test scenes without the grid, with and without shadows. Test this part of the assignment with examples from past assignments. Note that the number of rays cast and objects intersected can be predicted from the image resolution and number of objects in the scene.
Make sure that you accelerate all your ray intersection routines, in particular do not forget shadow rays. Pay attention to objects that overlap multiple cells --- don't incorrectly return intersections outside of the current cell. Implementation of the marking strategy mentioned in lecture to avoid duplicate ray-primitive intersections for objects that overlap multiple cells is optional (extra credit).
The CheckerBoard class will store pointers to two Materials, and a pointer to a Matrix which describes how the world space coordinates are mapped to the 3D solid texture space. Your solid texture implementation will describe a unit-length axis-aligned checkerboard between the two materials and the matrix will control the size and orientation of checkerboard cells. The prototype for the constructor is:
CheckerBoard(Matrix *m, Material *mat1, Material *mat2);
Handle this new material type in the interactive viewer by implementing CheckerBoard::glSetMaterial(). Because OpenGL does not implement general procedural texturing, you will simply call the corresponding glSetMaterial() method of the first material.
Implement the CheckerBoard::shade routine for ray tracing. You simply need to delegate the work to the shade function of the appropriate Material of the checkerboard. Using a float-to-integer conversion function floor and the function odd, devise a boolean function that corresponds to a 3D checkerboard. As a hint, start with the 1D case, just alternated segments of unit length, then generalize to 2 and 3 dimensions. Remember to multiply the intersection point by the matrix to properly transform the materials.
Noise appears random, but isn't really. If it were really random, then you'd get a different result every time you call it. Instead, it's "pseudo-random" - it gives the appearance of randomness. Its appearance is similar to what you'd get if you took a big block of random values and blurred it (ie: convolved with a gaussian kernel). Although that would be quite expensive to compute.
So we'll use his more efficient (and recently improved) implemenation of noise (http://mrl.nyu.edu/~perlin/noise/) translated to C++:
First derive the class Noise from Material which computes the function:
N(x,y,z) = noise(x,y,z) + noise(2*x,2*y,2*z)/2 + noise(4*x,4*y,4*z)/4 + ...
for the specified number of octaves. With just the first octave, the Noise function is very smooth and blobby. Including additional octaves of higher frequencies adds finer details to the texture. The value of N(x,y,z) will be a floating point number that you should clamp and use to interpolate between the two contained Materials. Here's the constructor for Noise:
Noise(Matrix *m, Material *mat1, Material *mat2, int octaves);
UPDATE (11/2): You'll need to interpolate the values returned by your Material::Shade computation. In addition, to correctly handle the ambient term and the reflective and transmissive components of materials, you'll need to modify the prototype of some Material accessor functions to also take the world space coordinate as an argument.
M(x,y,z) = sin (frequency * x + amplitude * N(x,y,z))
Try different parameter settings to understand the variety of appearances you can get. Remember that this is not a physical simulation of marble, but a procedural texture attempting to imitate our observations. The constructor for Marble is:
Marble(Matrix *m, Material *mat1, Material *mat2, int octaves, float frequency, float amplitude);
Wood(Matrix *m, Material *mat1, Material *mat2, int octaves, float frequency, float amplitude);
Procedural materials can be used for fun recursive material definitions. You can have a checkerboard where cells have different refraction and reflection characteristics, or a nested checkerboard containing different wood materials. The notion of recursive shaders is central to production rendering.
Analyze your raytracing statistics for each example and make sure they make sense. In some cases, using the grid may slow you down. Also, the total number of ray-primitive intersection operations may actually increase when the grid is added (unless you've implemented marking to prevent duplicate intersections between the ray and the same primitive).
raytracer -input scene6_01_sphere.txt -output output6_01a.tga -size 200 200 -stats raytracer -input scene6_01_sphere.txt -output output6_01b.tga -size 200 200 -grid 10 10 10 -stats raytracer -input scene6_01_sphere.txt -output output6_01c.tga -size 200 200 -grid 10 10 10 -visualize_grid
raytracer -input scene6_02_sphere_triangles.txt -output output6_02a.tga -size 200 200 -stats raytracer -input scene6_02_sphere_triangles.txt -output output6_02b.tga -size 200 200 -grid 10 10 10 -stats raytracer -input scene6_02_sphere_triangles.txt -output output6_02c.tga -size 200 200 -stats -shadows raytracer -input scene6_02_sphere_triangles.txt -output output6_02d.tga -size 200 200 -grid 10 10 10 -stats -shadows raytracer -input scene6_02_sphere_triangles.txt -output output6_02e.tga -size 200 200 -grid 10 10 10 -visualize_grid
raytracer -input scene6_03_sphere_plane.txt -output output6_03a.tga -size 200 200 -stats raytracer -input scene6_03_sphere_plane.txt -output output6_03b.tga -size 200 200 -grid 10 10 10 -stats raytracer -input scene6_03_sphere_plane.txt -output output6_03c.tga -size 200 200 -stats -shadows raytracer -input scene6_03_sphere_plane.txt -output output6_03d.tga -size 200 200 -grid 10 10 10 -stats -shadows raytracer -input scene6_03_sphere_plane.txt -output output6_03e.tga -size 200 200 -grid 10 10 10 -visualize_grid
raytracer -input scene6_04_bunny_mesh_200.txt -output output6_04a.tga -size 200 200 -stats raytracer -input scene6_04_bunny_mesh_200.txt -output output6_04b.tga -size 200 200 -grid 10 10 7 -stats raytracer -input scene6_04_bunny_mesh_200.txt -output output6_04c.tga -size 200 200 -stats -shadows raytracer -input scene6_04_bunny_mesh_200.txt -output output6_04d.tga -size 200 200 -grid 10 10 7 -stats -shadows raytracer -input scene6_04_bunny_mesh_200.txt -output output6_04e.tga -size 200 200 -grid 10 10 7 -visualize_grid raytracer -input scene6_05_bunny_mesh_1k.txt -output output6_05.tga -size 200 200 -grid 15 15 12 -stats -shadows raytracer -input scene6_06_bunny_mesh_5k.txt -output output6_06.tga -size 200 200 -grid 20 20 15 -stats -shadows raytracer -input scene6_07_bunny_mesh_40k.txt -output output6_07.tga -size 200 200 -grid 40 40 33 -stats -shadows
raytracer -input scene6_08_scale_translate.txt -size 200 200 -output output6_08a.tga raytracer -input scene6_08_scale_translate.txt -size 200 200 -output output6_08b.tga -grid 15 15 15 raytracer -input scene6_08_scale_translate.txt -size 200 200 -output output6_08c.tga -grid 15 15 15 -visualize_grid
raytracer -input scene6_09_rotated_triangles.txt -size 200 200 -output output6_09a.tga raytracer -input scene6_09_rotated_triangles.txt -size 200 200 -output output6_09b.tga -grid 15 15 9 raytracer -input scene6_09_rotated_triangles.txt -size 200 200 -output output6_09c.tga -grid 15 15 9 -visualize_gridNote: the grid voxelization of the green triangle uses an optional special case for transformed triangles and will look different if you have not implemented this option.
raytracer -input scene6_10_nested_transformations.txt -size 200 200 -output output6_10a.tga raytracer -input scene6_10_nested_transformations.txt -size 200 200 -output output6_10b.tga -grid 30 30 30 raytracer -input scene6_10_nested_transformations.txt -size 200 200 -output output6_10c.tga -grid 30 30 30 -visualize_gridNote: the grid voxelization of the blue rhombus uses an optional special case for transformed triangles and will look different if you have not implemented this option.
raytracer -input scene6_11_mirrored_floor.txt -size 200 200 -output output6_11a.tga -shadows -bounces 1 -weight 0.01 -stats raytracer -input scene6_11_mirrored_floor.txt -size 200 200 -output output6_11b.tga -shadows -bounces 1 -weight 0.01 -grid 40 10 40 -stats raytracer -input scene6_11_mirrored_floor.txt -size 200 200 -output output6_11c.tga -grid 40 10 40 -visualize_grid
raytracer -input scene6_12_faceted_gem.txt -size 200 200 -output output6_12a.tga -shadows -shade_back -bounces 5 -weight 0.01 -stats raytracer -input scene6_12_faceted_gem.txt -size 200 200 -output output6_12b.tga -shadows -shade_back -bounces 5 -weight 0.01 -grid 20 20 20 -stats raytracer -input scene6_12_faceted_gem.txt -size 200 200 -output output6_12c.tga -grid 20 20 20 -visualize_grid
raytracer -input scene6_13_checkerboard.txt -size 200 200 -output output6_13.tga -shadows raytracer -input scene6_14_glass_sphere.txt -size 200 200 -output output6_14.tga -shadows -shade_back -bounces 5 -weight 0.01 -grid 20 20 20
raytracer -input scene6_15_marble_cubes.txt -size 300 300 -output output6_15.tga raytracer -input scene6_16_wood_cubes.txt -size 300 300 -output output6_16.tga
raytracer -input scene6_17_marble_vase.txt -size 300 300 -output output6_17a.tga -grid 15 30 15 -bounces 1 -shadows raytracer -input scene6_17_marble_vase.txt -size 300 300 -output output6_17b.tga -grid 15 30 15 -visualize_gridUPDATE (11/2): this new image correctly handles procedural textures with a large ambient light contribution.
raytracer -input scene6_18_6.837_logo.txt -size 400 200 -output output6_18a.tga -shadows -shade_back -bounces 5 -weight 0.01 -grid 80 30 3 raytracer -input scene6_18_6.837_logo.txt -size 400 200 -output output6_18b.tga -grid 80 30 3 -visualize_grid
See the main Assignments Page for submission information.