Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save christophermoverton/729cb8a7b4a82f9aee4c596e41adada3 to your computer and use it in GitHub Desktop.
Save christophermoverton/729cb8a7b4a82f9aee4c596e41adada3 to your computer and use it in GitHub Desktop.
Procedural terrain rendering
// Simple three.js example
var mesh, renderer, scene, camera, controls,texture;
var worldWidth = 400, worldDepth = 400,
worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2, tcount=0, objs=[];
init();
animate();
var clock = new THREE.Clock();
function init() {
// renderer
renderer = new THREE.WebGLRenderer();
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );
// scene
scene = new THREE.Scene();
// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 10000 );
camera.position.set( 20, 20, 20 );
// controls
controls = new THREE.OrbitControls( camera );
scene.background = new THREE.Color( 0xbfd1e5 );
// ambient light
scene.add( new THREE.AmbientLight( 0x222222 ) );
// directional light
var light = new THREE.DirectionalLight( 0xffffff, 1 );
light.position.set( 20, 20, 0 );
scene.add( light );
// axes
scene.add( new THREE.AxesHelper( 20 ) );
/*
// geometry
var geometry = new THREE.SphereGeometry( 5, 12, 8 );
// material
var material = new THREE.MeshPhongMaterial( {
color: 0x00ffff,
flatShading: true,
transparent: true,
opacity: 0.7,
} );
// mesh
mesh = new THREE.Mesh( geometry, material );
*/
var data = generateHeight( worldWidth, worldDepth, 0, 0);
var data2 = generateHeight( worldWidth, worldDepth, 1, 0);
camera.position.y = data[ worldHalfWidth + worldHalfDepth * worldWidth ] * 10 + 3100;
var geometry = new THREE.PlaneBufferGeometry( 7500, 7500, worldWidth - 1, worldDepth - 1 );
var geometry2 = new THREE.PlaneBufferGeometry( 7500, 7500, worldWidth - 1, worldDepth - 1 );
geometry.rotateX( - Math.PI / 2 );
geometry2.rotateX( - Math.PI / 2 );
geometry2.translate(7500,0,0);
geometry.attributes.position.needsUpdate=true;
geometry2.attributes.position.needsUpdate=true;
var vertices = geometry.attributes.position.array;
var vertices2 = geometry2.attributes.position.array;
for ( var i = 0, j = 0, l = vertices.length; i < l; i ++, j += 3 ) {
vertices[ j + 1 ] = data[ i ] * 10;
vertices2[ j + 1 ] = data2[ i ] * 10;
}
texture = new THREE.CanvasTexture( generateTexture( data, worldWidth, worldDepth ) );
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) );
mesh.material.needsUpdate=true;
objs.push({mesh:mesh,trx:0,tileind:0});
scene.add( mesh );
texture2 = new THREE.CanvasTexture( generateTexture( data2, worldWidth, worldDepth ) );
texture2.wrapS = THREE.ClampToEdgeWrapping;
texture2.wrapT = THREE.ClampToEdgeWrapping;
mesh2 = new THREE.Mesh( geometry2, new THREE.MeshBasicMaterial( { map: texture2 } ) );
mesh2.material.needsUpdate=true;
objs.push({mesh:mesh2,trx:7500,tileind:1});
scene.add( mesh2 );
}
function animate() {
requestAnimationFrame( animate );
renderer.render( scene, camera );
if (tcount%2==0){
let sp =-50;
let geom = objs[0].mesh.geometry.translate(sp,0,0);
objs[0].mesh.updateMatrix();
let geom2 = objs[1].mesh.geometry.translate(sp,0,0);
objs[1].mesh.updateMatrix();
objs[1].trx += sp;
if (objs[1].trx <= 0){
updateTiles();
}
// scene.remove(objs[0]);
}
tcount+=1;
}
function addNextTile(tileind){
var data = generateHeight( worldWidth, worldDepth, tileind, 0);
let geometry = objs[1].mesh.geometry;
// var geometry = new THREE.PlaneBufferGeometry( 7500, 7500, worldWidth - 1, worldDepth - 1 );
// geometry.rotateX( - Math.PI / 2 );
geometry.translate(2*7500,0,0);
objs[1].mesh.updateMatrix();
var vertices = geometry.attributes.position.array;
var vertices = geometry.attributes.position.array;
for ( var i = 0, j = 0, l = vertices.length; i < l; i ++, j += 3 ) {
vertices[ j + 1 ] = data[ i ] * 10;
}
geometry.attributes.position.needsUpdate=true;
let texture = new THREE.CanvasTexture( generateTexture( data, worldWidth, worldDepth ) );
texture.wrapS = THREE.ClampToEdgeWrapping;
texture.wrapT = THREE.ClampToEdgeWrapping;
objs[1].mesh.material.map=texture;
objs[1].mesh.material.map.needsUpdate = true;
//let mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) );
objs[1].tileind=tileind;
objs[1].trx=7500;
// objs.push({mesh:mesh,trx:7500,tileind:tileind});
//scene.add( mesh );
}
function updateTiles(){
// scene.remove(objs[0].mesh);
objs.push(objs[0]);
objs.shift();
addNextTile(objs[0].tileind+1);
}
function lerp(t, a, b) {
return a + t * (b - a);
}
function clamp(v,min, max) {
return Math.min(Math.max(v, min), max);
};
function generateHeight( width, height, trw, trh) {
var size = width * height, data = new Uint8Array( size ), data2 = new Uint8Array( size ),
perlin = new ImprovedNoise(), quality = .75, z = 0;
var b2 =1.0, q2 =.23453, q3 = 1.15, b3 = .56850, b4=.1,c1=1.50, c2=1.2;
for ( var j = 0; j < 6; j ++ ) {
for ( var i = 0; i < size; i ++ ) {
var x = (i % width)+trw*width, y = ~ ~ ( i / width ); y+=trh*height;
let t = clamp(perlin.noise(x/450,y/450,0)*c1,0,1);
let t2 = clamp(perlin.noise(x/550,y/550,0)*c2,0,1);
let h1 = Math.abs((perlin.noise( x / quality, y / quality, z )+1)* b2 *.15);
let h2 = Math.abs((perlin.noise( x / q2, y / q2, z )+1)* b3 *.0690123);
let h3 = lerp(t,h2,h1);
let h4 = Math.abs((perlin.noise( x / q3, y / q3, z )+1)* b4 *.347690123);
let h5 = lerp(t2,h3,h4);
data[ i ] += h5;
}
quality *= 2.5;
q2 *= 1.80975;
q3 *= 3.75
b2 *= 3.5;
b3 *= 2.035;
b4 *= 5.0;
}
return data;
}
function generateTexture( data, width, height ) {
var canvas, canvasScaled, context, image, imageData, vector3, sun, shade;
vector3 = new THREE.Vector3( 0, 0, 0 );
sun = new THREE.Vector3( 1, 1, 1 );
sun.normalize();
canvas = document.createElement( 'canvas' );
canvas.width = width;
canvas.height = height;
context = canvas.getContext( '2d' );
context.fillStyle = '#000';
context.fillRect( 0, 0, width, height );
image = context.getImageData( 0, 0, canvas.width, canvas.height );
imageData = image.data;
for ( var i = 0, j = 0, l = imageData.length; i < l; i += 4, j ++ ) {
vector3.x = data[ j - 2 ] - data[ j + 2 ];
vector3.y = 2;
vector3.z = data[ j - width * 2 ] - data[ j + width * 2 ];
vector3.normalize();
shade = vector3.dot( sun );
imageData[ i ] = ( 96 + shade * 128 ) * ( 0.5 + data[ j ] * 0.007 );
imageData[ i + 1 ] = ( 32 + shade * 96 ) * ( 0.5 + data[ j ] * 0.007 );
imageData[ i + 2 ] = ( shade * 96 ) * ( 0.5 + data[ j ] * 0.007 );
}
context.putImageData( image, 0, 0 );
// Scaled 4x
canvasScaled = document.createElement( 'canvas' );
canvasScaled.width = width * 4;
canvasScaled.height = height * 4;
context = canvasScaled.getContext( '2d' );
context.scale( 4, 4 );
context.drawImage( canvas, 0, 0 );
image = context.getImageData( 0, 0, canvasScaled.width, canvasScaled.height );
imageData = image.data;
// /*
for ( var i = 0, l = imageData.length; i < l; i += 4 ) {
var v = ~ ~ ( Math.random() * 5 );
imageData[ i ] += v;
imageData[ i + 1 ] += v;
imageData[ i + 2 ] += v;
}
//*/
context.putImageData( image, 0, 0 );
return canvasScaled;
}
<script src="https://threejs.org/build/three.js"></script>
<script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
<script src="https://threejs.org/examples/js/ImprovedNoise.js"></script>
<script src="https://threejs.org/examples/js/WebGL.js"></script>
<script src="https://threejs.org/examples/js/libs/stats.min.js"></script>
body {
background-color: #000;
margin: 0px;
overflow: hidden;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment