12,746,891 members (30,209 online)
alternative version

#### Stats

72.3K views
53 bookmarked
Posted 15 Mar 2004

# The Art of Noise

, 15 Mar 2004
 Rate this:
An image synthesis algorithm using noise and interpolation.

## Introduction

Do you want to know how the above image was created? Then read on...

First, let's take the simple approach: we assign to each pixel in the 256x256 bitmap, a random value. We only assign a value to the blue component:

```for each row in the bitmap
for each column in the bitmap
N = a random value in the range [0,1]
Color = (Red = 0, Green = 0, Blue = N * 255)
end for
end for```

Here's the result:

As you can see, it's not that bad, but it's not what we want either. In fact, what do we want? We don't want a bitmap with random colors, we want a bitmap with more or less random colors. But what does that mean? It means that if we take, for example, pixel (0,1), the color of pixel (0,2) will be more or less the same color of pixel (0,1). So, if the color of pixel (0,1) is (0,0,255), the color of pixel (0,2) cannot be (0,0,0). No, it should be, let's say, (0,0,250).

Ok, how do we do that? Instead of assigning a random color to each and every pixel in the bitmap, we assign a random color to only some of the pixels in the bitmap. We could, for example, assign a random color to every 10 pixels. So, the pixels (0,0), (0,10), (0,20), ... (10,0), (10,10), (10,20), ... (20,0), (20,10), (20,20), ... will have a random color. All other pixels will be calculated based on the color of the neighboring pixels.

Here's some code:

```...
void Ran()
{
Random r = new Random();
_Random = new Double[_Width * _Height];
for (Int32 i = 0; i < _Width * _Height; i++)
_Random[i] = r.NextDouble();
}

public void Create(Bitmap bmp)
{
_Width = bmp.Width;
_Height = bmp.Height;
Ran();
for (Int32 y = 0; y < bmp.Height; y++) {
for (Int32 x = 0; x < bmp.Width; x++) {
Double N = 0.0;
N += GetColor(x, y, 40);
Byte b = (Byte)(N * 255.0);
Color c = Color.FromArgb(255, 0, 0, b);
bmp.SetPixel(x, y, c);
}
}
}
...```

The method `Create()` will fill a bitmap with the desired pattern. `Create()` first calls `Ran()` which creates an array with random values in the range [0,1]. The array contains as much cells as there are pixels in the bitmap, just for the sake of simplicity. Then, we assign a color to each pixel in the bitmap. The method `GetColor()` will assign a random color to the pixels (0,0), (0,40), (0,80), ..., (40,0), (40,40), (40,80), ... and so on. `GetColor()` returns a number in the range [0,1].

Here's the result:

But what does `GetColor()` do? For example, how do we calculate the color for pixel (0,12)? What do we know? We know the values for pixel (0,0) and pixel (0,40): we can look up their values in the `_Random` array, remember? Here's an illustration to make it clear:

There are several ways to calculate a value for (0,12). We can draw a straight line between the points, or maybe a curved line:

A curved line is more natural, so this is what we're going to use. We have to write a function which takes as input two points `x0` and `x1` with values `y0` and `y1` and a variable `x`. That function will return the value for `x`. Here's the code:

```...
Double f(Double x0, Double y0, Double x1, Double y1, Double x)
{
return (1.0 + Math.Cos(Math.PI +
(Math.PI / (x1-x0)) * (x-x0))) / 2.0 * (y1-y0) + y0;
}
...```

Whoa, I hear you cry! Where does that formula come from? I'll explain. Remember, we want to put a smooth, curved line between two points, right? Take a look at the following picture:

Interesting, isn't? This picture is a plot of the function `cos(x)`. Now, take a closer look at the function in the green rectangle. That's the line we want to put between our points (`x0`,`y0`) and (`x1`,`y1`). How do we put that line there? Well, we know our `x` is in the range [`x0`,`x1`]. We want to take the cosine of `x`. But as you can see in the plot of `cos(x)`, `x` must be in the range [`pi`,`2pi`]:

```x                         -> [x0,x1]
x / (x1 - x0)             -> 1
x / (x1 - x0) * pi        -> pi
(x / (x1 - x0) * pi) + pi -> [pi,2pi]```

We have converted `x` from the range [`x0`,`x1`] to the range [`pi`,`2pi`]. Now we can take the cosine of `x`: `cos(x)`. `cos(x)` will have a value in the range [-1,1]. But we want a value in the range [`y0`,`y1`]. We have to convert our value `y = cos(x)` again:

```y                              -> [-1,1]
y + 1                          -> [0,2]
(y + 1) / 2                    -> [0,1]
(y + 1) / 2 * (y1 - y0)        -> (y1 - y0)
((y + 1) / 2 * (y1 - y0)) + y0 -> [y0, y1]```

If you replace `y` with `cos((x / (x1 - x0) * pi) + pi)`, you can see that we get the formula:

```...
Double f(Double x0, Double y0, Double x1, Double y1, Double x)
{
return (1.0 + Math.Cos(Math.PI +
(Math.PI / (x1-x0)) * (x-x0))) / 2.0 * (y1-y0) + y0;
}
...```

Back to `GetColor()`. Now that we know how to calculate the value of a point between to other points, we can calculate the value of a pixel in a bitmap. I'm not going to show you how to calculate the value for pixel (0,12) because we're working in 2D. To make things more clear, I'm going to show how to calculate the value for pixel (10,12).

The first thing we have to do is to take the value of the neighboring pixels. Well, these pixels are not really neighbors, they are the pixels which were assigned a random value and lay closest to the pixel we want to calculate. So, we look up the value of pixels (0,0), (0,40), (40,0) and (40,40). Then we have to find the value for `x` = 10 in row 0 (we'll name it `xx0`). We also have to find the value for `x` = 10 in row 40 (`xx1`). We can find the value for pixel (10,12) by calculating the value for `y`: `f(0, xx0, 40, xx1, y)`. Here's a picture:

Note that this picture has nothing to do with our data, I only show it to make things clear. So, the first step is to calculate the value for `x` between the pixels (0,0) and (40,0). So, we ignore one dimension: we have a range of values [0,40], we know the `y` value for 0 and 40, so we can find the value for `x` = 10. Think about it. We do the same for `y` = 40: we have a range, [0,40], and we know the values for 0 and 40: not on row 0 but on row 40. Now the final step: again, we have a range, [0,40], and we know the values for 0 and 40, because we just calculated these values in the two previous steps. Think about it and look at the picture above.

Here's the code for `GetColor()`:

```Double GetColor(Int32 x, Int32 y, Int32 M)
{
Int32 x0 = x - (x % M);
Int32 x1 = x0 + M;
Int32 y0 = y - (y % M);
Int32 y1 = y0 + M;

Double x0y0 = Noise(x0, y0);
Double x1y0 = Noise(x1, y0);
Double x0y1 = Noise(x0, y1);
Double x1y1 = Noise(x1, y1);

Double xx0 = Interpolate(x0, x0y0, x1, x1y0, x);
Double xx1 = Interpolate(x0, x0y1, x1, x1y1, x);

Double N = Interpolate(y0, xx0, y1, xx1, y);
return N;
}

Double Noise(Int32 x, Int32 y)
{
if (x < _Width && y < _Height)
return _Random[y * _Width + x];
else
return 0.0;
}```

The method `Noise()` looks up the value for a pixel (`x`,`y`). If the pixel is outside the bitmap, `Noise()` returns 0. Note that it's not necessary to return 0: any number between 0 and 1 is fine. In fact, `Noise()` should be able to return a random number for every (`x`,`y`)! In our program, we don't do that: we use an array with random values.

We're almost there. Take a look at the following pictures:

These pictures are generated using the same algorithm as before, except in the call to `GetColor()`, 40 is replaced with 40/2, 40/4, 40/8, 40/16 and 40/32. Now, we're just adding these pictures and what do we get? Right, the picture at the top of this article! Here's the new code for `Create()`:

```public void Create(ref Bitmap bmp)
{
_Width = bmp.Width;
_Height = bmp.Height;
Ran();
for (Int32 y = 0; y < bmp.Height; y++) {
for (Int32 x = 0; x < bmp.Width; x++) {
Double N = 0.0;
N += GetColor(x, y, 40/32);
N += GetColor(x, y, 40/2);
N += GetColor(x, y, 40/4);
N += GetColor(x, y, 40/8);
N += GetColor(x, y, 40/16);
N += GetColor(x, y, 40/32);
N /= 6.0;
Byte b = (Byte)(N * 255.0);
Color c = Color.FromArgb(255, 0, 0, b);
bmp.SetPixel(x, y, c);
}
}
}```

See? We add the values together and divide the result by 6 to get again a value between 0 and 1.

Well, that's it! You've learned to create art with noise.

A list of licenses authors might use can be found here

## Share

 Belgium
No Biography provided

## You may also be interested in...

 First Prev Next
 My vote of 5 manoj kumar choubey26-Feb-12 22:24 manoj kumar choubey 26-Feb-12 22:24
 art genetic sameabd9-Mar-06 4:33 sameabd 9-Mar-06 4:33
 underscores at start of varnames tedvg11-Jun-04 6:35 tedvg 11-Jun-04 6:35
 Re: underscores at start of varnames Anonymous11-Jun-04 22:24 Anonymous 11-Jun-04 22:24
 Re: underscores at start of varnames tedvg12-Jun-04 0:53 tedvg 12-Jun-04 0:53
 Re: underscores at start of varnames omoshima17-Jun-04 9:56 omoshima 17-Jun-04 9:56
 Re: underscores at start of varnames DaberElay20-Jul-04 7:38 DaberElay 20-Jul-04 7:38
 Re: underscores at start of varnames gnjunge17-Mar-06 7:12 gnjunge 17-Mar-06 7:12
 Re: underscores at start of varnames Natza Mitzi6-Nov-08 10:21 Natza Mitzi 6-Nov-08 10:21
 Reminds me of some pictures I saw once... Marc Clifton21-Mar-04 11:55 Marc Clifton 21-Mar-04 11:55
 Perlin Npise? Joel Holdsworth16-Mar-04 7:29 Joel Holdsworth 16-Mar-04 7:29
 Re: Perlin Npise? Anonymous19-Mar-04 10:56 Anonymous 19-Mar-04 10:56
 Layout PJ Arends16-Mar-04 6:51 PJ Arends 16-Mar-04 6:51
 Re: Layout rfmobile16-Mar-04 7:09 rfmobile 16-Mar-04 7:09
 Re: Layout Anonymous16-Mar-04 7:20 Anonymous 16-Mar-04 7:20
 Last Visit: 31-Dec-99 19:00     Last Update: 19-Feb-17 2:51 Refresh 1