Displaying an array of points using Perlin noise

edited March 2018 in Python Mode

Hi guys,

I'm having a hard time trying to display points pseudo-randomly on the screen using Perlin noise. Ideally I would like those points to form small clusters as they get close to each other (higher density). Quite like the dark areas of a texture generated by noise.

Do you have an idea how I could do that ?

My failed attempt:

def setup():
    size(800, 600)
    background(255)

    t = 0


    for x in range(0, width, 10):
        for y in range(0, height, 10):
            t += .001
            p = PVector(x, y)
            n = map(noise(p.x + t, p.y + t), 0, 1, 0, 10)
            p.mult(n)
            strokeWeight(2)
            point(p.x, p.y)

Please note that I'm writing in Python here but the question is open to everyone and any help (ideas, suggestions, Java code) is welcomed.

Answers

  • edited March 2018

    I came up with this idea but it's certainly not the best solution: combining noise texture algorithm with a probabilistic function + add some randomness.

    def setup():
        size(800, 600, P2D)
        background(255)
    
        n_range = 10
        step = 3
        treshold = .95
    
        for x in range(0, width, step):
            for y in range(0, height, step):
                nx = map(x, 0, width, 0, n_range)
                ny = map(y, 0, height,0, n_range)
                n_value = noise(nx, ny) * 255
                if n_value  < 100 and random(1) > treshold:
                    strokeWeight(3)
                    point(x + random(10), y + random(10))
    

    By tweaking the variables and computing Delaunay triangulations you can get interesting shapes

    Anyway, if you have better suggestions, I would love to hear it.

  • @solub --

    1. Do the points have a minimum distance that they should spawn from each other?
    2. Is the number of point static and known ahead of time?
    3. Are the points static, or is the idea that they are each animated, e.g. points all random-walking?
  • edited March 2018

    Hi @jeremydouglass.

    1. Not really. As long as they do not share the exact same positions it's fine.
    2. The number of point doesn't matter much.
    3. The points remain static.

    What's important to me is the final result. Ideally I'd like to see the point density decreasing as points get farther away from the center of the "cluster" they belong to. In the Delaunay picture above you can see a clear and sudden separation between the clusters, there's no such gradation.

  • edited March 2018 Answer ✓
    1. imagine a grid of potential l points -- e.g. 1 per pixel, or 1 per 2x2 pixels, etc.

    2. each square is populated with a 2D Perlin noise value. the output value range of Processing's Perlin noise() is 0-1.0.

    3. map this to e.g. 255-128 to draw light and dark areas, and draw it as a colored pixel.

    4. map this against a range of probabilities of drawing a point. E.g. (0.0001) to (0.3) to indicate low probability of drawing a point vs. high probability of drawing a point.

    5. generate an independent random() number.... if the number is lower than the noise() number, draw a point!

    Now you can draw randomized clouds of points with density corresponding to 2D Perlin noise. Play with the probability parameters to get the kinds of point clouds that you want.

    ...in addition, as you have done above, you can drift the point when you place it.


    Note that in your example,

    if n_value  < 100 and random(1) > treshold:
    

    What you have done is taken a low-pass filter on the perlin noise, leaving yourself fixed black blobs. You have then thinned the points in those blobs by a fixed thinning threshold (0.95, or 1/20). This means you are throwing almost all the randomness out and thinning a small slice of the data by a fixed amount -- I'm suggesting that you use all the randomness, populating by a variable amount. The downside of this approach is that there is a small chance you could get a point absolutely anywhere -- but you can fix this with a much smaller pass filter, throwing away the 10-20% of the Perlin noise you want to be white space (rather than throwing away almost all of the noise and then scattering the few the remaining points out by random(10)).


    ...there is another way to do this, which in some cases will have different behavior based on settings:

    1. Populate your grid with perlin noise.
    2. Average your grid into tiles (pixelate) -- e.g. instead of 200x200 pixels you have 10x10 tile of 20x20 pixels each.
    3. Loop over your 100 tiles and populate each tile with randomly placed points -- a number of points scaled to the value of that tile.

    Note that if you get very large tiles or have dramatically different tiles (very dark or very light) next to each other, this will create a visible fencing effect, and you will see the bins.

  • edited April 2018

    Sorry, I think I have some language issue. I'm not sure to grasp the meaning of step 4: to "map [the noise value] against a range of probabilities". Do you think the following is an accurate translation of your explanation ?

    def setup():
        size(800, 600, P2D)
        background(255)
    
        step = 2
        n_range = 6
    
        for x in range(0, width, step):
            for y in range(0, height, step):
                nx = map(x, 0, width, 0, n_range)
                ny = map(y, 0, height,0, n_range)
                n_value = map(noise(nx, ny), 0, 1, 128, 255)
                prob = map(n_value, 128, 255, 0, .3)
    
                if random(1) < prob:
                    strokeWeight(3)
                    stroke(0)
                    point(x, y)
    
  • edited March 2018 Answer ✓

    @solub -- You are correct!

    Your starter example takes the top half the noise map (128-255) and fills that with between 0 and 1/3 pixel density:

    prob = map(n_value, 128, 255, 0, .3)
    

    Try instead taking the top quarter of the noise map and scaling up to 1/5 density:

    prob = map(n_value, 192, 255, 0, .2) 
    

    Or try other values for different visual results:

    prob = map(n_value, 160, 255, 0, .5) 
    

    If you use randomSeed() at the beginning of draw to make each pass of random numbers deterministic then you can explore parameters interactively.

    Here is an interactive example of your sketch with mouseX mapped against density, and mouseY mapped against your high-pass filter. Explore the space with your mouse to see what settings do to the output.

    # Processing 3.3.6 / Processing.py 3033 / 2018-03-25
    # forum.processing.org/two/discussion/27138/displaying-an-array-of-points-using-perlin-noise
    
    def setup():
        size(800, 600, P2D)
        global seed
        seed = int(random(0,1024))
        print(seed)
    
    def draw():
        randomSeed(seed)
        step = 2
        n_range = 6
        density = map(mouseX, 0, width, 0, 1)
        highpass = map(mouseY, 0, height, 0, 255)
        background(255)
        for x in range(0, width, step):
            for y in range(0, height, step):
                nx = map(x, 0, width, 0, n_range)
                ny = map(y, 0, height,0, n_range)
                n_value = map(noise(nx, ny), 0, 1, 128, 255)
    
                prob = map(n_value, highpass, 255, 0, density) 
                if random(1) < prob:
                    strokeWeight(3)
                    stroke(0)
                    point(x, y)
    

    Notice that the sweet spot -- the exact kinds of dispersion or density you are hoping for -- may be extremely particular, and would have been very hard to find by just plugging in values and rerunning the sketch.

    Also notice that these rates of dispersion all rely on the slope of the Perlin noise output, no matter how you scale it. If you want blobs have very dense centers and very broad sparse perimeters (like a fried egg), or if you want blobs that have elliptical outlines, et cetera... then you could use a similar process, but you don't want to use Perlin noise specifically.

    Screen Shot 2018-03-25 at 10.04.11 AM

  • edited March 2018

    Fantastic ! Many thanks for always providing super comprehensive answers and clever tips.

Sign In or Register to comment.