![]() |
Lab 6: Fractal Landscape
|
You are to work alone for this lab.
In this lab you are given a digital elevation model (DEM) which consists of a 2D grid of height values. Your job is to convert the DEM to a surface (triangles/vertices), compute the normals for each vertex and, in the fragment shader, implement a color ramp which generates the color of each fragment based on the height (y value in object coordinates) of the vertex.
In part 1 you can just set the color based on the uniform color. In part 2, you will write a color ramp method in the Javascript code to compute the vertex color based on the height of the vertex. In part 3, you will implement the color ramp directly in the fragment shader.
Part 1:
Start with a copy of your program from Lab 5. You can do this by creating a new Lab6 folder and copying/pasting your Lab 5 code into the Lab 6 folder. Be sure to keep your original Lab 5 code. Use the Phong Shader.
Download FractalDEM.js and place in the geometry folder of Lab 6.
Create a new class e.g. called Fractal.js (along the lines of the Cube or Disk) which takes the heights calculated in FractalDEM and generates a list of triangles (vertices), colors, and normals. Read the comments in FractalDEM so you understand what is being generated.
In calculating the vertices, the scaling of the landscape takes some thought. The heights calculated in FractalDEM.js are already scaled to be between 0 (water level) and 1 (mountain top).
For Fractal.js, it is suggested that you pass in, as a parameter, the total length of the landscape along x and z,
along with the power of 2 for the grid. The size of each xz grid can then be calculated based on the length and the number of grids.
(Note, when selecting the xz length, it is best to choose a value that looks about right in proportion to the y-value (height between 0 and 1) because, if you need to scale in the render
function, it is best to use uniform scaling otherwise the normals may not be quite right.)
For now, just set all the vertex colors to the same color. In this part, we will just use uniform colors. The vertex colors will be used in part 2.
As before, when you add a new shape, you need to load the .js files (in start_here.js) and add a Fractal object to Shapes.js similar to the way you did for the other primitive shapes.
Before trying to render the landscape, just try running the code (i.e. the
landscape shape can be created in Shapes.js but not yet rendered in the render
function). If you have errors in you code, you can try to fix them now. Select a really small
grid size and print out the list of vertices, colors, and normals to
make sure they look somewhat correct. (Getting an array that is the
wrong size or values with incorrect
dimensions, crazy values, or NaN, are a clue that something is wrong!)
Once you are confident that the buffer arrays (vertex, normal, color)
look correct, you can try actually rendering the landscape using the
Phong shader from Lab 5. Set the color in the render
function using the uniform
color uColor since that is what the shader expects.
Once you get the landscape up and running, examine it carefully to see if the lighting looks correct. If not, you may need to go back and double check your normals.
Part 2: Next, you are to switch to vertex colors and set the colors based on the height (y-value) of the landscape:
Next, implement a color ramp. That is, calculate the vertex colors in the colors array based on an elevation (y-value of the position).
It is suggested that you write a getColor
function in Fractal.sj which passes in a height (y-value) and returns a color vector.
Part 3: The vertex colors don't look great because of how they are interpolated across triangles. You can see this better in the closeup on the right above. For this reason, you are now to move the color computation to the fragment shader.
To do this, the fragment shader first needs the height, which is the y-value of the untransformed attribute variable vPosition. Attribute variables are only available in the vertex shader so you need to create a varying variable that sends the (interpolated) height value to the fragment shader:
In the vertex shader declare a varying variable:
varying float yval;
then, in the vertex shader's main, set yval to the y value of vPosition (vPosition.y
) before vPosition has been
transformed to eye coordinates.
In the fragment shader, add the same declaration for yval
.
Now that you have access to the height value, yval
, in the fragment shader, you can use this height to
calculate a color much like what you did
in getColor to calculate the color array, only now, you need to write code in glsl. To do the interpolation
of the colors, it is handy to use the mix
and smoothstep
functions available in glsl. Look up what
they do in the glsl documentation. For example if
you have the color c0 at height h0, and c1 at height h1, and your yval is between h0 and h1, then
myColor = mix(c0, c1, smoothstep(h0,h1,yval) );
By the beginning of lab on the due date above, be ready to demonstrate your finished program. The scene should show your fractal landscape with a light source that can be moved around. The landscape should have a realistic looking color ramp (e.g. blue for water, white for snow peaks, etc) implemented in the fragment shader.
Then submit your code to WISE: