Over the weekend I celebrated my "golden" birthday, I turned 23 on the 23rd of September. I got a lot of cool gifts: Stephanie bought me Mario Kart DS, Gross Point Blank, and Metal: A Headbangers Journey. Kelly and Laurence bought me some new clothes to wear to my new job. Shalese got me some much needed cookie sheets (and cookies!). And my parents gave me money which I used to get an umbrella and my sixth tattoo (pictures coming soon). My mom cooked dinner for all of us and afterwards Steph and I went out to see Jet Li's Fearless. I didn't realize it was going to be subtitled, but I don't mind that, and it turned out to be a really good movie. So that was my birthday weekend, I'll post more about my new job and tattoo in the coming days. 'Til then, Rock On!
August 2007
The date for the wedding has been set (almost): the 4th of August 2007. So go ahead and mark your calendars! The reception will be held at Grazie! Banquets in Des Plaines, IL (1050 East Oakton Street) - the location of the ceremony has yet to be decided. More information on the wedding to follow when I get it.
Two Weeks Notice
September 15th, 2006 marks my last day as an employee of SPSS, Inc. ending my two and a half years of employment at the company. I have gone from an Intern Web Developer to an Associate Quality Assurance Engineer to finally an (unofficial) Associate Software Engineer. I want to thank the variety of people I have worked with at SPSS, Inc. from the members of the Web Team in ITS to the folks in QA and the Text Mining Development group for all of the opportunities they have given me (and the marketing and events departments for given me so much web work to do ;-).
25th Anniversary
My parents celebrated their 25th wedding anniversary this past Saturday (their actual anniversary is today) and are now soaking up the sun about an hour south of Cancun in Riviera Maya, Mexico. My parents renewed their vows at Saint Hubert Church after the 4:30pm mass (at which my sister and I were late for at no fault of our own) followed by food and drink at their house until the wee hours of the morning. I got up bright and early on Labor day (okay, not so bright... it was 3:00am) to take them to O'Hare International Airport so they could enjoy a real honeymoon in Mexico. Congratulations!
Updated: September 11th, 2006
During their week in Mexico, my parents stayed at the Occidental Grand Flamenco Xcaret which is adjacent to the Xcaret Eco Archaeological Theme Park. This was their first trip outside the United States since they have been married and they enjoyed it so much that they invested in a bi-annual timeshare option and plan to get passports. Next time, remember to bring a cell phone! They had to take a taxi home from O'hare because after about two hours of looking, my parents and my sister could not find each other at the airport.
Paint.NET - Perlin Noise for 2-Dimensional Clouds
Upon browsing the Paint.NET Forums, I ran across someone looking to have a Photoshop tutorial converted to a Paint.NET tutorial. The first thing this tutorial showed was to use Filter -> Render -> Clouds. Currently, there is no “Cloud” effect within Paint.NET, so I set out to figure out how to mathematically generate clouds. After searching the Google Groups, I ran across something called “Perlin Noise,” created by Ken Perlin to generate textures for Tron. More searching led me to a few web sites explaining Perlin Noise and its features. From there I converted a few of the functions to work within the Paint.NET Code Lab, and I present that to you today.
There are five functions in the Paint.NET Code Lab implementation of Perlin Noise: Render, PerlinNoise2d, Smooth, Noise, and Interpolate.
The Render function looks as follows:
void Render(Surface dst, Surface src, Rectangle rect) { for(int y = rect.Top; y < rect.Bottom; y++) { for (int x = rect.Left; x < rect.Right; x++) { ColorBgra srcBgra = src[x, y]; byte c = (byte)(PerlinNoise2d(x, y) * 255); dst[x, y] = ColorBgra.FromBgra(srcBgra.B, srcBgra.G, srcBgra.R, c); } } }
This is the entry point, from the Code Lab, to the drawing surface in Paint.NET. The Render function takes three parameters, the destination Surface, the source Surface, and the drawing area (or invalid) Rectangle. The function does not return a parameter.
What this function does is loop through each pixel in the drawing area, retrieves the source pixel information (Red, Green, Blue, Alpha), sets a byte (0 – 255) based on the result of the PerlinNoise2d function on the given pixel location, and sets the destination pixel by adjusting the alpha level for a “cloud”-like effect while keeping the source color.
This function can be adjusted depending on what you would like to do. For example, to only draw clouds in a transparent space, you can add an if-statement to check the transparency on the source pixel before setting the destination pixel. Then, line 9 from above (the last line in the for-loop) would become:
dst[x, y] = (0 == srcBgra.A) ? ColorBgra.FromBgra(srcBgra.B, srcBgra.G, srcBgra.R, c) : src[x, y];
Next, we look inside the PerlinNoise2d function. This is where all the magic happens:
double PerlinNoise2d(int x, int y) { double total = 0.0; double frequency = .015; // USER ADJUSTABLE double persistence = .65; // USER ADJUSTABLE double octaves = 8; // USER ADJUSTABLE double amplitude = 1; // USER ADJUSTABLE for(int lcv = 0; lcv < octaves; lcv++) { total = total + Smooth(x * frequency, y * frequency) * amplitude; frequency = frequency * 2; amplitude = amplitude * persistence; } double cloudCoverage = 0; // USER ADJUSTABLE double cloudDensity = 1; // USER ADJUSTABLE total = (total + cloudCoverage) * cloudDensity; if(total < 0) total = 0.0; if(total > 1) total = 1.0; return total; }
As you can see, there is a lot going on within this function. Simply put, PerlinNoise2d takes in an (x, y) coordinate and returns a double value between 0 and 1, which gets expanded in our Render function to the range of 0 – 255. But, there are a lot of user adjustable variables which can vary the output.
Frequency gives you number of noise values defined between each 2-dimensional point.
Persistence is a constant multiplier adjusting our amplitude in each iteration.
Octaves define the number of iterations over the noise function for a particular point. With each iteration, the frequency is doubled and the amplitude is multiplied by the persistence.
Amplitude is the maximum value added to the total noise value.
Those variables act within the for-loop to generate a cumulative total. There are two other user adjustable variables that act upon the total after the for-loop that help in defining the finished look of your scene.
Cloud Coverage is a constant that gets added (or subtracted) to each total. The default value is zero, meaning that the total should not be altered. Adding values will increase the size of the clouds, while subtracting will reduce them.
Cloud Density is a constant that gets multiplied with the total to increase or decrease the apparent thickness of the clouds. The default value is 1, meaning the totalshould not be altered. Any number between 0 and 1 will reduce the apparent density (making the clouds appear more like fog), while any number greater than 1 will increase the density, and -1 will invert the clouds.
The actual call to the Noise function is buried within the Smooth function called within the for-loop. Since we have double values for our x and y coordinates, our Smoothfunction interpolates the noise value by using the four corners as known data points.
double Smooth(double x, double y) { double n1 = Noise((int)x, (int)y); double n2 = Noise((int)x + 1, (int)y); double n3 = Noise((int)x, (int)y + 1); double n4 = Noise((int)x + 1, (int)y + 1); double i1 = Interpolate(n1, n2, x - (int)x); double i2 = Interpolate(n3, n4, x - (int)x); return Interpolate(i1, i2, y - (int)y); }
Say we are given an (x, y) coordinate as shown in the graph. Noise values are only known for integer locations per our Noisefunction below. So, the Smooth function needs to approximate a value for the coordinate we were given based on the known values of the corner points (labeled v1, v2, v3, and v4 in the graph). Once we get our noise values for the corners, we have to interpolate the values to approximate (x, y). In this case, interpolating the values in the x-direction (done by passing the fractional value of x as the third parameter to our Interpolate function), we receive a value that is closer to the right side of the graph. And interpolating in the y-direction (done by passing the fractional value of y as the third parameter to ourInterpolate function), we receive a value that is closer to the top of the graph. After interpolating in the x- and y-directions, we have a noise value for (x, y) that we can return.
The Noise function acts as a semi-random number generator. Instead of returning a random number each time it is called, it returns a random number based on the input parameters. If you pass the same parameters, you get the same result. TheNoise function is as follows:
static Random r = new Random(); int r1 = r.Next(1000, 10000); int r2 = r.Next(100000, 1000000); int r3 = r.Next(1000000000, 2000000000); double Noise(int x, int y) { int n = x + y * 57; n = (n<<13) ^ n; return ( 1.0 - ( (n * (n * n * r1 + r2) + r3) & 0x7fffffff) / 1073741824.0); }
The Noise function above will return a value between -1 and 1 for each (x, y) coordinate. Since this is only a semi-random number generator, having set values for r1, r2, and r3 (original values: 15731, 789221, 1376312589) will generate the same design each time. I have modified the function to set random values for r1, r2, and r3 so that we can generate a new cloud pattern each time the code is "built" in the Code Lab. The most important part of the Noise function is the return statement. By performing a bitwise AND operation with the Hexadecimal value 0×7FFFFFFF, the largest value possible is 0×80000000 (or 2147483648). Dividing by 1073741824.0 gives us a maximum value of 2.0 and a minimum value of 0.0, subtracting from 1.0, we return a value between -1 and 1.
In the Interpolate function, we are using a standard cosine interpolation formula:
double Interpolate(double x, double y, double a) { double val = (1 - Math.Cos(a * Math.PI)) * .5; return x * (1 - val) + y * val; }
That completes the walkthrough of my Perlin Noise function for generating 2-dimensional clouds. Enjoy!
Examples
Download the C# Source Code Segment for Paint.NET’s Code Lab
Perlin Noise Sources
- Hugo Elias – Perlin Noise
- James Long – Perlin Noise Tutorial
- Ken Perlin – Making Noise
- Wikipedia – Perlin Noise