Help with generative noise landscape

Hi guys, I am building a webVR scene using A-Frame and three.js.

Right now I am trying to build a component that will generate a moving perlin noise landscape. I am using a processing script I built through this video as the basis for my three.js code. However I am having trouble getting it to look right. Instead of making a curved and hilly landscape, instead it ends up looking like this:

Here is the code I have for it. Tick is an a-frame component function that runs every frame. I’m using an imported obj file made from the mesh I made in the processing sketch:

  tick: function(time, timeDelta) {
        var el = this.el;
        if(el.getObject3D('mesh')) {
          var terrain = el.getObject3D('mesh').children[0].geometry;
          // console.log(terrain.vertices);

          // var terrain = el.getObject3D('mesh').geometry;
          var data = this.data;
          var simplex = new SimplexNoise();
          //
          //
          terrain.verticesNeedUpdate = true;
          //
          // var xoff = 0;
          // var yoff = data.flying;
          data.flying -= 0.1;
          var yoff = data.flying;
          var xoff = 0;

          //
          for (var i = 0; i < terrain.vertices.length; i++)
            {
              var v = terrain.vertices[i];
              v.z = simplex.noise2D(xoff, yoff) * 10;
              // do stuff with v...
              yoff += 0.2;
              xoff += 0.2;

            }

And here is the original processing sketch:

import nervoussystem.obj.*;
boolean record = false;

int cols, rows;
int scl = 20;
int w = 2000;
int h = 1700;

float flying = 0;

float[][] terrain;

void setup() {
  size(600, 600, P3D);
  cols = w / scl;
  rows = h / scl;
  terrain = new float[cols][rows];



}

void draw() {
  flying -= 0.1;

  float yoff = flying;
  for(int y = 0; y < rows; y++) {
    float xoff = 0;
    for(int x = 0; x < cols; x++) {
      terrain[x][y] = map(noise(xoff, yoff), 0, 1, -100, 100);
      xoff += 0.2;
    }
    yoff += 0.2;
  }

  background(0);
  stroke(255);
  noFill();

  translate(width/2, height/2+50);
  rotateX(PI/3);

  translate(-w/2, -h/2);
  if (record) {
    beginRecord("nervoussystem.obj.OBJExport", "terrain.obj");
  }
  for(int y = 0; y < rows - 1; y++) {
    beginShape(TRIANGLE_STRIP);
    for (int x = 0; x < cols; x++) {
      vertex(x * scl, y * scl, terrain[x][y]);
      vertex(x * scl, (y + 1) * scl, terrain[x][y+1]);

    }
    endShape();
  }
  if (record) {
    endRecord();
    record = false;
  }
  

}

Any help would be greatly appreciated!

You only increment the offset for the noise (offx, offy) per vertex equally, you need to use the vertex position and scale this position down as noise usually utilizes 0-1, what should already suffice for 65536 units in a average world scale to get cartoony hills, for more complex noise you can use a FBM function to use multiple octaves.

1 Like

Hmm ok, so it’s an issue with how i’m using the xoff/yoff variables? Or convert the values I’m getting from the noise function into a wider range of values? I think maybe I just am having hard time conceptualizing how it works ):

Like I need to be mapping the noise from 0-1 to say -100, 100?

for (var i = 0; i < terrain.vertices.length; i++)
{
    var v = terrain.vertices[i];
    v.z = simplex.noise2D(v.x / 1024.0, v.y / 1024.0) * 10;
}

If the plane of the terrain is XY and you create this plane with a width and height of 1024 for example, then you can try this. You can imagine the noise as a texture you sample from, which is also represented by UV or XY coordinates from 0.0 to 1.0. Too high values will cause a high frequent texture / many repetitions. Like a “infinite” non-existing texture were only the position you get the current value / height from will be calculated.

1 Like

You might want to check out the terrain examples of three.js:

https://threejs.org/examples/#webgl_geometry_terrain
https://threejs.org/examples/#webgl_terrain_dynamic

Both examples use noise algorithms for terrain generation.

From the examples @Mugen87 gave you, you will be able to see 2 things that could help you debug this case scenario:

  • Both examples use a plane as base geometry to work on. The mesh you are using may have translation bug in it and using a plane geometry will remove this potential problem (+ the code will perform better).

  • To render a procedural terrain with noise in three.js, one just update the y axis from each vertex position. If you look at both examples, you will see the world width and depth (x & z) are passed to the noise function which returns a weighted y offset (relative to its surrounding points). i.e.:

vertices[ j + 1 ] = data[ i ] * 10;

where j + 1 is the y coordinate in buffergeometry.

GL & HF

Thanks so much! @Fyrestar that fixed it for me, now it generates a new landscape every time it renders :slight_smile:

1 Like