Skip to content

Commit 3298d97

Browse files
committed
Finished noise lab
1 parent c30845c commit 3298d97

File tree

4 files changed

+268
-8
lines changed

4 files changed

+268
-8
lines changed

Noise.html

+132-8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@
3232
<!-- Simulations -->
3333
<script src="/SimulationLabs/Scripts/Noise/RandomHeightMap.js" defer></script>
3434
<script src="/SimulationLabs/Scripts/Noise/NoiseHeightMap.js" defer></script>
35+
<script src="/SimulationLabs/Scripts/Noise/SmoothNoiseHeightMap.js" defer></script>
36+
<script src="/SimulationLabs/Scripts/Noise/DesertIslands.js" defer></script>
37+
<script src="/SimulationLabs/Scripts/Noise/DetailedDesertIslands.js" defer></script>
3538
</head>
3639

3740
<body>
@@ -47,14 +50,13 @@ <h2> What you'll create </h2>
4750
Hopefully, you'll be able to apply this in your own projects!
4851
Along the way, we'll learn a little bit about p5.js, rendering, and simulating physics. </p>
4952

50-
<div id="BoidContainer"></div>
51-
52-
<em> (This is not an actual boids sketch btw - this is a random stepper) </em>
53+
<div id="DetailedDesertIslands"></div>
54+
<em> Click to generate a new map </em>
5355

5456

5557
<p> We will be coding in JavaScript but don't worry about knowing the ins and outs of the language; you should be able to pick it up as we go! </p>
5658

57-
<h2> Introduction to p5.js </h2>
59+
<h2> Using p5.js </h2>
5860

5961
We will be using JavaScript and a library called <strong> p5.js</strong>, made by the lovely <a href="https://processingfoundation.org/"> Processing Foundation</a>.
6062

@@ -99,7 +101,7 @@ <h3> What is a sketch? </h3>
99101

100102
<p><strong>Mini-Task:</strong> Try running the above simulation! Maybe change the variables to display your own favourite colour. </p>
101103

102-
<h3> Creating Random Noise </h3>
104+
<h2> Creating Random Noise </h2>
103105

104106
<div id="RandomHeightMap"></div>
105107
<em>Click to generate new random noise</em>
@@ -114,7 +116,7 @@ <h3> Creating Random Noise </h3>
114116

115117
<p>Let's tackle (1) first!</p>
116118

117-
<h4>Pixels</h4>
119+
<h3>Pixels</h3>
118120

119121
<pre>
120122
<code class="language-js">
@@ -187,7 +189,7 @@ <h4>Pixels</h4>
187189

188190
<p><strong>Mini-Task:</strong> Run the above simulation, and see if you guessed what it does correctly! </p>
189191

190-
<h4>Random Values</h4>
192+
<h3>Random Values</h3>
191193

192194
<p>A key thing to remember about p5.js is that ultimately, it is still JavaScript. Therefore, we can use JavaScript functions and libraries with p5.js. </p>
193195
<br>
@@ -232,7 +234,7 @@ <h3>Task: Random Noise</h3>
232234
</pre>
233235
<em>Generally, taking this out into its own function for this is a good idea; especially since this function's sole purpose is drawing random noise </em>
234236

235-
<h3>Perlin Noise</h3>
237+
<h2>Perlin Noise</h2>
236238

237239
<p>Okay so we've programmed <em>random noise</em> which is "incoherent" noise - you can have sharp constrasts between neighbouring pixels.
238240
If we want nice smooth gradients, then we can use perlin noise! </p>
@@ -254,8 +256,130 @@ <h4>Smoothing it out</h4>
254256
<image src="/SimulationLabs/Images/Noise/FrequentSamplingNoise.PNG"></image>
255257

256258
<p>From the above, we can see that the values sampled are <em>a lot closer</em>; you can think of this as sampling the 10 points at $x=0.2, 0.4, 0.6 \dots$</p>
259+
260+
<p> So the general takeaway is that we can generate smoother noise, by sampling more frequently. How do we do that? We can just divide x,y before feeding it into the noise function!</p>
261+
262+
<div id="SmoothNoiseHeightMap"></div>
263+
264+
<h2>Task: Smooth Noise </h2>
265+
266+
<p>Use the tools above to generate some smooth perlin noise! Have a variable called the smoothing factor, and change its value to see how the noise varies. </p>
267+
268+
<h2>Creating Islands</h2>
269+
270+
<p>We now have the tools to create islands! By designating pixel with noise values past a certain threshold as "land" and the rest as "ocean", we can generate islands.
271+
Since the <strong>noise()</strong> function returns a value between 0 and 1, we should have a threshold value between 0 and 1. </p>
272+
273+
<pre>
274+
<code class="language-js">
275+
276+
const threshold = 0.55;
277+
278+
function getColour(noiseVal){
279+
if(noiseVal > threshold){
280+
return color(239, 221, 111); // sandy yellow (island)
281+
}else{
282+
return color(0, 157, 196); // ocean blue
283+
}
284+
}
285+
</code>
286+
</pre>
287+
288+
<p> Note that the above function returns a "colour" object; if you want to work with large p5.js programs, then you should really be using classes and objects.
289+
Now we can use the returned colour object like so:
290+
</p>
291+
292+
<pre>
293+
<code class="language-js">
294+
let colour = getColour(noise(x/sf, y/sf)); // colour object
295+
stroke(colour); // stroke accepts the colour object directly (instead of number values representing a colour)
296+
</code>
297+
</pre>
257298

299+
<h2> Task: Desert Islands </h2>
300+
301+
<p>Create desert island generation by modifying your smooth noise task, with the above tools. It should look something like below: </p>
302+
303+
<div id="DesertIslands"></div>
258304

305+
<h3>More Detailed Islands </h3>
306+
307+
<p>Sometimes, you want more detailed islands; you want the overall shape of the islands to be the same so you have the same smoothness factor,
308+
but you want there to be some more granular detail. You might want your beaches to have some roughness to its terrain.
309+
310+
After all, terrain isn't perfectly smooth in nature!
311+
</p>
312+
313+
<br>
314+
315+
<p>You can add this granular detail by adding layers of perlin noise together; each of these layers being called "octaves".
316+
Typically, you'll have a large amplitude perlin noise wave which describes the general overall shape,
317+
and then you'll have a smaller amplitude perlin noise waves to describe the finer details.
318+
These smaller waves usually have higher frequency, which will give the islands a distinct roughness.
319+
</p>
320+
321+
<br>
322+
323+
<p> You can increase the number of octaves and how much the smaller waves contribute to the noise by using p5's <strong>noiseDetail()</strong> function.
324+
Read up on the details <a href="https://p5js.org/reference/p5/noiseDetail/">here</a>.</p>
325+
326+
<br>
327+
328+
<p>You can also add more "detail" to the islands by creating multiple threshold levels, like in the example at the top of this webpage! An example of a more complicated threshold colour function: </p>
329+
330+
<pre>
331+
<code class="language-js">
332+
const snowcapThreshold = 0.75;
333+
const mountainThreshold = 0.6;
334+
const grassThreshold = 0.4;
335+
const shallowWaterThreshold = 0.35;
336+
337+
function getColour(noiseVal){
338+
if(noiseVal > snowcapThreshold){
339+
return color(219, 219, 219); // snowcap white
340+
}else if(noiseVal > mountainThreshold){
341+
return color(112, 112, 112); // mountain gray
342+
}else if (noiseVal > grassThreshold){
343+
return color(75, 139, 59); // grass green
344+
}else if (noiseVal > shallowWaterThreshold){
345+
return color(0, 157, 196); // light blue
346+
}else{
347+
return color(0, 147, 186); // slightly darker blue
348+
}
349+
}
350+
</code>
351+
</pre>
352+
353+
<h2> Final Task: Create your own islands! </h2>
354+
355+
<p>You have everything you need to create your own amazing terrain generation!
356+
You can take my generator(s) (displayed at the top of the page) as inspiration, but I'm sure you'll be able to create a better looking generator than mine.
357+
</p>
358+
359+
<br>
360+
361+
<p>You can view the code for my two generators here:</p>
362+
<ul>
363+
<li><a href="https://editor.p5js.org/RexMortem/sketches/sk3iSZ_ly">Desert Island Generator</a></li>
364+
<li><a href="https://editor.p5js.org/RexMortem/sketches/-9_U5dF6S">Mountainous Terrain Generator</a></li>
365+
</ul>
366+
367+
<p><strong>Tip: </strong> Getting good terrain with perlin noise is often a case of just tweaking your parameters (octaves, amplitude/frequency, smooth factor etc) until they're <em>just</em> right. </p>
368+
369+
<h2> Going Further </h2>
370+
371+
<p> If you want to make your generator even cooler, then you can explore more procedural generation techniques. Natural next steps include: </p>
372+
373+
<ul>
374+
<li>Adding rivers (perhaps using perlin worms) </li>
375+
<li>Adding trees e.g. with poisson-disc sampling </li>
376+
<li>Structure generation</li>
377+
</ul>
378+
379+
<ol>
380+
<li>Add fragments/headers </li>
381+
<li></li>
382+
</ol>
259383
</section>
260384
</body>
261385
</html>

Scripts/Noise/DesertIslands.js

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
let desertIslands = new p5((sk) => {
2+
const width = 150;
3+
const height = 150;
4+
5+
const threshold = 0.55;
6+
const sf = 25;
7+
8+
function getColour(noiseVal){
9+
if(noiseVal > threshold){
10+
return sk.color(239, 221, 111); // sandy yellow (island)
11+
}else{
12+
return sk.color(0, 157, 196); // ocean blue
13+
}
14+
}
15+
16+
function drawMap(){
17+
sk.background(255,255,255);
18+
sk.noiseSeed(Math.random()*1000);
19+
20+
for(let x = 0; x < width; x++){
21+
for(let y = 0; y < height; y++){
22+
let c = getColour(sk.noise(x/sf, y/sf));
23+
sk.stroke(c);
24+
sk.point(x,y);
25+
}
26+
}
27+
}
28+
29+
sk.setup = () => {
30+
let cnv = sk.createCanvas(width, height);
31+
cnv.parent("DesertIslands");
32+
33+
sk.describe("Click to generate desert islands!");
34+
35+
drawMap();
36+
}
37+
38+
function onSim(){
39+
return ((sk.mouseX >= 0) && (sk.mouseX <= width) && (sk.mouseY >= 0) && (sk.mouseY <= height));
40+
}
41+
42+
sk.mouseClicked = () => {
43+
if(onSim()){
44+
drawMap();
45+
}
46+
}
47+
});
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
let DetailedDesertMap = new p5((sk) => {
2+
const width = 150;
3+
const height = 150;
4+
5+
const sf = 25;
6+
7+
// I love having hardcoded threshold values
8+
function getColour(noiseVal){
9+
if (noiseVal > 0.7){
10+
return sk.color(209, 191, 81); // darker yellow
11+
} else if (noiseVal > 0.55){
12+
return sk.color(239, 221, 111); // sandy yellow (island)
13+
} else if (noiseVal > 0.4){
14+
return sk.color(0, 157, 196); // ocean blue
15+
} else{
16+
return sk.color(0, 127, 166); // deeper blue
17+
}
18+
}
19+
20+
function drawMap(){
21+
sk.background(255,255,255);
22+
sk.noiseSeed(Math.random()*1000);
23+
24+
for(let x = 0; x < width; x++){
25+
for(let y = 0; y < height; y++){
26+
sk.stroke(getColour(sk.noise(x/sf, y/sf)));
27+
sk.point(x,y);
28+
}
29+
}
30+
}
31+
32+
sk.setup = () => {
33+
let cnv = sk.createCanvas(width, height);
34+
cnv.parent("DetailedDesertIslands");
35+
36+
sk.noiseDetail(8, 0.5);
37+
sk.describe("Click to generate new Perlin Noise threshold map!");
38+
39+
drawMap();
40+
}
41+
42+
function onSim(){
43+
return ((sk.mouseX >= 0) && (sk.mouseX <= width) && (sk.mouseY >= 0) && (sk.mouseY <= height));
44+
}
45+
46+
sk.mouseClicked = () => {
47+
if(onSim()){
48+
drawMap();
49+
}
50+
}
51+
});
52+

Scripts/Noise/SmoothNoiseHeightMap.js

+37
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
let smoothNoiseHM = new p5((sk) => {
2+
const width = 150;
3+
const height = 150;
4+
const sf = 30;
5+
6+
function drawMap(){
7+
sk.background(255,255,255);
8+
sk.noiseSeed(Math.random()*1000);
9+
10+
for(let x = 0; x < width; x++){
11+
for(let y = 0; y < height; y++){
12+
let c = sk.noise(x/sf, y/sf)*255;
13+
sk.stroke(c);
14+
sk.point(x,y);
15+
}
16+
}
17+
}
18+
19+
sk.setup = () => {
20+
let cnv = sk.createCanvas(width, height);
21+
cnv.parent("SmoothNoiseHeightMap");
22+
23+
sk.describe("Click to generate new Perlin Noise heightmap!");
24+
25+
drawMap();
26+
}
27+
28+
function onSim(){
29+
return ((sk.mouseX >= 0) && (sk.mouseX <= width) && (sk.mouseY >= 0) && (sk.mouseY <= height));
30+
}
31+
32+
sk.mouseClicked = () => {
33+
if(onSim()){
34+
drawMap();
35+
}
36+
}
37+
});

0 commit comments

Comments
 (0)