1
\$\begingroup\$

I am creating a game in which a world map is generated using Perlin noise, and then specific points in the map are rendered using higher resolution Perlin noise. I am trying to do this by zooming in on specific points of the map, therefore rendering them at a high resolution (by dividing by a larger number in my Perlin calculations)

This works exactly as intended, up until I try to offset the Perlin noise from the center. As soon as this is done, zooming in no longer works-- When you zoom in, random noise is produced. I have ensured that the offset is remaining consistent, so it isn't that.

I have a feeling I'm misunderstanding something about how Perlin noise works, here. The following code works fine.

public void Generate()
{
    var perlin = new Perlin();
    
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            TileBase tile = GetTile((1d + perlin.NoiseOctaves(
                 (double)(x - 50) / scale.value,
                 (double)(y - 50) / scale.value,
                 0.5d, 10, lacunarity.value, 1 - persistence.value)) / 2d);
            tilemap.SetTile(new Vector3Int(x , y, 0), tile);

        }
    }
}

However, as soon as I try to offset x by a constant value in order to make the noise different, it breaks the zoom-in feature.

https://gyazo.com/a741bec0ec9e509e796a70d8750cf10d Expected behavior

https://gyazo.com/d3460e3cfbf7562b0582eacfd9b0d662 Offsetted, unexpected behavior

What could be going wrong? Am I misunderstanding something with Perlin noise? Any help is much appreciated!

Edit: The -50 in the calculation is because I am working with a 100 x 100 tile grid, and I am wanting to zoom in on the center, rather than on the bottom left corner as would be the default behavior. The offset code would look like this:

public void Generate()
{
    var perlin = new Perlin();
    
    for (int x = 0; x < 100; x++)
    {
        for (int y = 0; y < 100; y++)
        {
            TileBase tile = GetTile((1d + perlin.NoiseOctaves(
                 (double)(x - 50 + offsetX) / scale.value,
                 (double)(y - 50 + offsetX) / scale.value,
                 0.5d, 10, lacunarity.value, 1 - persistence.value)) / 2d);
            tilemap.SetTile(new Vector3Int(x , y, 0), tile);

            //tilemap.SetTile(new Vector3Int(x, y, 0), );
        }
    }
}

With the offsets being randomly generated integers.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ This has to do with the order of operations regarding where you add the offset in the coordinate math. Add it in the wrong place, and changing the scale no longer zooms into the center of the view, it zooms out of frame, bringing fresh noise into view with each tick along the slider; or worse, you get different offsets on different octaves, shearing and sloshing through noise space. In the code above, is the - 50 your offset? If not, can you edit your question to show the code that adds the offset? \$\endgroup\$
    – DMGregory
    Jul 20 at 13:23
  • \$\begingroup\$ @DMGregory I thought it was something similar to that. I am working with a 100 x 100 grid of tiles, the -50 is to zoom in on the center rather than the bottom left corner. I'll add this and the new code to the question \$\endgroup\$ Jul 20 at 18:54

1 Answer 1

0
\$\begingroup\$

The way you've written this math, the scale slider zooms in toward (0, 0). You could distribute the scale over the formula like so:

(x - 50 + offsetX) / scale.value
==
(x - 50) / scale.value + offsetX/scale.value

So the tile at the center of your map only corresponds to (offsetX, offsetY) when scale.value = 1. As you scale down / zoom in, you also translate sideways by dividing the offset.

This gets more pronounced the larger your offset value. If offsetX is 1000 and you reduce your scale to 0.9, now the center of your view is at 900, and you've scrolled the 950-1050 window you had previously been looking at out of view entirely. Now you see a patch of noise uncorrelated with the patch you were looking at before (which is way off to the right, out of frame), so it looks like your tiles just randomized.

To fix this, move the offset to the outside of the expression:

(double)((x - 50) / scale.value + offsetX)

Now the offset is independent of the scale.

Another factor that can come into play is aliasing. If the noise function you're sampling has detail on scales smaller than a tile, then it matters a lot where exactly within a tile your sample point lands. As you zoom, your sample point slides continuously over all those little bumps and valleys, rather than jumping across them in tile-sized intervals. That can make detail that was not apparent at one scale suddenly appear, making the tile pattern shimmer. This is a form of undersampling or spatial aliasing, and it can be solved by damping out octaves at higher frequencies than your tile spacing.

\$\endgroup\$
1
  • \$\begingroup\$ Thank you so much, this works perfectly! That makes so much more sense now that you explain it. \$\endgroup\$ Jul 20 at 23:15

You must log in to answer this question.

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