Programming Assignment 1
CS 45500
Computer Graphics
Fall, 2023

This assignment makes use of the files contained in this zip file. This assignment is due Thursday, September 7.

This assignment and your next assignment are about the data structures used at the two ends of the 3D graphics rendering pipeline; what goes into the beginning of the pipeline and what comes out of the end of the pipeline. Roughly, what goes into the pipeline is the Scene data structure which describes the geometry of what the renderer should draw. What comes out of the pipeline is the FrameBuffer data structure which holds the image of the scene drawn by the renderer. This assignment is about what comes out of the graphics pipeline, the FrameBuffer data structure. Assignment 2 will be about what goes into the rendering pipeline, the Scene data structure.

A FrameBuffer object holds an array of pixel data that represents an image that can be displayed on a computer's screen. For each pixel in the image, the FrameBuffer's array holds three bytes, one byte that represents the red component of the pixel's color, one byte that represents the green component, and one byte that represents the blue component of the pixel's color. Each of these three bytes is only eight bits in size, so each of the three colors has only 256 shades, but there are 256^3 = 16,777,216 distinct pixel colors. If a framebuffer has dimensions of n rows of pixels by m columns of pixels, then the framebuffer's array holds n*m integers and each integer holds the three bytes for one pixel (with one unused byte in each integer). The pixel data is NOT stored as a "two-dimensional" (or "three-dimensional") array. It is stored as a one-dimensional integer array of length n*m. This one-dimensional array is in row major form, meaning that the first m integers in the array are the pixels from the image's first (top) row. The next m integers from the array are the pixels from the image's second row, etc. Finally, the last m integers in the array are the pixels from the image's last (bottom) row of pixels as displayed on the computer's screen.

In this assignment you are to write a program that creates a FrameBuffer object and then fills it with pixel data so that the resulting image looks like the image in the file Hw1_demo.png from the zip file. In the zip file there is a file Hw1.java that you need to complete. In Hw1.java there is a brief outline of what you need to do.

Your program should produce an image that looks exactly like Hw1_demo.png. There are a number of facts about the image in Hw1_demo.png that you need to find out (like, what are the exact colors, what are the dimensions of the little squares?). Use tools from pixel-utilities.zip to determine these details about Hw1_demo.png.

when you write your code, you should make as much use of the FrameBuffer interface as possible. To learn about the FrameBuffer class's interface, look at its source code in the framebuffer sub folder of the zip file. You should also build and read the Javadoc html file for the FrameBuffer class. And the FrameBuffer.java class file also contains a main() method that tests the code in the class. Read that main() method carefully because it is a good demonstration of how to write code that uses the FrameBuffer class and its Viewport nested class.

Notice that the program Hw1.java uses a Java Properties file, called assets.properties, which is located in the zip file. The assets.properties file holds the names of the two image files that need to be imbedded into the final picture. Your program should refer to these two images using the values extracted from the properties file (you should not hard-code the image file names into your source code). This abstracts your program from the exact location of the two image files. When you are writing your solution, the two image files are in the assets subfolder of the zip file. When I grade your assignment, the two image files will be in a folder that I use for grading (I will change the contents of the assets.properties file when I grade the assignments).

Your program should not read or write any files other than the two input files given by the properties file and the output file Hw1.ppm.

The framebuffer that will hold the final image should be 1,000-by-700 pixels. You can think of the framebuffer as being made up of 70 squares that are 100-by-100 pixels. Each of those 100-by-100 pixel squares contains a two pixel wide border around it. That means that all the grid lines are four pixels wide each (except for the border around the outer edge of the framebuffer, which is two pixels wide).

There are many ways to implement the striped checkerboard pattern. The most elegant way makes use of the integer division and integer remainder operators. You can create this pattern with as little as ten lines of code.

When you write the code to implement the striped disk, use the distance formula from geometry. For each pixel in the viewport, compute the distance from that pixel to the center of the viewport. Use the distance value to choose the pixel's color.

When you copy pixel data from the RebelTrooper.ppm framebuffer into the main framebuffer, you should only copy a pixel if it is not a background pixel. For the most part, the background pixels are white (the color with (r, g, b) = (255, 255, 255)). But some of the background pixels near the image are not quite exactly white. For example, a background pixel very near the image might be (253, 252, 254). In order to get a good sharp "cutout" of the image, it helps to not copy any pixel that is "nearly" white. You need to come up with a logical test for "nearly white".

The last step of creating the Hw1.ppm image it to add Dumbledore's "ghost". Load the Dumbledore pixel data in its own FrameBuffer object. Then create a Viewport object the covers the background area where Dumbledore should appear. Use two nested for-loops to simultaneously step through the pixels of the Dumbledore framebuffer and the background viewport. For each pixel from the Dumbledore framebuffer, if that pixel is not white (or not almost white) then blend that pixel from the Dumbledore framebuffer with the corresponding background pixel from the viewport. Blend each pixel with 60% weight on the Dumbledore (framebuffer) pixel and 40% weight on the background (viewport) pixel. So if c1 is the pixel color from the framebuffer and c2 is the color from the viewport, then blend them (more or less) like this (for the red component, similarly for the green and blue components),


      new_red = 0.6 * c1.getRed() + 0.4 * c2.getRed()

After you compute the new blended color components, you write that new color into the viewport's pixel. By averaging Dumbledore's pixels with the background pixels already in the viewport, you create the effect of a "see-through" ghost of Dumbledore superimposed on the framebuffer's contents.

In the zip file there are two image files, Hw1_demo_viewports.png and Hw1_demo_without_images.png, that you can use to help figure out exactly where the upper left-hand corner of each viewport is located in the framebuffer. If you open any image file in IrfanView and you click on any pixel in the image, IrfanView will show you, on the left end of its title-bar, the (x,y)-coordinates of the pixel.

When your version of Hw1.java is able to duplicate the image in Hw1_demo.png, then you can start to work on the file Hw1_fade.java. The Hw1_fade.java program should produce the frames of an animation that looks like the animation in Hw1_fade_demo.gif (the individual frames from Hw1_fade_demo.gif are in the sub-folder fade_demo_frames). This animation makes the Dumbledore image fade by 1% in each of 100 frames (the image in Hw1_demo.png has Dumbledore faded by 40%). You start on Hw1_fade.java by copying almost all of your code from Hw1.java and then putting the last part of your code from Hw1.java in a for-loop that repeatedly blends Dumbledore with the background of the framebuffer, but with 1% less weight placed on the Dumbledore image (and 1% more weight placed on the background image) with each iteration of the loop. The Hw1_fade.java file contains an outline of what the for-loop should look like (in the above line of code that computes new_red, replace 0.6 with the variable fade, and replace 0.4 with 1.0-fade). When your Hw1_fade.java program runs, it should produce 101 ppm files with Hw1_fade_000.ppm having Dumbledore solid, and with Hw1_fade_100.ppm having Dumbledore disappeared.

Turn in a zip file called CS455Hw1Surname.zip (where Surname is your last name) containing your versions of Hw1.java and Hw1_fade.java. Please do NOT submit your frames of the animation or the demo frames from the zip file. Please only submit your two Java source files.

This assignment is due Thursday, September 7.