2

I have been implementing a perlin noice generator in python. It works pretty well except for clearly visible lines in the result.

The problem seems to be related to where i switch between gradients in X-direction.

Here is the code:

from random import randint, seed
from PIL import Image
from numpy import asarray, interp, uint8

seed(1)
gradients = []
for x in range(20):
    gradients.append([])
    for y in range(20):
        gradients[x].append([randint(-1,1), randint(-1,1)])

def getInfluenceValue(x,y,Xgrad,Ygrad):
    return ((gradients[Xgrad][Ygrad][0] * (x-Xgrad)) + (gradients[Xgrad][Ygrad][1]*(y-Ygrad)))
    
def lerp(v0,v1,t):
    return (1 - t) * v0 + t * v1;

def fade(t):
    return 3*pow(t,2) - 2*pow(t,3)

def perlin(x, y):
    X0 = int(x) 
    Y0 = int(y)
    X1 = X0+1
    Y1 = Y0+1
    sx = fade(float(x) - float(X0));
    sy = fade(float(y) - float(Y0));
    topLeftDot = getInfluenceValue(x,y,X0,Y1)
    topRightDot = getInfluenceValue(x,y,X1,Y1) 
    bottomLeftDot = getInfluenceValue(x,y,X0,Y0)
    bottomRightDot = getInfluenceValue(x,y,X1,Y0)

    return lerp(lerp(topLeftDot, topRightDot, sx), lerp(bottomLeftDot, bottomRightDot, sx), sy)

tmp_list = []
for x in range(1000):
    tmp_list.append([])    
    for y in range(1000):
        tmp_list[x].append(perlin(x/100.0, y/100.0))

data = asarray(tmp_list)
rescaled = interp(data, (data.min(), data.max()), (0, 255)).astype(uint8)
Image.fromarray(rescaled).save('test.png')

Here is the result:

current results

I have tried replacing the lerp function and i have used other fade functions. But the problem remains. What's going on here?

PS. I have seen other questions on stackoverflow regarding "blocky" results in perlin noise generation, but since this result only seem to be "blocky" in 1 direction/dimension, i think the problem is unrelated to those questions.

martineau
  • 119,623
  • 25
  • 170
  • 301
ViktorA
  • 21
  • 3
  • 1
    You're basically asking for someone the help debug your code. It would be helpful if you added at least an overview of what your code does (or is trying to do). – martineau Aug 24 '20 at 22:01

2 Answers2

1

To answer your question, I am not certain if this is the entire issue, but I do see one error. In X, you lerp along sx from X0 to X1. But in Y, you lerp along sy from Y1 to Y0. Swapping Y1/Y0 in the topLeftDot-bottomRightDot vars should fix that. Alternatively, switch which variables are in which parts of the lerp return line. If this doesn't solve it, try reversing X0/X1 instead. I am wondering why your image shows the discontinuities breaking up the X axis instead of breaking up the Y axis.

To add, I see that you are using an integer cast to turn x,y into X0 and Y0. This will work fine for postive numbers, but might not work for negative numbers. See this question and its answers for more info.

Finally, my general recommendation: Perlin is an old method for noise that tends to produce very grid-aligned looking results. Once you get your noise working, or if you look at other images of Perlin noise, it may become apparent that a lot of the parts of the noise are aligned 45 or 90 degrees (Perlin is top row). This can be a rewarding programming exercise, but if you're going further than a programming exercise then I would suggest coding or using a good Simplex or Simplex-related noise implementation. See this post for an elaboration on implementing 2D simplex noise out of Perlin, or this if you want an easy-to-import Simplex-related noise to use in your project.

KdotJPG
  • 811
  • 4
  • 6
  • Thank you, Switching the Y variables as you said fixed the issue, it seems like i made a the error by thinking about the bottom left corner as (0,0). And yes, this is just used as a programming exercise. I have implemented perlin noise and diamond square algorithm for use in terrain generation. And was thinking of implementing simplex noice next. So the links to simplex noice is appreciated. – ViktorA Aug 25 '20 at 14:38
1

It looks like the only problem is that bottom-top are reversed in Y-interpolation. This results in vertical bands because with the way the code is set up, Y is the horizontal axis.

Switching the return statement to have "bottom" first "top" second:

return lerp(lerp(bottomLeftDot, bottomRightDot, sx), lerp(topLeftDot, topRightDot, sx), sy)

produces the following Perlin noise image:

perlin noise

Joni
  • 108,737
  • 14
  • 143
  • 193