Web代写:CS418 Terrain MP

使用JavaScript绘制一副3D的Fractal Terrain.

Fractal Terrain

Requirement

In this MP you will

  1. Generate a grid of user-specified size
  2. Use a simple fracture-based fractal to make it look like rough terrain

This MP is core, with no elective components. It assumes you have already completed the Logo MP.

Overview

You will submit a webpage that has

  • Two number entry fields
  • One button
  • One canvas
  • A 3D view of dynamically-generated fractal terrain

Submit an HTML file and any number of js and glsl files. No image files, JSON files, or CSS files are permitted for this assignment.

You are welcome to use a JavaScript math library, such as the one used in in-class examples or others you might know.

Specification

HTML input setup

HTML input elements, styling, and event handling are beyond the scope of this class, so we simply give you what you need here.

Your HTML file should have the following after the various script elements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
  <style>
body {
margin: 0; border: none; padding: 0;
display: flex; flex-direction: column;
width: 100%; height: 100vh;
}
.controls {
flex: 0 0 auto;
}
.controls > * { margin: 1em; }
.display {
flex-grow: 1;
line-height: 0rem;
}
</style>

</head>
<body>
<form class="controls" action="javascript:void(0);">
<label>Grid size: <input id="gridsize" type="number" value="50"/></label>
<label>Faults: <input id="faults" type="number" value="50"/></label>
<input id="submit" type="submit" value="Regenerate Terrain"/>
</form>
<div class="display">
<canvas width="300" height="300"></canvas>
</div>
</body>
</html>

You should register the following with a window.addEventListener(‘resize’,fillscreen) in your setup function after setting window.gl to handle canvas resize:

1
2
3
4
5
6
7
8
9
10
11
12
function fillScreen() {
let canvas = document.querySelector('canvas')
document.body.style.margin = '0'
canvas.style.width = '100%'
canvas.style.height = '100%'
canvas.width = canvas.clientWidth
canvas.height = canvas.clientHeight
canvas.style.width = ''
canvas.style.height = ''
gl.viewport(0,0, canvas.width, canvas.height)
// TO DO: compute a new projection matrix based on the width/height aspect ratio
}

You should listen to input events like so:

1
2
3
4
5
document.querySelector('#submit').addEventListener('click', event =] {
const gridsize = Number(document.querySelector('#gridsize').value) || 2
const faults = Number(document.querySelector('#faults').value) || 0
// TO DO: generate a new gridsize-by-gridsize grid here, then apply faults to it
})

Because the program starts with no terrain generated yet, add a check in your draw callback that skips drawing if there’s nothing to draw.

Generate terrain

When the button is clicked,

Create a square grid with gridsize vertices per side.

Don’t duplicate vertices. We will check that it has exactly gridsize*gridsize vertices and (gridsize-1)*(gridsize-1)*2 triangles.

Your code should wok for any integer gridsize between 2 and 255, inclusive.

Most students find it easier to make the grid occupy the same range of x values regardless of the gridsize, as that simplifies setting up the view and projection matrices. Positions between -1 and +1 are the most popular choice.

Larger gridsize should result in higher-resolution terrain, but not otherwise change the visual appearance; for example, the terrain should occupy the same part of the field of view, have the same ratio of height to width, etc, at all gridsizes.

Displace the vertices in the grid with faults faults.

For 0 faults, the result should be perfectly flat. For more faults it should approach a fractal bumpy terrain.

Your code should wok for any non-negative integer faults; there is no upper limit to what we might provide.

Faults should be distributed uniformly on average, with no bias towards particular fault directions no parts of the grid that get many fewer faults than other parts.

Normalize heights so that the terrain has the same height regardless of the number of faults (assuming there is at least one fault).

To do this, find the min and max heights after faulting, then replace each height where c is a constant that changes how tall the highest peak is.

Compute correct grid-based normals.

Because grids usually have 6 triangles around each vertex, generic triangle-based normals tend to look asymmetric. But we can do much better with less work by taking advantage of the structure of the grid.

Consider a vertex v with four neighboring vertices: s to the south, w to the west, n to the north, and e to the east. Then the normal at v is (n - 8) * (w - e). If v is on the edge of the grid and one or more of those neighbors is missing, use v itself in place of the missing neighbors in the normal computation.

Render and light

Render the terrain with one white light source and with both diffuse and specular lighting. Use an earth tone (i.e. 1 > r > g > b > 0) for the diffuse color, picking a color light enough that lighting is obvious. Use white for the specular highlights. Have the light source coming from above the terrain, but not directly above it.

Have the camera moving around the terrain. The terrain should fill most of the camera’s field of view, and most of the terrain should be in the camera’s field of view. The camera should be a little above the highest peak in the terrain.

The light source should be fixed relative to the terrain: that is, changing the view should not change how much diffuse lighting any given part of the terrain has.

Evaluating your results

On both your development machine and when submitted to the submission server and then viewed by clicking the HTML link, the resulting page should initially be blank. Once the button is pressed a terrain should appear, filling most of the screen with a moving camera. One example might be the following: A video of an example result.