Skip to content

Commit c30845c

Browse files
committed
Noise lab progress
1 parent 8118449 commit c30845c

10 files changed

+379
-10
lines changed
File renamed without changes.
File renamed without changes.

Boids.html

+42-6
Original file line numberDiff line numberDiff line change
@@ -455,25 +455,61 @@ <h3> Accelerating Ball </h3>
455455
<p> So to simulate our ball's motion, we would: </p>
456456

457457
<ol>
458-
<li> Update the velocity with acceleration </li>
459-
<li> Update the position with velocity </li>
458+
<li> Update the velocity according to acceleration </li>
459+
<li> Update the position according to velocity </li>
460460
</ol>
461461

462462
<p> As we'll soon see, we have to do this in a <em>very careful</em> way. </p>
463463

464464
<h4>Enter deltaTime</h4>
465465

466466
<p>Until now, we've not really considered how long each frame takes.
467-
It'd be reasonable to assume each frame takes about 1/60 seconds to process <em>however</em> each frame takes a slightly different amount of time and, when simulations get more complex, each frame might take a non-slight different amount of time!
467+
It'd be reasonable to assume each frame takes about 1/60 seconds to process <em>however</em> each frame <strong>actually</strong> takes a slightly different amount of time. When simulations get more complex, this difference in time may increase!
468468
</p>
469469

470-
<p>To test this theory, console.log the <em>deltaTime</em> variable inside the <em>draw()</em> function; it will set itself to the time taken to process the previous frame! </p>
470+
<p>To test this theory, console.log the <em>deltaTime</em> variable inside the <em>draw()</em> function in a blank sketch; deltaTime will be set to the time taken to process the previous frame! You should see differences in the values logged despite not much going on.</p>
471+
472+
<h4>Why should we care?</h4>
473+
474+
<p>Imagine two different devices: device A and device B. A runs slowly and can only handle the simulation running at 20 FPS, and B runs quickly and can handle the simulation running at its intended 60 FPS.
475+
476+
If we <em>don't</em> use deltaTime, then B will run the simulation 3 times as quickly as A! If this is a game, then A will be running the game at a third of the speed
477+
it's meant to be played at. Imagine playing your favourite platformer where everything is 3 times slower!
478+
</p>
479+
480+
<p>Of course, this doesn't matter massively for simple simulations </p>
471481

472-
<h4>Using deltaTime</h4>
482+
<h4>Using deltaTime (Incorrectly)</h4>
473483

474-
<p> We know $v = p/t$ (where ) </p>
484+
<p> We know acceleration is the rate of change of velocity, and velocity is the rate of change of position so it'd be reasonable to write our motion simulation as: </p>
475485

486+
<pre>
487+
<code class="language-js">
488+
simulate(){
489+
velocity = velocity + (deltaTime * acceleration);
490+
position = position + (deltaTime * velocity);
491+
}
492+
</code>
493+
</pre>
494+
495+
<p>Or in p5.js code:</p>
496+
497+
<pre>
498+
<code class="language-js">
499+
simulate(){
500+
this.velocity.add(p5.Vector.mult(this.acceleration, deltaTime));
501+
this.position.add(p5.Vector.mult(this.velocity, deltaTime));
502+
}
503+
</code>
504+
</pre>
505+
<em>Again, if you're confused by p5.js code, look at the <a href="https://p5js.org/reference/#vector">reference</a></em>
506+
507+
<p>This looks and feels correct, but there's a slight problem! Here is a simulation using this method, with </p>
476508
<div id="WrongFPSBallsContainer"></div>
509+
<em></em>
510+
<h4>Problem </h4>
511+
512+
<h4>Using deltaTime (Correctly)</h4>
477513

478514
<div id="CorrectFPSBallsContainer"></div>
479515

19.7 KB
Loading
21.2 KB
Loading

Noise.html

+261
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,261 @@
1+
---
2+
---
3+
4+
<!DOCTYPE html>
5+
6+
<html lang="en">
7+
<head>
8+
<meta charset="UTF-8">
9+
<title> Noise </title>
10+
11+
<!-- CDNs -->
12+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
13+
<script src="https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.11.2/p5.min.js" integrity="sha512-1YMgn4j8cIL91s14ByDGmHtBU6+F8bWOMcF47S0cRO3QNm8SKPNexy4s3OCim9fABUtO++nJMtcpWbINWjMSzQ==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
14+
15+
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/themes/prism.min.css" rel="stylesheet">
16+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.23.0/prism.min.js"></script>
17+
18+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
19+
<link href="/SimulationLabs/Stylesheets/Default.css" rel="stylesheet">
20+
21+
<script type="text/x-mathjax-config">
22+
MathJax.Hub.Config({
23+
tex2jax: {
24+
inlineMath: [ ['$','$'], ["\\(","\\)"] ],
25+
processEscapes: true
26+
}
27+
});
28+
</script>
29+
30+
<script src="https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-AMS-MML_HTMLorMML" type="text/javascript"></script>
31+
32+
<!-- Simulations -->
33+
<script src="/SimulationLabs/Scripts/Noise/RandomHeightMap.js" defer></script>
34+
<script src="/SimulationLabs/Scripts/Noise/NoiseHeightMap.js" defer></script>
35+
</head>
36+
37+
<body>
38+
{% include HeaderBar.html %}
39+
40+
<!-- Page content -->
41+
<div class="section">
42+
<p> This is the noise page </p>
43+
44+
<h2> What you'll create </h2>
45+
46+
<p> The aim of this workshop is to recreate boids, which are a simulation of birds and other flocking entities (like fishes).
47+
Hopefully, you'll be able to apply this in your own projects!
48+
Along the way, we'll learn a little bit about p5.js, rendering, and simulating physics. </p>
49+
50+
<div id="BoidContainer"></div>
51+
52+
<em> (This is not an actual boids sketch btw - this is a random stepper) </em>
53+
54+
55+
<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>
56+
57+
<h2> Introduction to p5.js </h2>
58+
59+
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>.
60+
61+
<ol>
62+
<li> <p>To get started, create an account for the <a href="https://editor.p5js.org/signup">web editor</a>. </p> </li>
63+
<li> <p> Open a new sketch (File->New) </p> </li>
64+
</ol>
65+
66+
<h3> What is a sketch? </h3>
67+
68+
<p>A sketch is a small program that will work with p5.js to produce a simulation.
69+
Just like a game, your simulation will have frames (an image of your simulation) and p5.js will aim to show 60 frames a second.
70+
You'll be able to code what happens on certain events e.g. just before a frame is shown, just before the simulation starts, when the mouse is clicked.
71+
72+
In this lab, since we're just drawing an image, we'll only concern ourselves with drawing before the simulation starts and when the mouse is clicked.
73+
</p>
74+
75+
<br>
76+
77+
<p> Your new sketch should have 2 functions: </p>
78+
79+
<ul>
80+
<li> <strong> setup() </strong> which is a function executed once at the start </li>
81+
<li> <strong> draw() </strong> which is a function executed every frame </li>
82+
</ul>
83+
84+
We're going to <strong>ignore</strong> the <strong>draw()</strong> function, since we'll just be using the setup function. In other words, in this lab, just delete the <strong>draw()</strong> function.
85+
86+
We create the canvas (arguments being the width and length) in <strong> setup() </strong>; this is something you only do once pretty much regardless of the simulation.
87+
<pre>
88+
<code class="language-js">
89+
function setup() {
90+
createCanvas(400, 400);
91+
let redV = 128; // if you're new to js, use "let" to declare variables
92+
let greenV = 12;
93+
let blueV = 128;
94+
background(redV, greenV, blueV); // my favourite colour; purple!
95+
}
96+
</code>
97+
</pre>
98+
<em> RGB values have R,G,B values ranging from 0-255 </em>
99+
100+
<p><strong>Mini-Task:</strong> Try running the above simulation! Maybe change the variables to display your own favourite colour. </p>
101+
102+
<h3> Creating Random Noise </h3>
103+
104+
<div id="RandomHeightMap"></div>
105+
<em>Click to generate new random noise</em>
106+
107+
<p>Let's dive straight into generating random noise! We'll need to know a couple things: </p>
108+
109+
<ol>
110+
<li>Drawing a pixel to the screen, with a specific colour</li>
111+
<li>Generating random values</li>
112+
<li>Turning a random value into a colour</li>
113+
</ol>
114+
115+
<p>Let's tackle (1) first!</p>
116+
117+
<h4>Pixels</h4>
118+
119+
<pre>
120+
<code class="language-js">
121+
function setup() {
122+
createCanvas(400, 400);
123+
124+
// the eyes
125+
point(25, 25);
126+
point(28, 25);
127+
128+
// the smile
129+
point(25, 30);
130+
point(26, 30);
131+
point(27, 30);
132+
point(28, 30);
133+
134+
point(24, 29);
135+
point(23, 28);
136+
137+
point(29, 29);
138+
point(30, 28);
139+
}
140+
</code>
141+
</pre>
142+
143+
<p>We can use the <strong>point()</strong> function to draw a pixel at specified <strong>X,Y</strong> coordinates. What about the colour? </p>
144+
145+
<pre>
146+
<code class="language-js">
147+
function setup() {
148+
createCanvas(400, 400);
149+
150+
// eye colour
151+
stroke(0, 0, 255);
152+
153+
// the eyes
154+
...
155+
156+
// smile colour
157+
stroke(255, 0, 0);
158+
159+
// the smile
160+
...
161+
}
162+
</code>
163+
</pre>
164+
165+
<p>We can use the <strong>stroke()</strong> function to determine the colour of the pixels drawn from that point on; we can supply it with 3 values to specify an RGB colour.
166+
If we're only using grayscale colours, then we can provide just a single value (to represent the brightness or "value" of the colour) e.g. 255 for pure white, 0 for pure black.
167+
</p>
168+
169+
<br>
170+
171+
<p>Using this knowledge, can you guess what the following code does?</p>
172+
173+
<pre>
174+
<code class="language-js">
175+
function setup() {
176+
createCanvas(400, 400);
177+
178+
for(let y = 0; y &lt; 400; y++){
179+
for(let i = 0; i &lt;= 255; i++){
180+
stroke(i);
181+
point(i,y);
182+
}
183+
}
184+
}
185+
</code>
186+
</pre>
187+
188+
<p><strong>Mini-Task:</strong> Run the above simulation, and see if you guessed what it does correctly! </p>
189+
190+
<h4>Random Values</h4>
191+
192+
<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>
193+
<br>
194+
<p>To handle randomness, we'll use js' Math library; in particular, the function <strong>Math.random()</strong>! Let's use the JavaScript console to test it out. </p>
195+
196+
<pre>
197+
<code class="language-js">
198+
for(let i = 0; i &lt; 20; i++){
199+
console.log(Math.random()); // values between 0-1
200+
}
201+
</code>
202+
</pre>
203+
204+
<p> Okay cool, there's only a slight problem now; we get random values in the range 0-1, but the colour values are in the range 0-255. All we need to do is multiply up :) </p>
205+
206+
<h4>Use the docs! </h4>
207+
208+
<p>If you ever have questions about p5 functions, then please refer to the excellent <a href="https://p5js.org/reference/">documentation</a>! </p>
209+
210+
<h3>Task: Random Noise</h3>
211+
212+
<p>It's now your job to replicate the random noise sketch! Use the tools above and the template below to create some random noise. </p>
213+
214+
<pre>
215+
<code class="language-js">
216+
217+
const width = 400; // these are "constant" variables, as they don't change
218+
const height = 400; // made to be variables since they're used in 2 places (iterating and createCanvas)
219+
220+
function drawRandomNoise(){
221+
for(let x = 0; x &lt; width; x++){
222+
for(let y = 0; y &lt; height; y++){
223+
// FILL ME
224+
}
225+
}
226+
}
227+
function setup() {
228+
createCanvas(width, height);
229+
drawRandomNoise();
230+
}
231+
</code>
232+
</pre>
233+
<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>
234+
235+
<h3>Perlin Noise</h3>
236+
237+
<p>Okay so we've programmed <em>random noise</em> which is "incoherent" noise - you can have sharp constrasts between neighbouring pixels.
238+
If we want nice smooth gradients, then we can use perlin noise! </p>
239+
240+
<div id="NoiseHeightMap"></div>
241+
242+
<p>We can definitely see that the noise is more ordered than random noise, however we want it to be less sharp; we want it to be smoother. </p>
243+
244+
<h4>Smoothing it out</h4>
245+
246+
<p>So how do we achieve a smoother noise? We can figure this out by looking at 1D noise, and think about sampling 10 points from perlin noise. </p>
247+
248+
<image src="/SimulationLabs/Images/Noise/InfrequentSamplingNoise.PNG"></image>
249+
250+
<p>From the above, we can see that the values sampled can be quite different. You can think of this as sampling the 10 points at $x=1, 2, 3, \dots$ </p>
251+
252+
<p>What if we sampled points more frequently instead?</p>
253+
254+
<image src="/SimulationLabs/Images/Noise/FrequentSamplingNoise.PNG"></image>
255+
256+
<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>
257+
258+
259+
</section>
260+
</body>
261+
</html>

Scripts/Noise/NoiseHeightMap.js

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

0 commit comments

Comments
 (0)