Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The last time I made a world generation algorithm for a voxel engine prototype I got reasonable results through the judicious combination of different types of noise, (specifically simplex, ridged and turbulence noise) and transforming the result using cubic hermite splines for added control, provided you are okay with generally only having one or two main continents (three and four do occur, but are much less common).

This image shows 16 possible world maps (I found it useful to just have them appear next to each other in the world so I could quickly get an overview of the consequences of tweaking parameters): http://imgur.com/dthr7O2

This is the algorithm that generated the world (written in C#). permutation is a random ordering of the numbers 0 to 255, repeated twice. HermitePoints take an x and y coordinate and a slope. Noise functions take an x and y coordinate, a number of octaves, a frequency and a permutation. Turbulence and ridged noise output results in the domain [0, 1], simplex noise in the domain [-1, 1].

  private static Chunk GenerateChunk(short chunkX, short chunkZ, int seed, byte[] permutation)
  {
      var chunk = new Chunk(chunkX, chunkZ);

      int worldMapSize = 256;
      short maxHeight = (short)Math.Min(worldMapSize >> 2, Chunk.CHUNK_HEIGHT);

      float[] worley = new float[2];

      HermiteSpline continentCurve = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(0.07f, 0.03f, 1f), new HermitePoint(0.12f, 0.1f, 0.7f),
          new HermitePoint(0.21f, 0.18f, 1f), new HermitePoint(0.24f, 0.2455f, 0f), new HermitePoint(0.26f, 0.26f, 1f), new HermitePoint(1f, 1f, 1f)
      });
      HermiteSpline continentMask = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(0.25f, 0f, 0f), new HermitePoint(0.4f, 1f, 0f), new HermitePoint(1f, 1f, 0f)
      });
      HermiteSpline plateMountainCurve = new HermiteSpline(new[]
      {
          new HermitePoint(0f, 0f, 0f), new HermitePoint(1f, 1f, 3.5f)
      });

      for (short x = 0; x < Chunk.CHUNK_SIZE; x++)
      {
          for (short z = 0; z < Chunk.CHUNK_SIZE; z++)
          {
              int globalX = (chunkX << Chunk.CHUNK_SIZE_LOG2) + x;
              int globalZ = (chunkZ << Chunk.CHUNK_SIZE_LOG2) + z;

              //Subdive the world into squares, each of which contains an independent world map
              //The edges of each square are lowered so that each map is separated by oceans
              float xSeparator = (float)Math.Sin((globalX & (worldMapSize - 1)) * MathHelper.Pi / worldMapSize);
              float zSeparator = (float)Math.Sin((globalZ & (worldMapSize - 1)) * MathHelper.Pi / worldMapSize);
              float rectSeparator = (float)Math.Min(1, 3 * Math.Min(xSeparator, zSeparator));
              float circleSeparator = xSeparator * zSeparator;
              float mapSeparator = rectSeparator * 0.375f + circleSeparator * 0.575f + 0.05f;

              //Use turbulence noise to get the typical clumped shape of continents and add some simplex and ridged noise for the thinner shapes
              float continentNoise1 = Noise.Turbulence(globalX, globalZ, 8, worldMapSize, permutation);
              float continentNoise2 = Noise.Simplex(globalX, globalZ, 8, worldMapSize * 0.16f, permutation) * 0.5f + 0.5f;
              float continentNoise3 = Noise.Ridged(globalX, globalZ, 8, worldMapSize * 0.5f, permutation);
              continentNoise2 *= continentNoise2;
              continentNoise3 *= continentNoise3;
              float continentHeight = continentNoise1 * 0.5f + continentNoise2 * 0.25f + continentNoise3 * 0.125f;
              float baseHeight = continentCurve.Map(continentHeight * mapSeparator);

              //Add mountains caused by convergent continental plate boundaries
              float continentMult = continentMask.Map(baseHeight);
              float plateMountainNoise1 = Noise.Ridged(globalX, globalZ, 8, worldMapSize * 0.4f, permutation);
              float plateMountainNoise2 = Noise.Simplex(globalX, globalZ, 8, worldMapSize * 0.2f, permutation) * 0.5f + 0.5f;
              float plateMountainNoise = plateMountainNoise1 * 0.66f + plateMountainNoise2 * 0.33f;
              float plateMountainHeight = plateMountainCurve.Map(plateMountainNoise) * continentMult;

              //Apply the map separation
              float finalHeight = baseHeight + plateMountainHeight;

              //Convert the height from range [0,1] to range [1,255]
              byte height = (byte)(finalHeight * (maxHeight - 1) + 1);

              for (short y = 0; y < height; y++)
              {
                  chunk[(short)x, y, z] = (byte)((y + 1) * 255 / (maxHeight + 1));
                  chunk.SetStack((short)x, z, new short[] { height, (short)(Chunk.CHUNK_HEIGHT - height) });
              }
          }
      }

      return chunk;
  }


The downside of Simplex noise is that anything greater than 2D is patented: http://en.wikipedia.org/wiki/Simplex_noise There is another project called OpenSimplex that has similar results.


Not in europe :)


Ha, touché.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: