5
\$\begingroup\$

I am using a simplex 2D noise to generate my worlds so far. This however was never the plan and has the downside that I basically can only generate a heightmap. Instead I want to aim for floating islands scattered in the sky, but I fail to see how I can utilize 3D noise properly for this.

Based on a simplex 3D noise function I am currently generating noise like this:

float
sc_perlin_noise3_ex(const sc_perlin_t *perlin, float x, float y,
                    float z, int octaves, float persistence,
                    float base_frequency, float base_amp,
                    float lacunarity)
{
    int i;
    float freq = base_frequency;
    float amp = base_amp;
    float max = 1.0f;
    float total = 1.0f;

    for (i = 0; i < octaves; i++) {
        total += sc_perlin_noise3(perlin, x * freq, y * freq, z * freq) * amp;
        max += amp;
        freq *= lacunarity;
        amp *= persistence;
    }
    return total / max;
}

Problems start there: first of all I think I got octaves wrong, at least the outcome is very unpredictable. Persistence would be a value smaller 1 that removes the effect of the higher octaves.

Secondly however I have a general problem understanding how 3D noise works. From my understanding a value < -1 could mean air, a value > 1 would be solid and 0 is the thin line between them (the surface). First of all however I get a lot more values below zero than above zero, so I had to flip those two around. That seems odd, do I have the math wrong?

The actual problem however is that I cannot locate "islands" properly. Say I have a container of 256^3 in size and I want to have one large island in there with maybe up to three smaller islands in there I have to ensure that:

  1. no island is cut off at the edges
  2. that I only get two even sized islands in my block
  3. I have some better control of the actual appearance of my island. For instance it would be nice if it had an actual flat-ish surface where I can put a heighmap on top of that for mountains etc.

Based on that I have the feeling I am on the completely wrong track here. Can anyone give me some pointers on where to head now?

Generally speaking I would like to have something that looks like this: floating island

\$\endgroup\$

4 Answers 4

3
\$\begingroup\$

A floating island? You don't really need 3D noise for that, just get two layers of 2D noise, then overlay them. One layer is the "ground" layer, then the next layer is the "floating islands" layer, and you could even go for a 3rd layer that you consider the "clouds" layer. There really should be no correlation between the ground layer and the "floating islands" layer.

3D perlin noise is basically for animations. Say you had a pool of molten lava that you generated with 2D perlin noise, and you want it to appear flowing. Well you'd use 3D perlin noise for that, and create an animation by selecting "sheets" from the cube of 3D perlin noise. The beauty of it is the noise would appear continuous throughout the animation, because the Perlin noise would be smooth in the 3 dimensions.

But for what you're doing you don't really need 3D perlin noise, just 2 layers of 2D noise will do.

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

Sounds like you are in over your head. You would probably want to split the two tasks of generating and positioning islands. Try first generating a bunch of islands, and then placing them randomly, if an island is placed so that it collides with an already placed island (or is just too close) reposition it, check again etc.

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

I went with 3D noise again because it produces much nicer things than I could accomplish with 2D noise. I'm currently using a falloff factor for the general form of my island but unfortunately it still sometimes happens that the island will just be too small.

Generally though it works:

static int
block_is_solid(const sc_worldgen_t *worldgen, float x, float y, float z)
{
    float noise, density, center_falloff, plateau_falloff;

    /* caves */
    if (powf(sc_perlin_noise3_ex(worldgen->perlin, x * 5.0f, y * 5.0f,
                                 z * 5.0f, 3), 3.0f) < -0.5f)
        return 0;

    /* falloff from the top */
    if (y > 0.9f)
        return SC_BLOCK_AIR;
    else if (y > 0.8f)
        plateau_falloff = 1.0f - (y - 0.8f) * 10.0f;
    else
        plateau_falloff = 1.0f;

    /* falloff from center */
    center_falloff = 0.1f / (
        powf((x - 0.5f) * 1.5f, 2.0f) +
        powf((y - 1.0f) * 0.8f, 2.0f) +
        powf((z - 0.5f) * 1.5f, 2.0f)
    );

    /* noise combined density */
    noise = sc_perlin_noise3_ex(worldgen->perlin, x, y * 0.5, z, 7);
    density = noise * center_falloff * plateau_falloff;
    return density > 0.2f ? SC_BLOCK_STONE : SC_BLOCK_AIR;
}

Technique from pyalot.

\$\endgroup\$
3
  • \$\begingroup\$ Looks like this: i.imgur.com/AseTr.png \$\endgroup\$ Dec 14, 2010 at 13:56
  • \$\begingroup\$ Cool! I was thinking you wanted it to have a flat-ish bottom, so I suggested a way to create extra 2D height maps, but this looks good. \$\endgroup\$
    – bobobobo
    Dec 15, 2010 at 19:30
  • \$\begingroup\$ Once you have the noise somewhat under control it's easy to adapt the values for different forms. \$\endgroup\$ Dec 16, 2010 at 21:39
0
\$\begingroup\$

Like @bobobob suggest just layer two 2 dimensional perlin noise (or similar functions) together, here is an example, (this is assumes you are using voxels, but you can easily adapt it to pretty much any thing with height data) here is the pesudo code:

Voxel[,,] voxels = new int[sizeX, sizeY, sizeZ];

for (int x = 0; x < < sizeX; x++)  {
    for (int y = 0; y < sizeY; y++)  {
       for (int z = 0; z < sizeZ; z++)  {
           voxel[x, y, z] = VoxelType.Air;    
      } 
   }
}

// Bottom half
PerlinNoise[] perlinOctaves = new PerlinNoise[] { new PerlinNoise(freq, amp, seed) } /* Add as many octaves as you feel nessercary*/ }

double[,] noiseMap = PerlinNoise.SumOctaves(sizeX, sizeY, sizeZ, perlinOctaves);

int centreY = sizeY / 2;

for (int x = 0; x < < sizeX; x++)  {
   for (int z = 0; z < sizeZ; z++)  {
       // This basic inverts the "hill"'s created from the perlin noise;
       int y = centreY - (int)noiseMap[x, z];

       // Fill from the bottom of the hill up to the centre of the map
       for (int j = y; j < centreY; j++)  {
           voxels[x, j, z] = Voxel.Solid; 
       }
   }
}

// top half, go crazy with this add hills, what ever you want :)
perlinOctaves = new PerlinNoise[] { new PerlinNoise(freq, amp, seed) } /* Add as many octaves as you feel nessercary*/ }

noiseMap = PerlinNoise.SumOctaves(sizeX, sizeY, sizeZ, perlinOctaves);

for (int x = 0; x < < sizeX; x++)  {
   for (int z = 0; z < sizeZ; z++)  {
       // Go about the top half
       int y = centreY - (int)noiseMap[x, z] + centreY;

       // Place the top layer
       voxels[x, y, z] = Voxel.Solid;

       // If the top half is not touching a part of the bottom get rid of it
       if (voxels[x, y - 1, z] == VoxelType.Air)  {
           voxels[x, y, z] = Voxel.Air;
       }

       // If the top part is generated below the bottom half, remove what is above it so the top part is visible
        for (int j = y + 1; j < centreY; j++) {
           voxels[x, j, z] = Voxel.Air;
        }
   }
}

So as the top and bottom half are pretty much similar, just inverted and really huge, make the frequency really low but the amplitude really high.

\$\endgroup\$

You must log in to answer this question.

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