CS184 Fall 06: Ray Tracer Design note
Disclamer:
This note
is an organization of a raytracer adapted and greatly
simplified from the PBRT presented in Physically Based Rendering Book (Matt
Pharr and Greg Humphreys). By no mean it is completed (many members, methods
and some of the classes are missing). It is meant to give you a big picture of
what a raytracer code might need to have. You don’t
need to follow this code for your assignment 3. If you start following this
structures and implement each classes directly, it is likely that it will not
work.
The steps recommend are 1) Think
about what a ray tracer should do, 2) Read this note to see how one might
organize the code, 3). Think some more and about how you want to implement the raytracer and then decide how much you want to follow the
guideline. 4) When each class is implemented, make sure to debug it thoroughly,
otherwise when you put them together they will likely not to work at all and it
will be very difficult to debug.
I try to
minimize the typos but if you spot one, please let me know!
-
Nuttapong
High Level
The Scene class has a rendering loop that asks the Sampler class samples for all the pixels in the screen.
For each sample, Scene asks the camera class to create eye ray. Then Scene asks the Raytracer class to compute the color for that ray.
Finally, Scene will tell Film class that the sample has the color value. Film then output the image to the file.
Inside Raytracer class, it asks Primitive to compute the nearest ray-object intersection. Then it shades that pixel, or recursively create mirror reflection ray and call itself.
Then it returns the resulting color to scene. Primitive will transform the ray from world space to object space and hand it to Shape to do intersection in object space.
Shape then computes the intersection and return the result to Primitive.
Basic Classes
Vector
Members:
float x, y, z
Notes:
Constructor from 3 floats
Support vector +, -
Support scalar *,/
Support normalization
Point – Point = Vector
Members:
float x, y, z
Notes:
Constructor from 3 floats
Support +, -
Note: Need to be normalized after operations (Be careful for 0 vector)
Point
Members:
float x, y, z
Notes:
Constructor from 3 floats
Support +,- with vector
Ray
Members:
Point pos
Vector dir
float t_min, t_max
Notes:
It represent the ray ray(t) = pos + t*dir, where t_min <= t <= t_max
Matrix
Members:
float mat[4][4]
Notes:
Support creation of rotation, translation, scaling matrices
May support matrix inversion if needed
Also could support SVD, or other matrix decomposition, for future extension
Transformation
Members:
// Storing matrix m and its inverse transpose, minvt (for transforming normal)
Matrix m, minvt
Notes:
Support Point, Vector,
operator * overloading
Color
Members:
float r, g, b
Notes:
Support +,- with other color
Support scalar *, /
May support conversion from xyz
BRDF
// Storing information enough for shading (it is not the actual BRDF function // in the rendering equation that will be covered later in the semester)
Members:
// kd, ks, ka are diffuse, specular and ambient component respectively
// kr is the mirror reflection coefficient
Color kd, ks, ka, kr
Sample
Members:
float x, y; // Store screen coordinate
LocalGeo
Members:
Point pos
Normal normal
Notes:
Store the local geometry at the intersection point. May need to store
other quantities (eg. texture coordinate) in a more complicated
raytracer.
More Classes
Shape
Methods:
// Test if ray intersects with the shape or not (in object space), if so,
// return intersection point and normal
bool intersect(Ray& ray, float* thit, LocalGeo* local)
// Same as intersect, but just return whether there is any intersection or
// not
bool intersectP(Ray& ray)
Notes:
// Triangle and Sphere are probably best implemented here
// The intersection with the ray at t outside the range [t_min, t_max]
// should return false.
Primitive
Methods:
bool intersect(Ray& ray, float* thit, Intersection* in)
bool intersectP(Ray& ray)
void getBRDF(LocalGeo& local, BRDF* brdf);
Notes:
Abstract class for primitives in the scene
Intersection
Members:
LocalGeo localGeo
Primitive* primitive
GeometricPrimitive
Members:
Transformation objToWorld, worldToObj;
Shape* shape;
Material* mat;
Methods:
bool intersect(Ray& ray, float* thit, Intersection* in) {
Ray oray = worldToObj*ray;
LocalGeo olocal;
if (!shape->intersect(oray, thit, &olocal)) return false;
in->primitive = this;
in->local = objToWorld*olocal;
return true;
}
bool intersectP(Ray& ray) {
Ray oray = worldToObj*ray;
return shape->intersectP(oray);
}
void getBRDF(LocalGeo& local, BRDF* brdf) {
material->getBRDF(local, brdf);
}
AggregatePrimitive
Methods:
AggregatePrimitive(vector<Primitive*> list);
bool intersect(Ray& ray, float* thit, Intersection* in)
bool intersectP(Ray& ray)
void getBRDF(LocalGeo& local, BRDF* brdf) {
exit(1);
// This should never get called, because in->primitive will
// never be an aggregate primitive
}
Notes:
Constructor store the STL vector of pointers to primitives.
Intersect just loops through all the primitives in the list and
call the intersect routine. Compare thit and return that of the nearest one (because we want the first hit).
Also, the in->primitive should be set to the pointer to that primitive.
When you implement acceleration structure, it will replace this class.
Material
Members:
BRDF constantBRDF;
Methods:
void getBRDF(LocalGeo& local, BRDF* brdf) {
return constantBRDF;
}
Notes:
Class for storing material. For this example, it just returns a constant
material regardless of what local is. Later on, when we want to support
texture mapping, this need to be modified.
Sampler
Methods:
bool getSample(Sample* sample);
Notes:
It will generate (x,y) of a screen sample and return true.
Next time it gets called, it will generate another sample for the next pixel. It will return false when all the samples from all the pixels
are generated. (In our case, we generate 1 sample per pixel, at the pixel sample. Later on, if we want to do multi-sample per pixel, we need to modify this class.
Camera
Methods:
void generateRay(Sample& sample, Ray* ray);
Notes:
Create a ray starting from the camera that passes through the
corresponding pixel (sample.x, sample.y) on the image plane.
(from last week discussion, and also section
RayTracer
Methods:
void trace(Ray& ray, int depth, Color* color) {
if (depth exceed some threshold) {
Make the color black and return
}
if (!primitive.intersect(ray, &thit, &in) {
// No intersection
Make the color black and return
}
// Obtain the brdf at intersection point
in.primitive->getBRDF(in.local, &brdf);
// There is an intersection, loop through all light source
for (i = 0; i < #lights; i++) {
lights[i].generateLightRay(in.local, &lray, &lcolor);
// Check if the light is blocked or not
if (!primitive->intersectP(lray))
// If not, do shading calculation for this
// light source
*color += shading(in.local, brdf, lray, lcolor);
}
// Handle mirror reflection
if (brdf.kr > 0) {
reflectRay = createReflectRay(in.local, ray);
// Make a recursive call to trace the reflected ray
trace(reflectRay, depth+1, &tempColor);
*color += brdf.kr * tempColor;
}
}
Notes:
Shading is similar to hw2
Beware when you generate reflection ray, make sure the ray don’t start
exactly on the surface, or the intersection routine may return
intersection point at the starting point of the ray. (This apply to light
ray generation as well)
Light
Methods:
void generateLightRay(LocalGeo& local, Ray* lray, Color* lcolor);
Notes:
This is an abstract class that will generate a ray starting from
the position stored in local to the position of the light source.
You might want to consider creating 2 derived classes for
point light source and directional light source.
For directional light, the origin of the ray is the same, and the ray points to the light direction, however, t_max is infinity.
Film
Methods:
// Will write the color to (sample.x, sample.y) on the image
void commit(Sample& sample, Color& color)
// Output image to a file
void writeImage();
Notes:
Can be implemented just by a 2D array of Color (Later on, we can
implement more complicated things such as multi-sample per pixel, or
post processing, eg. tone mapping in this class)
Scene
Methods:
// This is the main rendering loop
void render() {
while (!sampler.generateSample(&sample) {
camera.generateRay(sample, &ray);
raytracer.trace(ray, &color);
film.commit(sample, color);
}
film.writeImage();
}