Skip to content

Instantly share code, notes, and snippets.

@FadedWeiss
Created December 19, 2023 15:10
Show Gist options
  • Save FadedWeiss/071681f3a036fc168c16c6c6909d47d2 to your computer and use it in GitHub Desktop.
Save FadedWeiss/071681f3a036fc168c16c6c6909d47d2 to your computer and use it in GitHub Desktop.
Terrain coloring without a shader
<script async src="https://ga.jspm.io/npm:[email protected]/dist/es-module-shims.js" crossorigin="anonymous"></script>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/[email protected]/build/three.module.js",
"three/addons/": "https://unpkg.com/[email protected]/examples/jsm/"
}
}
</script>
// https://discourse.threejs.org/t/...
import * as THREE from "three";
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
import { SimplexNoise } from "three/addons/math/SimplexNoise.js";
// general setup, boring, skip to the next comment
console.clear( );
var scene = new THREE.Scene();
scene.background = new THREE.Color( 'gainsboro' );
var camera = new THREE.PerspectiveCamera( 30, innerWidth/innerHeight );
camera.position.set( 0, 5, 8 );
camera.lookAt( scene.position );
var renderer = new THREE.WebGLRenderer( {antialias: true} );
renderer.setSize( innerWidth, innerHeight );
renderer.setAnimationLoop( animationLoop );
document.body.appendChild( renderer.domElement );
window.addEventListener( "resize", (event) => {
camera.aspect = innerWidth/innerHeight;
camera.updateProjectionMatrix( );
renderer.setSize( innerWidth, innerHeight );
});
var controls = new OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
controls.autoRotate = true;
var light = new THREE.DirectionalLight( 'white', 3 );
light.position.set( 0, 1, 0 );
scene.add( light );
// next comment
// create an elevation texture
var canvas = document.createElement( 'CANVAS' );
canvas.width = 16;
canvas.height = 128;
var context = canvas.getContext( '2d' ),
gradient = context.createLinearGradient( 0, 0, 0, canvas.height );
gradient.addColorStop( 0.200, 'black' );
gradient.addColorStop( 0.330, 'blue' );
gradient.addColorStop( 0.345, 'royalblue' );
gradient.addColorStop( 0.350, 'yellow' );
gradient.addColorStop( 0.351, 'forestgreen' );
gradient.addColorStop( 0.700, 'seagreen' );
gradient.addColorStop( 0.800, 'darkseagreen' );
gradient.addColorStop( 0.900, 'white' );
context.fillStyle = gradient;
context.fillRect( 0, 0, canvas.width, canvas.height );
var elevationTexture = new THREE.CanvasTexture( canvas );
// some terrain with overlapping simlpex noise
const NX = 300, // granularity
NY = 200;
var geometry = new THREE.PlaneGeometry( 6, 4, NX, NY ),
pos = geometry.getAttribute( 'position' ),
nor = geometry.getAttribute( 'normal' ),
uv = geometry.getAttribute( 'uv' );
var terrain = new THREE.Mesh(
geometry,
new THREE.MeshPhysicalMaterial( {
roughness: 1,
metalness: 0,
side: THREE.DoubleSide,
map: elevationTexture,
} )
);
terrain.rotation.x = -Math.PI/2;
scene.add( terrain );
function randomTerrain( )
{
var simplex = new SimplexNoise( );
// updat all vertices - Z coordinate and texture coordinate
for( var i=0; i<pos.count; i++ )
{
// vertex coordinates
var x = pos.getX( i ),
y = pos.getY( i );
// calculate elevation
var z = 0.4*simplex.noise( 0.2*x, 0.2*y ) +
0.2*simplex.noise( 0.5*x, 0.5*y ) +
0.1*simplex.noise( 1.5*x, 1.5*y );
// make mountains more spiky, undwewater more smooth
if( z>0 ) z = 4*z**2; else z = -(Math.abs(z)**2);
// add tiny shallow water effect
z = z + 0.003*simplex.noise( 50*x, 50*y );
// shape vertical borders
if( Math.abs(x)==3 || Math.abs(y)==2 )
z = -0.5;
else
if( Math.abs(x)>=3*(1-2/NX) || Math.abs(y)>=2*(1-2/NY) )
z = 0;
pos.setZ( i, z );
uv.setXY( i, 0, THREE.MathUtils.mapLinear( z, -0.5,1,1,0) );
} // for i
// make sides black
geometry.computeVertexNormals( );
for( var i=0; i<nor.count; i++ )
{
var x = pos.getX( i ),
y = pos.getY( i );
if( Math.abs(x)>=3*(1-2/NX) || Math.abs(y)>=2*(1-2/NY) )
nor.setXYZ( i, 0, 0, 0 );
}
// register all updates
pos.needsUpdate = true;
nor.needsUpdate = true;
uv.needsUpdate = true;
} // randomTerrain
// that's all folks
// now generate some terrain
randomTerrain( );
// and keep regenerating it ...
setInterval( randomTerrain, 2000 );
function animationLoop( t )
{
controls.update( );
renderer.render( scene, camera );
}
body {
overflow: hidden;
margin: 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment