CMSC 435/634: Introduction to Computer Graphics

Assignment 1
Ray Tracing
Initial Submission February 6, 2024
Due February 13, 2024 @ 11:59pm

Before you start

Setup a GitHub Repository

You will be doing your work this semester in a GitHub respoitory. Before you get started on any of the assignments, you should create a GitHub repository from the class template repository that contains some hopefully helpful code, input files, and grading rubrics. To create your repository:

  1. Fill out this google form
  2. You will receive an email inviting you to a GitHub organization for this class. Accept the invitation.
  3. Create a new GitHub repository from template as follows The instructor and TA will have access to your repository because we own the organization. Here is a screenshot of what creating your repo should look like.
Now you can just commit and push to your repo as you normally would. The template should have everything for all the assignments but if something is added later in the semester you should be able to pull those changes with
git pull template

Setup Eigen

If Eigen is not already installed on your machine, install it. (Do not put Eigen in your GithHub repository.)

To use Eigen (in particular Eigen::Vector3d will make your life easier), add

-I<path_to_eigen> -I/usr/include/eigen3/ -I/usr/local/include/eigen3
to your compilation flags (<path_to_eigen> is the path to your Eigen installation if it is non-standard, the latter two include paths are common installation paths for Eigen and will make it easier for you to work on other systems and for the Instructor and TA to use your makefile) and add
#include <Eigen/Dense>
in relevant header and code files.

Commit and Push Something

To make sure you do not have last-minute problems, you must commit and push something to your repository by the Initial Submission date above.

The Assignment

For this assignment, you must write a C or C++ program that will render triangles using ray tracing and the barycentric coordinate intersection method. The input is in a simple text format called NFF, containing information about the view and objects in the scene. You will read an NFF scene from a file given on the command line, and ray trace the single image described there. Rays that hit an object should be rendered in the triangle color, while rays that do not hit any object should use the background color of the scene. Do not try to include any of the more advanced ray tracing features you may read about (shadows, reflection, refraction, lights, etc.), we will get to those in the next assignment. Your output should be an image file in PPM format.

You can run my program here on the University's GL machines:

~adamb/public/hide/hide input.nff output.ppm
tetra -s 3

Input

For the base assignment, you should be able to trace tetra-3.nff, which is checked into your proj1 directory. Additional NFF format scenes can be generated using a set of programs called the 'Standard Procedural Databases'. A copy of these programs may be found in ~adamb/public/spd3.14/. While NFF format is relatively simple, it does contain many features we will not be using in this assignment. You should be able to read any NFF format file, but ignore anything you do not implement. For the basic assignment, you should at least handle the "v" viewing specification, "b" background color, "f" object material specification (just the r g b color part, ignore the rest), and "p" polygon specification. "c" and "pp" are all multi-line, so you will at least need to recognize enough of those to know how much to skip. See here for more detail on the commands your program should support

Please note: angle should be the field of view for the entire image, the angle subtended from the left edge of the image to the right edge. Additionally, pixels should be square. The documentation for .nff above adopts other conventions.

Split any polygon with more than three vertices into a fan of triangles: given vertices 0,1,2,3,4, the triangles would be (0,1,2), (0,2,3), (0,3,4)

Since that has 4096 triangles and can be slow to raytrace, using the -s option can yield a simpler models. For example, tetra-3.nff with 64 triangles was generated with

~adamb/public/spd3.14/tetra -s 3 > tetra-3.nff

Tip: I use the std::getline funciton and std::strstream class for parsing. getline is good for reading the file into a string. It is easy to match the first character of that string in a switch statement, then depending on the case, I create a strstream (or many strstreams) to parse the line(s) of input into variables. For example, I have code that looks like

    	case 'b': {
	  std::stringstream ss(line);
	  ss>>ch>>bcolor[0]>>bcolor[1]>>bcolor[2];
	  break;
	}
  
to get the background color.

Output File Format

The PPM format is one of the simplest image formats available. See the man page for 'ppm' on the university GL server for a description.

To create a PPM file, first you should store your image in an array of bytes in y/x/color index order:

    unsigned char pixels[HEIGHT][WIDTH][3];
When filling in this array, remember that it is in y/x order, not the more familiar x/y order. The final index is the color component, with r=0, g=1 and b=2. Color values range from 0 to 255. For example, this would store a floating point color value of .5 into the green component at x,y:
    pixels[y][x][1]= .5*255;
Once you've filled in the pixels array, actually writing the PPM file is quite simple:
FILE *f = fopen("hide.ppm","wb");
fprintf(f, "P6\n%d %d\n%d\n", WIDTH, HEIGHT, 255);
fwrite(pixels, 1, HEIGHT*WIDTH*3, f);
fclose(f);
Alternatively, you could use the C++ file stream i/o:
  std::ofstream out(fname, std::ios::out | std::ios::binary);
  out<<"P6"<<"\n"<<WIDTH<<" "<<HEIGHT<<"\n"<<255<<"\n";
  unsigned char val;
  for (unsigned int y=0; y<HEIGHT; y++)
    for (unsigned int x=0; x<WIDTH; x++) {
	val = pixels[y][x][0];
	out.write (&val, sizeof(unsigned char));
	val = pixels[y][x][1];
	out.write (&val, sizeof(unsigned char));
	val = pixels[y][x][2];
	out.write (&val, sizeof(unsigned char));
    }
  }
  out.close();

Or more simply:
  std::ofstream out(fname, std::ios::out | std::ios::binary);
  out<<"P6"<<"\n"<<WIDTH<<" "<<HEIGHT<<"\n"<<255<<"\n";
  out.write(pixels, WIDTH*HEIGHT*3*sizeof(unsigned char));
  out.close();
But, of course, C++ does not allow
    unsigned char pixels[HEIGHT][WIDTH][3];
But, C++ does allow
  unsigned char *pixels = new unsigned char[WIDTH*HEIGHT*3];
  

gears

Extra Credit

For up to 25 points of extra credit, handle concave polygons. Splitting polygons into a fan of triangles only works for convex polygons. Instead, use the test-ray method to handle arbitrary concave polygons. Try the "gears" SPD program to generate a scene with some pretty complex polygons.

balls

For up to 15 points of extra credit, implement spheres too. Try the balls SPD program for a scene with lots of spheres.

Other people's code

Ray tracing is a popular rendering technique, and the internet contains lots of resources for ray tracers in general and things like ray-object intersection in particular. Other than the PPM snippet above, YOU MAY NOT USE ANY OUTSIDE CODE. All code that you use must be your own.

Strategy

This is a big assignment. Start NOW, or you will probably not finish. No, really, I promise you will not be able to do it in the last two days. Even before we get to all of the details of the ray tracing itself, you can still start working on your file parsing.

Some implementation tips from a previous instructor.

What to turn in

Turn in this assignment electronically by pushing your source code to your proj1 GitHub directory by 11:59 PM on the day of the deadline. We will be looking for multiple checkins documenting your development process.

Do your development in the proj1 directory so we can find it. Be sure the Makefile will build your project when we run 'make' (or edit it so it will). Also include a README.txt file telling us about your assignment. Do not forget to tell us what (if any) help did you receive from books, web sites or people other than the instructor and TA.

Check in along the way with useful checkin messages. We will be looking at your development process, so a complete and perfectly working ray tracer submitted in a single checkin one minute before the deadline will NOT get full credit. Do be sure to check in all of your source code, Makefile, README, and updated .gitignore file, but no build files, log files, generated images, zip files, libraries, or other non-code content.