0
\$\begingroup\$

I am trying to generate terrain with discrete height levels using Unity's built in 2D Perlin noise function. The output is a float in [0,1]. I then scale the float linearly to a range [minimum Height, Maximum Height], and round it to the nearest integer.

The problem is that this results in artifacts at rounding boundaries, as can be seen in the following image:

Perlin noise artifacts

As you can see, the ridge line partially marked in red should not be there. It is created because that's the rounding boundary, meaning that this is the line between x.49 and x.50 (x is integer), so the x.49 is rounded to x, while the x.50 is rounded to x+1.

I'll demonstrate that by zooming to a part of the image and showing the height value of different blocks:

Block heights

The height of Block 1 is 14.48

The height of Block 2 is 14.52

The height of Block 3 is 15.6

The height of Block 4 is 14.6

As you can see, the only reason there is a ridge between Block 1 and Block 2, is because 1's height is rounded to 14, while 2's height is rounded to 15. This is unlike Blocks 3 and 4, which should have an elevation difference between them as per the Perlin noise function.

I can of course use Floor or Ceil or other methods to turn the float result from the Perlin Noise function into an integer, but that simply results in artifact lines appearing at other places.

Any help in solving this issue would be greatly appreciated.

Thank you.

Edit:

Here is how I get the height of each column:

float grassHeightFloat = WorldGenMath.getNoise(x + grassHeightRandOffSet, z + grassHeightRandOffSet,
            grassSmoothness, grassMinHeight, grassMaxHeight)

uint grassHeight = Convert.ToUInt32(grassHeightFloat);

WorldGenMath is:

private static float scaleNoise(float value, float minScale, float maxScale)
    {
        return scale(value, 0, 1, minScale, maxScale);
    }
private static float scale(float value, float min, float max, float minScale, float maxScale)
    {
        float scaled = minScale + (float)(value - min) / (max - min) * (maxScale - minScale);
        return scaled;
    }
public static float getNoise(uint x, uint z, float smoothFactor, int minScale, int maxScale)
    {
        float noise = Mathf.PerlinNoise(x * smoothFactor, z * smoothFactor);
        return scaleNoise(noise, minScale, maxScale);
    }
public static float getNoise(long x, long z, float smoothFactor, int minScale, int maxScale) {
        return getNoise(Convert.ToUInt32(x), Convert.ToUInt32(z), smoothFactor, minScale, maxScale);
    }
\$\endgroup\$
  • \$\begingroup\$ The coordinates for each tile are (x,y,z), with y being the height. To find out the height of each column, I simply input them into the Perlin noise function, meaning y = PerlinNoise(x,z). Is that not correct? \$\endgroup\$ – user92748 Aug 19 '17 at 17:21
  • \$\begingroup\$ I added the code to my post. \$\endgroup\$ – user92748 Aug 19 '17 at 17:30
  • \$\begingroup\$ The shape is selected later, and is not the issue. Please ignore the shapes of the tiles. The issue would still be there even if all the tiles were cubic. Please look at tiles number 1 and 2 in the image. The difference between their heights is minimal, but because one is rounded up and one is rounded down (since one is >0.5 while the other is <0.5), there is a noticeable height difference in the discrete solution. I'm trying to get rid of that difference, and only have tiles at different heights when there is a large difference (like in tiles 3 and 4). Sorry if that wasn't clear before. \$\endgroup\$ – user92748 Aug 19 '17 at 18:16
  • \$\begingroup\$ Ahh, I'd misunderstood which effect in the image you were trying to eliminate. The rounding between 1 and 2 shouldn't be any different than the rounding between 3 and 4 though, should it? Does this suggest the shape of the noise function itself has a bump in it that the terracing of the rounding is making more noticeable? Maybe an amplitude very close to your terrace step, so it manifests as just one big ring instead of an orderly set of nested curves like the greater amplitudes… \$\endgroup\$ – DMGregory Aug 19 '17 at 18:28
  • 1
    \$\begingroup\$ I'm not recommending a solution, I'm speculating on the source of the problem. Noise functions are often summations of multiple different components. A component whose intensity is very close to your rounding interval will make seemingly standalone features that only cross the rounding interval once, rather than stacking up in nested staircases. The arc you're seeing could be such a feature from a low-amplitude component. \$\endgroup\$ – DMGregory Aug 19 '17 at 19:12
0
\$\begingroup\$

The effect looks like its an issue of adding 2 perlin noise maps over one-another and 1 of them is not properly scaled resulting in an edge instead of a nice hill. 1 of the maps returns between 0 and 1, and not minHeight / maxHeight.

\$\endgroup\$
  • \$\begingroup\$ Thank you! That was indeed the issue. I made a mistake elsewhere in my code, and it resulted in adding 2 Perlin Noise maps one on top of the other. \$\endgroup\$ – user92748 Aug 22 '17 at 15:30

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.