Help with generative noise landscape

geometry

#1

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!


#2

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.


#3

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


#4

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


#5
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.


#6

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.


#7

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


#8

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