Programming Assignment 1
CS 45500 / CS 51580
Computer Graphics
Fall, 2024

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

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 simulates a two-dimensional 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 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 Irfanview and the 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 (that is, all the methods in the FrameBuffer class), 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. 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 (and 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,100-by-700 pixels. You can think of the framebuffer as being made up of a checkerboard with 60 squares that are 100-by-100 pixels and the 6-by-10 checkerboard has a border around it that is 50 pixels wide. When you draw the checkerboard pattern, you do not need to draw all 60 squares. You only need to draw 30 squares, because the other 30 squares get their color from the background color.

There are many ways to implement the striped pattern. Notice that every row of the pattern is the same as the row above it, just shifted to the right by one pixel. Each stripe is 30 pixels wide.

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

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".

After you place the two copies of RebelTrooper.ppm into the framebuffer, you need to create the gray bordered cutout of six checkerboard squares. The easiest way to copy pixels from one place in the framebuffer (the source) to another place in the framebufer (the destination) is to make the source rectangle into a viewport and then make the destination rectangle into another viewport that is initialized with the data from the source viewport (see the FrameBuffer API to see which Viewport constructors you should use).

The last step of creating the Hw1.ppm image it to add Dumbledore's "ghost". Load the Dumbledore pixel data into 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 and also the pixels (r,g,b) color values.

Turn in a zip file called CS455Hw1Surname.zip (where Surname is your last name) containing your version of Hw1.java.

This assignment is due Tuesday, September 10.