Applying noise to a circular grid


#1

Hi,

I’m having difficulties correctly applying Perlin noise to a circular grid.

When I first computed the noise value, the end of the grid was not matching its beginning anymore… which is normal.

The problem is, when I try to link the beginning to the end of the grid then the noise becomes “symetrical”. And I can’t seem to figure out how to break that symmetry. Is there something wrong or missing with the way I’m computing the 2D noise value ? (terrain[x][y] here below)

Circular grid without noise

Circular grid with noise

Matching edges circular grid with noise


Example sketch

add_library('peasycam')

n_points, radius, step = 200, 350, 2
maximum = 450
minimum = 170
factor = .006



def setup():
    global lines
    size(1800, 800, P3D)
    background(0)
    smooth(8)
    
    cam = PeasyCam(this, 1400)
    n_cols, n_rows = 1000, 80
    angle = radians(360) / n_cols

    terrain = [[0 for e in range(n_rows)] for f in range(n_cols+1)]
    
    # Computing noise
    for y in range(n_rows):
        for x in range(n_cols+1):
            #terrain[x][y] = map(noise((x *factor)+t, (y*factor)+t), 0, 1, 0, maximum)    ### Edges not matching
            terrain[x][y] = map(noise(cos(x * angle) * 3, sin(y * angle) * 3 ), 0, 1, 0, maximum)  ### Edges matching but symetrical noise
            
    lines = createShape()
    for y in range(n_rows):
        lines.beginShape(LINES) 
        lines.strokeWeight(1)
        lines.noFill()
        lines.stroke(255)
        for x in range(n_cols+1):
            lx1 = cos(angle * (x-1) ) * (radius + (y*step))
            ly1 = sin(angle * (x-1) ) * (radius + (y*step))
            lx2 = cos(angle * x ) * (radius + (y*step))
            ly2 = sin(angle * x ) * (radius + (y*step))
            lines.vertex(lx1, ly1, terrain[x-1][y])
            lines.vertex(lx2, ly2, terrain[x][y])
        lines.endShape(CLOSE)
            
       
def draw():
    background(0)

    shape(lines)

#2

I did some googling and ran across these:


#3

Hi @tony,

If you look at my script you’ll see that the noise value is somewhat similar to the example you’re providing, except it is computed from a 2D array list. There’s unfortunately no solution I can find from these links (not to mention the computation of the noise value from the first sketch is not quite correct).


#4

So you want the edges to match but avoid the symmetry?

Kf


#5

Here’s an idea;

Save the value of the of the noise you start with, and interpolate towards that near the end of the circle.


#6

I think you get a symmetry because of the way you are using the noise function.

If you look at your line:
terrain[x][y] = map(noise(cos(x * angle) * 3, sin(y * angle) * 3 ), 0, 1, 0, maximum)

the function cos(x * angle) * 3 look like this:


I changed the amplitude but the idea is the same: you have a symmetry around the 500 value.

This means that for all y,
terrain[x][y] = terrain[1000-x][y]

Now if you consider the following set of points
x1 = r * cos(x * angle)
y1 = r * sin(x * angle)

x2 = r * cos((1000 -x) * angle)
y2 = r * sin((1000 -x) * angle)

(x1, y1) is a point of a circle
(x2, y2) is a point on the same circle but opposed to it

And both of those points have the same value -> thus the symmetry you encounter.

Hope it’s clear :crazy_face:

EDIT:
To solve your problem you would need to generate a 3D noise.
This way you can use the x-y axis to get your circles loop on themself and use the remaining z axis to generate different circles.


#7

@kfrajer : Yes, I do want to avoid the symmetry while having the edges connected.

@tony : Thanks, I’ve tried interpolation before but found that it doesn’t look good with noise, you kind of see there’s something off with the end of the circle’s motion. I also feel it’s more a twisted workaround than a real solution to the issue.

@jb4x : I realize my question was not really clear. I do know why I have a symmetrical amplitude because I “noised” the sinus and cosinus of every point on purpose. The “1000” you’re mentionning is nothing but the number of columns (n_cols). It is the only way I found to connect the first and last vertices.

Regarding your suggestion, and if I’m not mistaken, generating a 3D noise would mean that I’m also noising the circles (x ans y coordinates) which I do not want. Only the height should be noised.


#8

How about generating a 2D plane with perlin noise (like a terrain map) and then just use the values at the points of your circles?

You would have your seamless circle but no symmetry.


#9

This is what I did in the first place (the first noise variable in the sketch above, line 26): the z-value is created from noising the x and y cooridnates. But on a 2D plane, the first and last vertices are not matching at all.


#10

The following code should work.

void setup() {
  size(500, 500, P3D);
  background(20);

  float n_points = 200.0;
  float angleStep = 2 * PI / n_points;

  translate(width /2.0, height / 2.0, 0);
  rotateX(radians(50));

  noFill();
  strokeWeight(1);
  stroke(255);

  for (int j = 150; j < 200; j+=10) {
    beginShape(LINES);
    for (int i = 0; i < 1000; i++) {
      float curX = j * cos(i * angleStep);
      float curY = j * sin(i * angleStep);
      vertex(curX, curY, 40 * noise(curX, curY));

      curX = j * cos((i + 1) * angleStep);
      curY = j * sin((i + 1) * angleStep);
      vertex(curX, curY, 40 * noise(curX, curY));
    }
    endShape(CLOSE);
  }
}

Otherwise I noticed a typo in yours on line 39:
lines.vertex(lx1, ly1, terrain[x-1][y])

It should be:
lines.vertex(lx1, ly1, terrain[x-1][y-1])


#11

You realize your sketch example gives symmetrical noise, right ?

Nope, this would draw diagonals (lines between 2 circles, y and y-1).


#12

Ho yep, I mixed it up in my head… Forgot that your y was your row value.

Actually no :sweat_smile: I used another transformation to try out and I was not paying attention enough.

I figured out why there is that symmetry though…
It comes from the noise function. Turns out that the function is symmetric so noise(-x) = noise(x)
Try to give it an offset of your biggest radius and it should work.

I added an offset and now I can’t see any symmetry.
I’ll let you double check to be sure :grin:

void setup() {
  size(1000, 1000, P3D);
  background(20);

  float n_points = 200.0;
  float angleStep = 2 * PI / n_points;

  translate(width /2.0 - 200, height / 2.0 - 200, 0);
  rotateX(radians(50));

  noFill();
  strokeWeight(1);
  stroke(255);

  int j=300;
  //for (int j = 150; j < 200; j+=10) {
  beginShape(LINES);
  for (int i = 0; i < 1000; i++) {
    float curX = j * cos(i * angleStep) + 200;
    float curY = j * sin(i * angleStep) + 200;
    vertex(curX, curY, 40 * noise(curX, curY));

    curX = j * cos((i + 1) * angleStep) + 200;
    curY = j * sin((i + 1) * angleStep) + 200;
    vertex(curX, curY, 40 * noise(curX, curY));
    //}
    endShape(CLOSE);
  }
}

#13

Oh now I see where my mistake was: the x and y coordinates in the noise variable were incorrect. While your answer is not quite right it did helped me spotting that oversight. Thank you.

  • You added an abitrary and unnecesseraly high offset to the wrong variables (the lines coordinates). That offset value should be put inside the noise function (offset = 1 is enough)
  • The lines coordinates inside the noise function should also be multiplied by a very small factor (.003 for instance)

Working example sketch

add_library('peasycam')

n_points, radius, step = 200, 350, 2
factor, offset = .003, 1

def setup():
    global lines
    size(1800, 800, P3D)
    background(0)
    smooth(8)
    
    cam = PeasyCam(this, 1400)
    n_cols, n_rows = 1000, 80
    angle = radians(360) / n_cols

    lines = createShape()
    for y in range(n_rows):
        lines.beginShape(LINES) 
        lines.strokeWeight(1)
        lines.noFill()
        lines.stroke(255)
        for x in range(n_cols+1): 
            lx1 = cos(angle * (x-1) ) * (radius + (y*step)) 
            ly1 = sin(angle * (x-1) ) * (radius + (y*step)) 
            lines.vertex(lx1, ly1, noise(lx1 * factor + offset, ly1 * factor + offset) * 400)
            
            lx2 = cos(angle * x ) * (radius + (y*step)) 
            ly2 = sin(angle * x ) * (radius + (y*step)) 
            lines.vertex(lx2, ly2, noise(lx2 * factor + offset, ly2 * factor + offset) * 400)
        
        lines.endShape(CLOSE)         
       
def draw():
    background(0)
    shape(lines)

Thank you for your time and commitment.