Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (578)
games submitted by our members
Games in WIP (499)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  Terrain heightmap generation, can this method be easily replicated?  (Read 1025 times)
0 Members and 1 Guest are viewing this topic.
Offline pixelprime

Junior Member


Medals: 3



« Posted 2013-02-21 16:19:00 »

Hey all,

I've been playing around with terrain heightmaps recently, and I can get reasonably decent results by first populating a 2D array with random values, then averaging them out using nearest-neighbour interpolation.

However, it all ends up being a bit too homogenous and 'samey'.

In contrast, when I load in a simple greyscale image from Photoshop (using Render -> Clouds, then Render -> Difference clouds), I get beautiful landscapes, from just this simple image:



Produces:


This is just a simple 2D array, with a constrained minimum and maximum elevation clamp (to which the height values are offset against).

I can't seem to work out which is the most suitable algorithm to produce nicely blended noise as shown in the greyscale image above.

I'd appreciate any insights any of you might have! Thanks!

Offline VeaR

Junior Member





« Reply #1 - Posted 2013-02-21 16:48:53 »

Its not hard to adapt a noise library yourself. There are couple of ports/rewrites of it under different names: libnoise, noisepp, libnoiseforjava. I've taken these, and made my own small library for myself. Then i can generate terrains by combining Perlin noise functions. Then i do a bit of erosion simulation on top of that, and calculate the texturing based on height, slope and erosion amount.

tl;dr: Te key is: Perlin noise
Offline pixelprime

Junior Member


Medals: 3



« Reply #2 - Posted 2013-02-21 16:54:24 »

Thanks! I found some additional resources on alternative methods, such as Diamond-Square and Brownian surfaces, but Perlin noise did come up.

A quick question, though: Generating a perlin noise map seems simple enough, but how do you treat repeated applications of the noise function (to improve terrain diversity)?

Would you apply the noise in an additive fashion, or with a difference method? I'm guessing a difference method would work best, so doing something like this:

1  
newHeight[x][z] = (float)Math.abs(noise1[x][z] - noise2[x][z]);


Still treading early water on this topic, but I appreciate your suggestions - thanks!
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline VeaR

Junior Member





« Reply #3 - Posted 2013-02-21 17:14:57 »

There are lots of ways to combine the noise, even different types. The libraries i mentioned let you combine noise functions like a pipeline. But the basic way to produce a Perlin noise heightmap is to combine multiple octaves of it. The first octave determines the biggest terrain features, like hills. The 8'th (or so) octave has much higher frequency but lower amplitude, and determines much finer details. You use multiplication to get the octaves.

 noise(x,y)/2 : half the amplitude,
 noise(x*2, y*2) : double the frequency

Then you sum up the octaves. But the libraries i mentioned already generate you Perlin noise with multiple octaves, its a parameter they take. The noise is not repeating, you control where you sample the noise with the coordinates. If you specifically want repeating heightmaps (like seamless textures), then there are also techniques for that too. Again the libraries i mentioned contain examples how to do it.
Offline pixelprime

Junior Member


Medals: 3



« Reply #4 - Posted 2013-02-21 17:57:47 »

Most helpful, thank you very much Smiley
Offline Grunnt

JGO Wizard


Medals: 55
Projects: 9
Exp: 5 years


Complex != complicated


« Reply #5 - Posted 2013-02-21 18:29:36 »

I recreate this effect using a modified version of public domain code by Stefan Gustavson.

First you need a way to generate noise, for which Simplex Noise is a good fast approach:
1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
/**
 * Modified version of the SimplexNoise code placed in the public domain by Stefan Gustavson, Link√∂ping University,
 * Sweden. This just generates 2D noise, and uses a special FastRandom class for a slight performance boost.
 */

public class SimplexNoiseGenerator {

   private static final FastRandom random = new FastRandom();

   private static int grad3[][] = { { 1, 1, 0 }, { -1, 1, 0 }, { 1, -1, 0 }, { -1, -1, 0 }, { 1, 0, 1 }, { -1, 0, 1 },
         { 1, 0, -1 }, { -1, 0, -1 }, { 0, 1, 1 }, { 0, -1, 1 }, { 0, 1, -1 }, { 0, -1, -1 } };

   private static int p[] = { 151, 160, 137, 91, 90, 15, 131, 13, 201, 95, 96, 53, 194, 233, 7, 225, 140, 36, 103, 30,
         69, 142, 8, 99, 37, 240, 21, 10, 23, 190, 6, 148, 247, 120, 234, 75, 0, 26, 197, 62, 94, 252, 219, 203,
         117, 35, 11, 32, 57, 177, 33, 88, 237, 149, 56, 87, 174, 20, 125, 136, 171, 168, 68, 175, 74, 165, 71, 134,
         139, 48, 27, 166, 77, 146, 158, 231, 83, 111, 229, 122, 60, 211, 133, 230, 220, 105, 92, 41, 55, 46, 245,
         40, 244, 102, 143, 54, 65, 25, 63, 161, 1, 216, 80, 73, 209, 76, 132, 187, 208, 89, 18, 169, 200, 196, 135,
         130, 116, 188, 159, 86, 164, 100, 109, 198, 173, 186, 3, 64, 52, 217, 226, 250, 124, 123, 5, 202, 38, 147,
         118, 126, 255, 82, 85, 212, 207, 206, 59, 227, 47, 16, 58, 17, 182, 189, 28, 42, 223, 183, 170, 213, 119,
         248, 152, 2, 44, 154, 163, 70, 221, 153, 101, 155, 167, 43, 172, 9, 129, 22, 39, 253, 19, 98, 108, 110, 79,
         113, 224, 232, 178, 185, 112, 104, 218, 246, 97, 228, 251, 34, 242, 193, 238, 210, 144, 12, 191, 179, 162,
         241, 81, 51, 145, 235, 249, 14, 239, 107, 49, 192, 214, 31, 181, 199, 106, 157, 184, 84, 204, 176, 115,
         121, 50, 45, 127, 4, 150, 254, 138, 236, 205, 93, 222, 114, 67, 29, 24, 72, 243, 141, 128, 195, 78, 66,
         215, 61, 156, 180 };

   // To remove the need for index wrapping, double the permutation table length
  private static int perm[] = new int[512];
   static {
      for (int i = 0; i < 512; i++)
         perm[i] = p[i & 255];
   }

   // This method is a *lot* faster than using (int)Math.floor(x)
  private static int fastfloor(double x) {
      return x > 0 ? (int) x : (int) x - 1;
   }

   private static double dot(int g[], double x, double y) {
      return g[0] * x + g[1] * y;
   }

   // 2D simplex noise
  public static double noise(double xin, double yin) {
      double n0, n1, n2;

      final double F2 = 0.5 * (Math.sqrt(3.0) - 1.0);
      double s = (xin + yin) * F2;
      int i = fastfloor(xin + s);
      int j = fastfloor(yin + s);

      final double G2 = (3.0 - Math.sqrt(3.0)) / 6.0;
      double t = (i + j) * G2;
      double X0 = i - t;
      double Y0 = j - t;
      double x0 = xin - X0;
      double y0 = yin - Y0;

      int i1, j1;
      if (x0 > y0) {
         i1 = 1;
         j1 = 0;
      } else {
         i1 = 0;
         j1 = 1;
      }

      double x1 = x0 - i1 + G2;
      double y1 = y0 - j1 + G2;
      double x2 = x0 - 1.0 + 2.0 * G2;
      double y2 = y0 - 1.0 + 2.0 * G2;

      int ii = i & 255;
      int jj = j & 255;
      int gi0 = perm[ii + perm[jj]] % 12;
      int gi1 = perm[ii + i1 + perm[jj + j1]] % 12;
      int gi2 = perm[ii + 1 + perm[jj + 1]] % 12;

      double t0 = 0.5 - x0 * x0 - y0 * y0;
      if (t0 < 0)
         n0 = 0.0;
      else {
         t0 *= t0;
         n0 = t0 * t0 * dot(grad3[gi0], x0, y0);
      }

      double t1 = 0.5 - x1 * x1 - y1 * y1;
      if (t1 < 0)
         n1 = 0.0;
      else {
         t1 *= t1;
         n1 = t1 * t1 * dot(grad3[gi1], x1, y1);
      }

      double t2 = 0.5 - x2 * x2 - y2 * y2;
      if (t2 < 0)
         n2 = 0.0;
      else {
         t2 *= t2;
         n2 = t2 * t2 * dot(grad3[gi2], x2, y2);
      }

      return 70.0 * (n0 + n1 + n2);
   }

   public static void genGrad(long seed) {
      for (int i = 0; i < 255; i++)
         p[i] = i;
      for (int i = 0; i < 255; i++) {
         int j = random.nextInt(255);
         int nSwap = p[i];
         p[i] = p[j];
         p[j] = nSwap;
      }

      for (int i = 0; i < 512; i++)
         perm[i] = p[i & 255];
   }


However, as VeaR says, only using this approach gives kind of ugly "smoothed-out" noise, not much like clouds. For that you need to use octaves. Here's the code I use for that, which makes use of the SimplexNoiseGenerator. It also adds an additional effect: by setting a radius you can make a single cloud (or island) instead of continuous noise. You may need to adjust the code a bit if you dont want this:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
import java.awt.Color;
import java.awt.image.BufferedImage;

public class CloudGenerator {

   public static final FastRandom random = new FastRandom();

   private static float[] octaveScale = { 2, 4, 8, 16 };
   private static float[] octaveAmplitude = { 8, 4, 2, 1 };

   public static BufferedImage generateCloudImage(int width, int height, float radius, float red, float green,
         float blue) {
      SimplexNoiseGenerator.genGrad(random.nextInt());
      float smallradius = radius * 0.8f;

      int octaves = octaveScale.length;
      float amplitudeSum = 0;
      for (int a = 0; a < octaves; a++) {
         amplitudeSum += octaveAmplitude[a];
      }

      BufferedImage bufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
      int halfWidth = width / 2;
      int halfHeight = height / 2;
      int xStart = (int) (halfWidth - radius);
      int xEnd = (int) (halfWidth + radius);
      int yStart = (int) (halfHeight - radius);
      int yEnd = (int) (halfHeight + radius);
      for (int x = xStart; x < xEnd; x++) {
         for (int y = yStart; y < yEnd; y++) {
            float noiseSum = 0;
            for (int o = 0; o < octaves; o++) {
               noiseSum += (float) SimplexNoiseGenerator.noise(x * octaveScale[o] / (float) width, y
                     * octaveScale[o] / (float) height)
                     * (octaveAmplitude[o] / amplitudeSum);
            }
            float dist = 1 - (MathUtils.distance(x, y, halfWidth, halfHeight) / smallradius);
            float val = (dist + noiseSum * 0.35f);
            float result = MathUtils.clamp(val, 0, 1);
            bufferedImage.setRGB(x, y, new Color(red, green, blue, result).getRGB());
         }
      }

      return bufferedImage;
   }
}


Here's a cloud generated using this approach:

Offline relminator
« Reply #6 - Posted 2013-02-22 12:31:41 »

An alternate method is to combine and smooth different textures by using a splatting-like pixel combiner.

http://rel.phatcode.net/junk.php?id=38

Another set of good procedural heightmap generator can be found here:

http://blackpawn.com/texts/default.html
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (30 views)
2014-04-15 18:08:23

BurntPizza (27 views)
2014-04-15 03:46:01

UprightPath (42 views)
2014-04-14 17:39:50

UprightPath (24 views)
2014-04-14 17:35:47

Porlus (41 views)
2014-04-14 15:48:38

tom_mai78101 (63 views)
2014-04-10 04:04:31

BurntPizza (123 views)
2014-04-08 23:06:04

tom_mai78101 (222 views)
2014-04-05 13:34:39

trollwarrior1 (189 views)
2014-04-04 12:06:45

CJLetsGame (198 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!