Applying noise to a circular grid

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)

I did some googling and ran across these:

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).

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

Kf

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.

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.

1 Like

@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.

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.

1 Like

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.

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])

You realize your sketch example gives symmetrical noise, right ?

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

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);
  }
}
2 Likes

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.


4 Likes