CS184 Fall 07:     Ray Tracer Design note       

                       

Disclamer:

This note describes the organization of a raytracer adapted and greatly simplified from the PBRT presented in Physically Based Rendering (Matt Pharr and Greg Humphreys). By no means it is complete (many members, methods and some classes are missing). It is meant to give you a high-level idea of how you might architect your raytracer. Use it as a guide, not a crutch.

The recommended steps are 1) Think about what a raytracer 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 work at all and it will be very difficult to debug.

START EARLY!

 

 

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 returns 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 

 

Normal

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, Normal, Ray, LocalGeo transformation by

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 10.1 in Shirley’s book)

 

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();

                        }