16
\$\begingroup\$

Hey guys, I'm trying to figure out how to generate tileable fractals in code (for game maps, but that's irrelevant) I've been trying to modify the Solid Noise plug-in shipped with GIMP (with my extremely limited understanding of how the code works) but I cant get mine to work correctly.

My modified code so far (Java)

GIMP's solid noise module that I'm basing my code off of (C)

Here is what I'm trying to achieve but This is what I'm getting

So if anybody can see what I've done wrong, or has a suggestion how I could go about doing it differently, I'd greatly appreciate it. Thanks in advance. And if I'm asking way to much or if just a huge failure at life, I apologize.

\$\endgroup\$
1

8 Answers 8

4
\$\begingroup\$

I don't follow your code exactly, but here is a simplified description of the algorithm that will roughly achieve the effect (based on the image you posted).

The following explanation is not the super optimised version, but is one that's conceptually clear (I hope). Once you have got it running, you can optimise it (quite drastically, in fact).

  1. Generate n layers of uniform random noise (just random greyscale pixels).
  2. Now sample each of these by sampling every, 1, 2, 4, 8, ... 2^(n-1) pixels, and interpolating the intermediate pixels. Each layer is smoother than the previous one.
  3. Now scale of these with a factor of 1, 2, 4, 8, etc. Each layer is darker than the previous one.
  4. Add all these together.
  5. Normalise by dividing each pixel with (1 + 2 + 4 + 8 + ... 2^(n-1)).

The difficult step is the sampling and interpolation step. Suppose we are in the layer skipping sampling every m-th pixel. Here is the basic idea for m > 1 (if m is 1, we use the image as is):

for each pixel x and y
   left_sample_coord = m *(x / m) //(integer division, automatically truncated)
   right_sample_coord = (left_sample_point + m) % image_width
   top_sample_point = m*(y / m) 
   bottom_sample_coord = (top_sample_point + m) % image_height

   horizontal_weight = (x - left_sample_point) / (m - 1)
   vertical_weight = (y - top_sample_point) / (m - 1)

   sample_top_left = image(left_sample_coord, top_sample_point)
   //and the same for the other four corners

   //now combine the top two points
   top_interpolate = sample_top_left * horizontal_weight + sample_top_right * (1-horizontal_weight)

  //now combine the bottom two points
  bottom_interpolate = sample_bottom_left * horizontal_weight + sample_bottom_right * (1-horizontal_weight)

  //and combine these two last obtained values

  smooth_noise(x, y) = top_interpolate * vertical_weight  + bottom_interpolate * (1 - vertical_weight)

A few tips:

  • The result of the algorithm above might seem a bit washed out. You can reduce this effect by using the same layer of noise for all layers, or be contrast enhancing the image afterwards.
  • The algorithm above uses linear interpolation, but cosine interpolation (do a search) gives much better results.
  • Make it possible to look at your layers separately during all parts of the algorithm. This will help you flush out bugs quickly.
\$\endgroup\$
3
\$\begingroup\$

It seems as though my original pastebin expired somehow, so I have no way to compare my nonworking code, but on my current game I went through and translated the GIMP code into java again and it seems to work fine now.

If anyone plans on using this code, I'd recommend modifying the constructor to modify the setting parameters (detail and size) so you can make it work how you'd like. EDIT: I realized my original question was about making it tileable, so remember to set tilable to true!

Code: http://pastebin.com/KsfZ99Xa

Example: perlin generator example

\$\endgroup\$
2
\$\begingroup\$

Just skimmed through the Java and C versions and noticed a slight difference in the way you use the noise function. Here's yours:

int val = Math.abs((int) Math.floor(255.0 * noise(((double)col/(double)width), ((double)row/(double)height))));

C code:

val = (guchar) floor (255.0 * noise ((col - xoffset) / width,

                                           (row - yoffset) / height));

Why have you chosen not to subtract the offset? (col - xoffset) and (row - yoffset)

Just wondering. I don't have time to do a full analysis of the code.

Hope this helps.

\$\endgroup\$
1
  • \$\begingroup\$ xoffset and yoffset are to calculate in the way GIMP divides images into grids, but my program all works on one tile so x and y offset would be 0 \$\endgroup\$
    – Nick Badal
    Commented Nov 7, 2010 at 0:08
2
\$\begingroup\$

I don't know if it could help but the following trick worked a treat for me :

http://www.gamedev.net/blog/33/entry-2138456-seamless-noise/

This fellow is using a 4d noise function and simply provide xy values of two circles as xyzw values for the 4d noise. The result is looping perfect.

Here is the idea(simplified) for a 1000*1000 picture :

for(i = 0;i < 1000; i++){

  for(j = 0; j < 1000; j++){

    nx = cos((i/1000) * 2 * PI);
    ny = cos((j/1000) * 2 * PI);
    nz = sin((i/1000) * 2 * PI);
    nw = sin((j/1000) * 2 * PI);

    looped_noise = noise_4D( nx, ny, nz, nw, octaves, persistence, lacunarity, blah...);
    noise_buffer[(i*1000)+j] = looped_noise;

  }
}
\$\endgroup\$
1
\$\begingroup\$

I recommend you use the diamond-square algorithm, also known as plasma fractal or random mid-point displacement fractal. Using this algorithm it is very easy to constrain the edges to have the same values. When you generate a value for an edge, copy it to the corresponding edge on the other side. This yields a perfectly tiling map.

\$\endgroup\$
0
\$\begingroup\$

There's a great article about noise generation here. It's not Perlin noise as the article claims (it's pink noise actually), but it is still incredibly useful to understand how noise images are generated.

So, to actually answer your question: to make a tileable noise image, you just need to keep "tileability" throughout the generation. That is, when noise is smoothed, you smooth it as if it is repeated infinitely in all directions - tiled.

\$\endgroup\$
0
\$\begingroup\$

From the screenshot, my guess is that only the "big" layers are being generated, which is why the noise appears too regular and lacks detail.

This may sound stupid, but did you try increasing the "detail" variable (line 16)? In your code, it's set to 1, which means the algorithm will only generate two layers of detail before stopping. Try increasing it to something like 8.

\$\endgroup\$
0
\$\begingroup\$

You should also take a look at Simplex Noise, which was developed by Ken Perlin to fix some of Perlin noise's shortcomings.

I've been working on an implementation of Simplex Noise for a C++ game. It supports multi-octave 1D/2D/3D/4D noise. Right now, the only built-in texture is marble, but you could easily add more:

(http://code.google.com/p/battlestar-tux/source/browse/trunk/src/lib/procedural/)

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .