This assignment makes use of the files contained in this zip file. This assignment is due Monday, September 15.
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 (from a Java Color object), 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.
You should not change the package structure of the folder hw1
. The file Hw1.java
is in the default package of the folder hw1
and the FrameBufer
class is in the framebuffer
package in the hw1
folder. You should not modify the FrameBuffer
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). If you want, you can modify the properties file to use the other images from the assets folder. Your program should work just as well with any two of the images from the assets folder.
Your program should not read or write any files other than 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. The squares are separated by grid lines that are four pixels wide. Each grid line uses two pixels from each of the squares on either side of it (so each light colored square is actually 96-by-96 pixels). When you draw the checkerboard pattern, do not draw 60 squares. You should fill the 1,100-by-700 framebuffer with the dark background color, then create a 1,000-by-600 viewport filled with the lighter color, and then draw the horizontal and vertical grid lines on top of the lighter color. After you draw the horizontal and vertical lines, draw the four short diagonals at the corners of the framebuffer.
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. Each square in the striped checkerboard is 30-by-30 pixels.
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 squares. The easiest way to copy pixels from one place in the framebuffer (the source) to another place in the framebuffer (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 that 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 only your version of Hw1.java
.
This assignment is due Monday, September 15.