xxxxxxxxxx
269
function terrain(p) {
const p5 = Object.getPrototypeOf(p).constructor;
const ElevationMultiplier = 10;
const InverseElevationMultiplier = 1 / ElevationMultiplier;
const size = 50;
const scale = 20;
let elevation;
let normals;
let gradient;
let debug = false;
p.preload = function () {
gradient = p.loadImage("TerrainGradient.png");
};
p.setup = function () {
p.createCanvas(p.windowWidth, p.windowHeight, p.WEBGL);
elevation = new Array(size);
for (let x = 0; x < size; x++) {
elevation[x] = new Array(size);
for (let z = 0; z < size; z++) {
elevation[x][z] = p.noise(x / 10, z / 10);
}
}
normals = new Array(size);
for (let x = 0; x < size; x++) {
normals[x] = new Array(size);
for (let z = 0; z < size; z++) {
let n = p.createVector(0, 0, 0);
// Compute the average normal for the following adjacent faces:
// .---.
// /|3 /|
// / | / |
// /4 |/ 2|
// +---*---+
// |5 /|1 /
// | / | /
// |/ 6|/
// '---'
if (x < size - 1) {
if (z < size - 1) {
// Face #1
n = n.add(
p
.createVector(
InverseElevationMultiplier,
elevation[x + 1][z] - elevation[x][z],
0
)
.cross(
p.createVector(
0,
elevation[x][z + 1] - elevation[x][z],
InverseElevationMultiplier
)
)
);
}
if (z > 0) {
// Face #2
n = n.add(
p
.createVector(
-1 * InverseElevationMultiplier,
elevation[x][z] - elevation[x + 1][z],
0
)
.cross(
p.createVector(
0,
elevation[x + 1][z - 1] - elevation[x + 1][z],
-1 * InverseElevationMultiplier
)
)
);
// Face #3
n = n.add(
p
.createVector(
InverseElevationMultiplier,
elevation[x + 1][z - 1] - elevation[x][z - 1],
0
)
.cross(
p.createVector(
0,
elevation[x][z] - elevation[x][z - 1],
InverseElevationMultiplier
)
)
);
}
}
if (x > 0) {
if (z > 0) {
// Face #4
n = n.add(
p
.createVector(
-1 * InverseElevationMultiplier,
elevation[x - 1][z] - elevation[x][z],
0
)
.cross(
p.createVector(
0,
elevation[x][z - 1] - elevation[x][z],
-1 * InverseElevationMultiplier
)
)
);
}
if (z < size - 1) {
// Face #5
n = n.add(
p
.createVector(
InverseElevationMultiplier,
elevation[x][z] - elevation[x - 1][z],
0
)
.cross(
p.createVector(
0,
elevation[x - 1][z + 1] - elevation[x - 1][z],
InverseElevationMultiplier
)
)
);
// Face #6
n = n.add(
p
.createVector(
-1 * InverseElevationMultiplier,
elevation[x - 1][z + 1] - elevation[x][z + 1],
0
)
.cross(
p.createVector(
0,
elevation[x][z] - elevation[x][z + 1],
-1 * InverseElevationMultiplier
)
)
);
}
}
normals[x][z] = n.normalize();
}
}
};
p.draw = function () {
p.background("gray");
p.orbitControl(3, 2, 0.1);
p.ambientLight(50);
p.directionalLight(
255,
255,
255,
Math.cos(p.millis() / 5000),
Math.sin(p.millis() / 5000),
0
);
p.noStroke();
p.push();
p.translate(
scale * ElevationMultiplier * -3 * Math.cos(p.millis() / 5000),
scale * ElevationMultiplier * -3 * Math.sin(p.millis() / 5000),
0
);
p.emissiveMaterial(255, 255, 0);
p.sphere(20);
p.pop();
if (!debug) {
p.push();
p.ambientMaterial(80, 80, 255);
// p.specularMaterial(40, 40, 255);
p.rotateX(Math.PI / 2);
p.translate(0, 0, -110);
p.plane(size * scale, size * scale);
p.pop();
}
// p.normalMaterial();
p.translate((-1 * size * scale) / 2, 0, (-1 * size * scale) / 2);
p.stroke("black");
p.push();
if (debug) {
p.stroke("green");
} else {
p.texture(gradient);
}
for (let x = 0; x < size - 1; x++) {
p.beginShape(p.TRIANGLE_STRIP);
for (let z = 0; z < size; z++) {
p.normal(normals[x][z].x, normals[x][z].y, normals[x][z].z);
p.vertex(
x * scale,
elevation[x][z] * ElevationMultiplier * scale,
z * scale,
0,
Math.min(100, Math.max(0, elevation[x][z] * 230 - 40))
);
// A bug in the normal() function causes this to incorrectly report an error.
p.normal(normals[x + 1][z]);
p.vertex(
(x + 1) * scale,
elevation[x + 1][z] * ElevationMultiplier * scale,
z * scale,
10,
Math.min(100, Math.max(0, elevation[x + 1][z] * 230 - 40))
);
}
p.endShape();
}
p.pop();
if (debug) {
// Debug normal vectors
p.push();
p.noFill();
p.stroke("red");
p.beginShape(p.LINES);
let ni = 0;
for (let x = 0; x < size; x++) {
for (let z = 0; z < size; z++) {
let normal = p5.Vector.mult(normals[x][z], 4);
p.vertex(
x * scale,
elevation[x][z] * ElevationMultiplier * scale,
z * scale
);
p.vertex(
x * scale + normal.x * 6,
elevation[x][z] * ElevationMultiplier * scale + normal.y * 6,
z * scale + normal.z * 6
);
}
}
p.endShape();
p.pop();
}
};
p.keyPressed = function (e) {
if (e.key === "d") {
debug = !debug;
}
};
}
let sketch = new p5(terrain);