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