How can I make this world more fancy?

I want to create a procedurally generated world with a low poly style. I had this procedural world:


based on this three.js example: webgl_geometry_terrain_raycast

But I had to change it because the generation wasn’t continuous, this was the issue: what-is-the-best-approach-to-make-procedural-world-generation-in-three-js

Now after changing some values and understanding the code, this was the best thing could achieve:

The code I am using is based on the SO answer mentioned before.

Do you have any suggestions on how to achieve the mountains and low poly style like in the first photo?

Main:


        ///////////////////////////////////////////////////////////////////////////////////////////////
        // ADD WATER LAYER + SUN

        this.sun = new THREE.Vector3();


        // Skybox

        this.sky = new Sky();
        this.sky.scale.setScalar( 20000 );
        this.sky.position.set(0,10,0)
        scene.add( this.sky );
        this.sky.castShadow = true

        const skyUniforms = this.sky.material.uniforms;

        this.parameters = {
            turbidity: 10,
            rayleigh: 3,
            mieCoefficient: 0.005,
            mieDirectionalG: 0.7,
            elevation: 2,
            azimuth: -20,
            exposure: renderer.toneMappingExposure - (renderer.toneMappingExposure/3)
        };

        skyUniforms[ 'turbidity' ].value = this.parameters.turbidity;
        skyUniforms[ 'rayleigh' ].value = this.parameters.rayleigh;
        skyUniforms[ 'mieCoefficient' ].value = this.parameters.mieCoefficient;
        skyUniforms[ 'mieDirectionalG' ].value = this.parameters.mieDirectionalG;

        this.pmremGenerator = new THREE.PMREMGenerator( renderer );
        this.pmremGenerator.castShadow = true

        //here starts the terrain generation
        this.perlin = new ImprovedNoise();

        let step = 20;
        let vertical = 2
        let horizontal = 2
        //creates a grid of chunks 
        for(let cols = -vertical; cols <= vertical; cols ++){
            for(let rows = -horizontal; rows <= horizontal; rows++){
                this.createPlane(step,rows,cols);                
            }
          }           


        this.setSunLight();
       this.updateSun()

UpdateSun() and setSunLight() just initialices the sun light and sets up a spotLight respectively so they aren’t relevant. So this is the CreatePlane() function:

createPlane(step, x,z){
        //this is just the layer of water that I got from the examples on the web
        let w = this.createWater()

        //if(Math.random() < 0.1) this.experience.loaderOBJ.CreateCloud('',x*step, 70,z*step)
      
        let amplitude = 40

       
        //Sets up the material and geometry
        let m = new THREE.MeshPhongMaterial({side:THREE.BackSide, flatShading: true,vertexColors: true});

        let g = new THREE.PlaneGeometry(step, step, 15,15);

        const count = g.attributes.position.count;

        g.setAttribute( 'color', new THREE.BufferAttribute( new Float32Array( count * 3), 3 ) );
       
        //starts the generation
        this.setNoise(g, new THREE.Vector2(x, z), 0.5, amplitude);

        const color = new THREE.Color();

        const colors = g.attributes.color;

        const vertices = g.attributes.position.array;
  
        //paints the vertex depending on their height 
        for ( let i = 0, j = 0, l = vertices.length; i < l; i ++, j += 3 ) {

          if(g.attributes.position.getZ(i) < -19){

              if(Math.random() < 0.1){

                color.setHSL(0, 0, (Math.random()/5)+0.80);

                colors.setXYZ( i, color.r, color.g, color.b );

              }else{

                color.setHSL(0, 0, 1 );

                colors.setXYZ( i, color.r, color.g, color.b );

              }

          }else if(g.attributes.position.getZ(i) < -12){

              if(Math.random() < 0.1){

                color.setHSL(0, 0, (Math.random()/5)+0.80);

                colors.setXYZ( i, color.r, color.g, color.b );

              }else{

                color.setHSL(0.0, 0, 0.71 );

                colors.setXYZ( i, color.r, color.g, color.b );

              }

          }else if(g.attributes.position.getZ(i) < -0){

              color.setHSL(0.285, 1, 0.60 );

              colors.setXYZ( i, color.r, color.g, color.b );

          }else if(g.attributes.position.getZ(i) < 4.5){

              color.setHSL(0.125, 0.84, 0.67 );

              colors.setXYZ( i, color.r, color.g, color.b );

          }else if(g.attributes.position.getZ(i) > 0){

            color.setHSL(0.125, 0.84, 0.67 );

              //color.setHSL(0.59, 0.89, 0.39);

              colors.setXYZ( i, color.r, color.g, color.b );

          }

        }

       
        //here I create the the terrain mesh and add a position to the water and terrain adding too a shadow config 
        let p = new THREE.Mesh(g,m)

        p.position.set(x, 0, z).multiplyScalar(step);
        w.position.copy(p.position);
        w.position.y -= 3

        p.geometry.rotateX(Math.PI / 2)
        p.geometry.computeVertexNormals(true);

        p.castShadow = true;
        p.receiveShadow = true;

        w.castShadow = true;
        w.receiveShadow = true;

        this.experience.scene.add(p,w);

  
      }

And finally the setNoise() function:

setNoise(g, uvShift, multiplier, amplitude){

        let pos = g.attributes.position;

        let uv = g.attributes.uv;

        let vec2 = new THREE.Vector2();

        for(let i = 0; i < pos.count; i++){

        vec2.fromBufferAttribute(uv, i).add(uvShift).multiplyScalar(multiplier);

        pos.setZ(i, this.perlin.noise(vec2.x, vec2.y, 0) * amplitude);

        }

    }

You can use the same approach with calculation of noise, like in the official example of terrain: Edit fiddle - JSFiddle - Code Playground

1 Like

That was exactly what I was looking for! seriously, you are a genius, you saved me twice on the same day :rofl::rofl::rofl:

You’re welcome :handshake: :slightly_smiling_face: