Java代写:CMPT361 Render Components

解析指定的语句,对图像进行渲染。

Requirement

For this assignment, you are to interpret a 3D graphics specification language, along with doing some preparatory work. The preparatory work is (1) to incorporate lerping and blerping into your line and polygon renderers, and (2) to implement zbuffering. The graphics interpretation will involve manipulating and transforming polygons and lines, and rendering using depth cueing.

These rendering components should be shown in a program that displays eight pages. Each of the required pages displays 1 panel, as shown in the following diagram:

The drawing area should be 650 x 650 pixels, the the white margins are all 50 pixels wide or tall. Thus, the total display area is 750 x 750 pixels. Use white and black for the backgrounds, as shown above.

You will be using and modifying your line drawing and polygon filling from assignment 1. You only need one line drawer for this assignment; I recommend using the DDA.

Color Interpolation

Modify your line drawer to linearly interpolate (lerp) RGB colors between its endpoints. Assume that each endpoint of a line is given to you with an RGB color attached. (You can decide between keeping colors in the range 0-1 or the range 0-255.) For each component (R, G, and B), you must interpolate that component.

Interpolating a color component is exactly like interpolating the y-value of the line when working in the first octant. You get the (say) red component r1 for p1, and the red component r2 for p2, and compute dr/dx = (r2-r1) / (x2-x1). Start the line with r = r1 and x = x1, and each time you advance one unit in x, you add dr/dx to r (and round). Do the same for g and b, and when writing the pixel with the current x and y, use the current r, g, and b. You should get a smooth gradation of color along a line.

Similarly, modify your polygon renderer to do bi-linear interpolation (blerping) of RGB colors between its vertices.

For pages 1 and 2 of your assignment, use the same setup as the perturbed mesh test from assignment 1 (page 4, third panel). Almost fill the (650 x 650) drawing area, not the smaller panel from assignment 1. On page 1, draw the mesh in wireframe: draw the edges (lines) of the mesh, not the filled polygons. On page 2, draw the mesh as solid: don’t use any lines, just fill in the polygons. On each page, assign a random color to each vertex of the mesh. These pages are for showing me that your color interpolation works.

To implement wireframe rendering, I recommend that you make a class that satisfies the same interface as your polygon renderer, but that class simply calls a line renderer (which one could be a constructor argument) to render the (usually 3) edges of a polygon.

Z-buffering

Allocate an array of doubles (calling it, say, zBuffer) that is the size of the image window (650 x 650). Set the entire array to 200 (which will be our back clipping plane) at the start of each page. Whenever you are about to write a pixel (x, y) with world-space coordinate z, first compare z to the zBuffer value at (x, y). If z [ zBuffer(x, y) then go ahead a write the pixel. Otherwise, do not write the pixel.

For page 3 of your assignment, I want you to draw 6 triangles (filled). Each triangle should be given a color of (v, v, v) for v=1, .85, .7, .55, .4, .25, respectively, for triangles 1 through 6. Each triangle should be regular (all three sides the same length), with vertices on the circle centered at the center of you drawing area, with radius 275. Give each triangle a random rotation about that center, say, in the interval from 0 to 120 degrees. Also give each triangle a random z-coordinate chosen between 1 and 199 inclusive. (Either floating random numbers or integer random numbers is okay.) Render the triangles, in order, using z-buffering.

Graphics file reading

You are required to be able to read in a file in “simp” format, which is as follows. Simp format files always have a “.simp” extension.

Whitespace means spaces, tabs, carriage returns, and newlines.

A line containing whitespace only is ignored.

A line starting with “#” (before any whitespace or other characters) is a comment and is also ignored.

The only other valid lines are ones containing one command from the commands shown in the following table. Any of these lines may start with an arbitrary number of whitespace characters.

All numbers are double precision.

Anything in angle brackets or double-quotes in the table is called a parameter. All parameters that are points (denoted with [pN] below, where N is a number) are of the format “(x, y, z)” where each of x, y, and z is a number. To simplify interpretation, each comma must immediately follow the number before it (no spaces or tabs inbetween), and a space after each comma is mandatory. Adjacent parameters must be separated by tabs and/or spaces (and no commas).

The language is case-sensitive. Use “rotate Z 45” and not “ROTATE Z 45” or “rotate z 45”. Also, there is only one “command” per line. Only whitespace is allowed on a line after the command and arguments (if any). (There are no trailing comments.

Please indent lines between { and }, as you would in C++ or java.

The first section of lines/commands are things that affect the current coordinate system or current transformation matrix (CTM). The second section are our primitives: the shapes that our renderer should know about. The third section are renderer directives, telling the renderer how to render the primitives that we have specified. The default rendering type is “filled.”

If you encounter any malformed file, you may abort your program or allow it to misbehave however it likes. We will not be testing behaviour on malformed files.

When you get a primitive, you should transform it from the current coordinate system to the world coordinate system by multiplying the vertices by the CTM.

The CTM starts as the identity matrix; this represents world space. The rendering style starts as “filled.”

We will be viewing only a small part of the world (world space). The panel we are using should map to the interval -100 to 100 in both X and Y in world space. We are looking down the z-axis, and we will use parallel projection to view objects. (This means that to convert from world space to window space, we simply forget about the Z coordinate.) To find screen space from the window space, scale by the appropriate amount (to convert 200 to 650) and translate it so that the origin is in the middle of the screen.

The range of Z coordinates that we are interested in are from 0 to 200. Anything with negative Z or Z greater than 200 should not be seen.

If a polygon or a line is completely outside any of the six planes defining the viewing volume, then cull it (do not draw it). Otherwise (it is partially or fully inside, or it is outside the viewing volume but not outside any one plane), you may use pixel clipping (scissor testing) to keep the image in the specified volume. Do not use pixel clipping on primitives that are entirely outside the volume.

We will be using depth cueing or depth shading to render our primitives from pages 3 on. In this technique, we give an object a color that indicates how deep it is into the scene. Typically, the back (or far) clipping plane (in our setup, Z=200) is given the color black and the front (or near) clipping plane (Z=0) is given white or some other bright color. Color is linearly varied through the Z range. For your renderer, when you find the world-space Z coordinate of a vertex, then that point/vertex gets the corresponding color. (For instance, assume your near clipping plane color is white (1, 1, 1). if a point is at (32, 49, 150) it would get a color of (.25, .25, .25). [150 is a quarter of the way from 200 (black) to 0 (white)]. Send this color along with your vertex to your renderer, and have your renderer interpolate the colors between its vertices or endpoints.

To create simp files, you may hand-edit them and/or you may use programs (that you have written) to create them. I will provide you with a file box.simp that contains a simple unit cube (one having every coordinate on every vertex either 0 or 1).

Page 4. In simp format, make a scene that contains several scaled boxes with some boxes (or parts of boxes) as far back as Z=200, and some as close as Z=0. Render with depth cueing, from full green (0, 1, 0) for the near color to full black (0, 0, 0) for the far color. Show rotations, translations, and scales of boxes. Render filled polygons.

Page 5. In simp format, and using at least one file command, make a scene that shows off the technical quality of your work. Use your own colors (or black and white) for the depth cueing. Use interesting shapes: spheres, cylinders, mountains, etc.

Pages 6 to 8. Read and display the following files from the current directory, with the black-to-white depth cueing and setup as above: test1.simp (page 6), test2.simp (page 7), and test3.simp (page 8). These are unknown files that we will supply when testing your program. We will release these files (and any files that they reference) with the assignment marks.

Implementation notes

Note 1

To read a file, start simple. First open the file (probably using an ifstream in C++, or a BufferedReader in java). Then just loop through the file, reading a line at a time, until there is no more in the file. Read the line as a String if possible, and call a function on that string right after reading it. Here’s the pseudocode:

1
2
3
4
5
6
fileStream = open(filename)
while (fileStream has input left) {
s = readALine(fileStream)
interpret(s)
}
close fileStream

Now all that remains is to write interpret. Start with an interpret that just prints the line, to be sure that you’re doing the above loop correctly. If that checks out, then all you have to do is interpret each line.

First, check for comments (s[0] is ‘#’) and blank lines (every character of s is space or tab). Simply return if you find one of these cases.

For distinguishing between the commands, I recommend using java’s String.split(), an STL equivalent if there is one, or simply writing one yourself. Split takes a string and a regular expression, and splits the string whereever it finds the regular expression. For our purposes, the regular expression is simply any (positive) number of spaces and/or tabs ([ \t]+). Split returns an array of Strings, which is the original String broken up into pieces. For example

1
2
3
4
5
String inputLine = "whodo do \tyou do?";
String[] tokens = inputLine.split("[ \t]+");
for (String token: tokens) {
System.out.println(token);
}

results in:

whodo
do
you
do?

Those are the four strings that tokens[] contains. If you do this split on an input line, then the first token you get would be the command. Use an if-then-elseif-elseif-elseif- or a switch statement to see which command you have. Once you have the command, you know how to interpret the rest of the tokenseach one is a parameter. Some numeric parameters may be preceded by a ‘(‘ or followed by a ‘)’ or a ‘,’, so you should trim these if they are present. For example, if inputLine in the above code is “line (1, 2.3, 4.5) (7.8, 9.01, 2.34)”, one gets:

line
(1,
2.3,
4.5)
(7.8,
9.01,
2.34)

To get rid of those extra characters, use something like this pseudocode, probably in its own subroutine (you figure out the params and the return value). You can call the subroutine whenever you have a numeric parameter. Other parameters (axes and filenames) don’t need this. (Although filenames will need you to remove the double-quotes.)

1
2
3
4
5
6
if (currentToken[0] == '(') {
currentToken = currentToken.substring(1, currentToken.length)
}
if(currentToken[currentToken.length-1] == ')' or ',') {
currentToken = currentToken.substring(0, currentToken.length-1)
}

Note 2

To perform the interpretation of a file, you will need the following objects1:

  1. A boolean or enum indicating whether you are rendering wireframe or filled polygons. Initially, this should be set to “filled”.
  2. A 4 x 4 matrix CTM, the Current Transformation Matrix. Initially, this should be set to the identity matrix, which means world space.
  3. A stack of 4 x 4 matrices. Initially, the stack is empty.
  4. A current file-reading object (ifstream, or whatever) to read commands from. Initially, set this to the file you are supposed to read.
  5. A stack of file-reading objects Initially, this is empty.
  6. A z-buffer attached to your (650 x 650) drawing area. Initially, set all values to 200.
  7. A line and a polygon renderer, and a wireframe polygon renderer, all with lerping or blerping.

Each command has a simple interpretation in terms of these objects.