The Advanced Random plugin allows you to generate Perlin noise. Unlike traditional random values it consists of predictable "waves" that can look like hills, clouds or water. These almost natural patterns can be used for creating worlds or textures in games. The classic example of this is of course Minecraft, which uses Perlin noise to generate it's worlds.
Performance
Generating Perlin noise involves a lot of maths, so to make it as fast as possible we have compiled the core of this plugin into WebAssembly. WebAssembly is a new low level language for the browser which under the right circumstances can be much faster than JavaScript. It's well suited for this sort of work, but procedural generation leans towards calling this code a lot so you should always keep this performance limit in mind when designing your game.
Types of noise
Perlin noise is a little different to what you would expect for a random function. It exists as an infinite field of values from which you can request a value at any position. Every time you ask for this position it will always give you the same value. Which is super useful if you need to check a position multiple times, or you want to break up your generation step into chunks.
Advanced Random doesn't include just one type of noise, it actually has 5, each with a 2D and 3D variant. You may wonder why you would need 3D noise in Construct, after all it's a 2D engine! But it can still be useful. You can for instance show a slice of 3D noise, and move slowly through the z dimension. As Perlin noise blends smoothly in all dimensions you will see your world slowly change shape. It's pretty cool to watch!
Let's take a look at some types of noise:
Value noise -
random(1)
Psuedo random unpredictable noise, just the output of a Psuedo Random Number Generator ( PRNG ) algorithm.
Classic noise -
AdvancedRandom.Classic2d(x, y)
Coherent predictable noise, using the 2D improved Perlin algorithm.
Billow noise -
AdvancedRandom.Billow2d(x, y)
A transformation of Perlin noise with a "billowy" shape.
Ridged noise -
AdvancedRandom.Ridged2d(x, y)
A transformation of billow noise with a sharp edge.
Cellular noise -
AdvancedRandom.Cellular2d(x, y)
Also known as Worley noise, closely related to voronoi noise.
Voronoi noise -
AdvancedRandom.Voronoi2d(x, y)
Identical to cellular noise except each cell is assigned a random value.
Fractional Brownian Motion ( FBM / Fractal noise )
Fractal noise isn't a style of noise in itself, it's actually a method of merging multiple layers ( or octaves ) of noise together to give more detail. With Perlin noise it is easy to scale noise to get more variation, but the result is too random losing the larger shapes. So to get around this repeated samples of noise are taken at different scales and merged with varying weights. The weights chosen are the inverse of the scale for that octave.
Octave 1 -
1 * sin(1 * x)
Octave 2 -
0.5 * sin(2 * x)
Octave 3 -
0.25 * sin(4 * x)
Octave 4 -
0.125 * sin(8 * x)
FBM -
(1 * sin(1 * x)) + (0.5 * sin(2 * x)) + (0.25 * sin(4 * x)) + (0.125 * sin(8 * x))
Classic, Billow and Ridged expressions all use fractal noise. By default they only use 1 octave, but you can increase this up to 16 using the Set Octaves action. Each octave is equivalent to taking an additional sample, so using 16 octaves will be 16 times slower than using 1 octave. You will find that the more zoomed out your noise is, the less octaves it needs to appear detailed.
The below is Classic2d noise generated with 4 octaves.
Generating your first noisy image
To get you started, we're going to walk through creating a simple texture using Perlin noise in Construct. We're going to use another plugin called Drawing Canvas, as it allows you to create an image pixel by pixel.
We'll start off by creating a new project and doing some setup.
1. Create a new SD landscape 16:9 project.
2. Select Layout 1 and resize it to 1280 x 720 which is the same size as the viewport.
3. Create a new Drawing Canvas and insert it into Layout 1.
4. Move the Drawing Canvas to 0, 0 and resize it to 1280 x 720 ( same size as the viewport again ).
5. Insert the Advanced Random plugin into your project.
For drawing the image we're going to be using the Drawing Canvas Snapshot feature which allows you to extract the pixel data from your Drawing Canvas. More importantly it allows you to modify this data and put it back into the Drawing Canvas. Let's go to our event sheet.
6. Add a new event - System: on start of layout.
7. Add the DrawingCanvas: Save snapshot action to the event.
Okay so we've asked for a snapshot, but the SaveSnapshot action is asynchronous. This means we have to wait for it to complete before we can continue. Usefully there is an event that tells us when it's ready, so let's add it.
8. Add a new event - DrawingCanvas: on snapshot.
Now we have our snapshot. We're going to go over each pixel and get a noise value for it. To do that we're going to use 2 repeat loops, one for the width and another for the height.
9. Add a new sub event to the on snapshot event.
10. Choose the system: Repeat loop as the condition.
11. Set the count as DrawingCanvas.SnapshotWidth.
... and now the height.
12. Add a new sub event to the Repeat event.
13. Choose the System: Repeat loop as the condition.
14. Set the count as DrawingCanvas.SnapshotHeight.
To make our lives a little easier we're going to use add few variables, which will make our event sheet more readable. We'll start by getting our position using the Loopindex expression.
15. Add 3 new number variables to the on snapshot event "x", "y" and "value".
16. Add the action System: set value action to the first "Repeat" event. Set the "x" variable to Loopindex.
17. Add the action System: set value action to the second "Repeat" event. Set the "y" variable to Loopindex.
After all that set up we can actually get our noise value, we'll put it into the "value" variable.
18. Add the action System: set value action to the second "Repeat" event. Set the "value" variable to AdvancedRandom.Classic2d(x, y).
Yup that little expression is all we need. Now we need to convert that value into a color and put it into our snapshot.
For creating our color we're going to use the rgbEx expression. It takes 3 values in the range 0 to 100, one for each color channel and returns a color value. To keep things simple we're going to create a grayscale image for now, so we'll use the same value for each channel.
19. Add the action DrawingCanvas: Set pixel in snapshot to the second "Repeat" event. Set the X value to "x" the Y value to "y" and the color value to rgbEx(value * 100, value * 100, value * 100).
Okay let's fire up our project and... blank screen. Turns out we need to put the snapshot back into the DrawingCanvas.
20. Add a new blank sub event after the first "Repeat" loop.
21. Add the action DrawingCanvas: load snapshot to the event.
Now if we fire it up again you should have a gray swirly thing, if you don't then double check your events against where we should be by now.
So what's next? Let's see how we can modify our noise.
Scaling
Given the position based nature of Perlin noise it is very easy to "zoom in" to the patterns. Just multiply the x and y co-ordinates by a scaling factor like so:
AdvancedRandom.Classic2d(x * scale, y * scale)
A scale less than 1 will "zoom in" and greater than 1 will "zoom out".
Threshold
If you don't want a smooth gradient then you can perform a simple threshold effect on the value using the conditional operator:
AdvancedRandom.Classic2d(x, y) > 0.5 ? 1 : 0
Domain Warping
This is a more advanced technique, but with some adjustment you can come up with some very decorative and cool effects. The trick here is to generate noise, modify the output some way then pass it back into the noise expression. Here's an example that looks a bit like rumpled fabric:
AdvancedRandom.Classic2d(y * AdvancedRandom.Classic2d(x * scale, y * scale), x * AdvancedRandom.Classic2d(y * scale, x * scale))
... and here it is again, but with the octave count increased to 16. It now looks like eroded stone!
Now is a good time to have a mess around, try modifying the input values for each noise functions; swapping the x and y around, adding an arbitrary value or multiplying it by some number. You might also want to try mixing different noise expressions here. All the 2D expressions take the same number of parameters, so you can swap them out really easily. Or you could even try doing a 3 stage transform!
Color Gradients
So you might have noticed so far that all our images have been in black and white, which is a little dull. So let's add some color! The plugin also includes a feature called gradients. These allow you fade across multiple colors or values with ease by setting "stops" for each value.
Linear gradient
rgbEx(90, 39, 40) - rgbEx(57, 60, 90)
Linear gradient
rgbEx(25, 53, 65) - rgbEx(92, 97, 88) - rgbEx(96, 62, 24)
To use a gradient you must first create one using the AdvancedRandom: Create Gradient action. Choose a name for your gradient and leave the type as "color". The name is for switching gradients, we're only going to use one gradient for now, so it doesn't matter.
Let's add some stops to the gradient using the AdvancedRandom: add gradient stop action. One at the position 0 with the value rgbEx(32, 65, 82) and a second at the position 1 with the value rgbEx(100, 100, 100).
Now we can easily sample a color from the gradient using the "gradient" expression. In our DrawingCanvas: Set snapshot pixel action we can replace the expression rgbEx(value x 100, value x 100, value x 100) with AdvancedRandom.Gradient(value).
Your initial render might look a little dull here... So let's try tweaking it a little. Try something like this:
We're still using basic noise. But with a few modifications:
1. Increased the octaves to 8 for more detail.
2. Zoomed in by scaling the position by 0.15.
3. Multiplied the noise value by 1.6 to stretch it a little.
4. Cubed the value; this redistributes the noise so that highs and lows are more common.
The final step might be a little confusing, but it's a useful technique. We should now have some pretty cool clouds:
Downloading the result
The Drawing Canvas plugin has a cool feature that has been used quite a lot making this post. You can save the current canvas, and then download it using the browser plugin.
This is super handy if you want to use your noise texture in your game, but don't want to pay the performance cost of creating it in real time!
Additional reading:
This online book is about creating generative art using a fragment shader, as we're creating an image a pixel at a time it has quite a few parallels with what we're doing. It has some quite excellent interactive examples as well, they are all written in GLSL but they are still quite pretty even if you don't understand how they work.
https://thebookofshaders.com
A short article about domain warping by Inigo Quilez, who is a bit of a wizard when it comes to procedural generation. He has a huge amount of articles on graphics effects on his website.
http://www.iquilezles.org/www/articles/warp/warp.htm