Terrain
Whole worlds from a seed for a three.js game seen from the air: volcanic coasts, islands, snowy peaks, canyons and dunes, with real water, clouds, a sky and heights you can trust.
It's ZyFou's procedural-terrains library made into one piece you add to a scene, with Patch's sky, haze, worlds and height reading, and a load about three times faster. It's for islands, flying, strategy and far-away land. It isn't first-person ground: up close the land is smooth and blurry, with no grass, rocks or trees, so don't build a walking game on it alone.
Use it in a game
Copy world.js, looks.js, sky.js and vendor/procedural-terrains/ into the game, with three@0.185.1 and fflate:
The library's built files are in vendor/procedural-terrains/dist, so there's nothing to fetch. Clamp your first dt to 0, because world.update throws on a negative one, and don't use top-level await in your main file. It also ran unchanged on three 0.186.1 beside other blocks.
"dependencies": { "three": "0.185.1", "fflate": "0.8.3", "procedural-terrains": "file:./vendor/procedural-terrains" }
import * as THREE from 'three';
import { createWorld } from './world.js';
const renderer = new THREE.WebGLRenderer({ canvas });
const scene = new THREE.Scene();
const camera = new THREE.PerspectiveCamera(46, innerWidth / innerHeight, 2, 30000);
camera.position.set(700, 400, 700);
camera.lookAt(0, 90, 0);
const world = await createWorld({ renderer, camera, look: 'volcano', seed: 7 });
scene.add(world.object);
function tick(dt) {
world.update(dt, camera);
world.render(scene, camera); // draws the whole frame, your scene included; don't call renderer.render too
}
Show a loading screen for it: a first load takes about 7 s on a fast PC, nearly all of it the browser building shaders (onProgress(value, words) tells you how far). The next visit takes about a second. It needs WebGL2.
Change the world:
world.setLook('alpine'): a whole world at once.LOOKSinlooks.jshasvolcano,islands,alpine,canyon,dunesandhighlands, as plain numbers: add your own.world.setSeed(1234): new land in about 10 ms. The same seed always makes the same land.world.set({ relief, sea, cloudCover, sunHeight, sunTurn }): any of these.
Each returns a promise for when it's drawn, and quick repeats are merged, so a slider can call them freely.
Put things on the land:
world.heightAt(x, z)is the height of the ground as drawn (typically 0.1 off, 2.3 at worst in my test), ornulloff the board. The first call after a change reads the whole world back (about 45 ms), then it's instant: read it once after a change, not during one.world.highestNear(x, z, radius)is cheap enough for a camera that has to stay above the land. Peaks can reach about 900 units, more with relief turned up, so check. The demo'sliftOverLandshows how.world.seaLevelis where the water is.world.sizeis the board's width, 4,096 units by default;createWorld({ chunks })changes it at 128 units a chunk.- Don't use the library's own
terrain.sampleHeight: it's an estimate, up to 54 units off the ground it draws here.
Things to know:
- Keep the camera inside the board, looking across it. The world is a square that ends: the haze hides the far side, but from outside you'd see its edge and a black slab beneath. The demo's camera limits stay inside.
- Your objects don't get its light. The land and water use their own sun and colours, and ignore scene lights and tone mapping. Light your props to match
world.sunDirection.createWorldalso sets the renderer's output to linear with no tone mapping, because the library's shaders write finished colours. - Only one thing can own the frame.
world.renderdraws everything through its own passes for the water and clouds. It won't combine with the Ocean block, and post effects needTerrainPassfromprocedural-terrains/renderin place of aRenderPass. - three.js must be 0.185.x. The library asks for
~0.185.1. - Trees aren't included. The library's vegetation is studio-only. Plant your own with
heightAt: the Tree generator or Low-poly trees blocks fit. From this height, thousands of small ones are what you'd want. - Weak graphics cards: the demo times a few frames and drops to fewer pixels and lighter clouds. Its frames were tested on an RTX 4090 only, so test yours on what your players have.
How it loads so much faster
Worth keeping if you change vendor/. Three changes took a cold start from about 24 s to about 7 s, with water and clouds on:
- Linear output. With the renderer on sRGB, three compiled each big shader twice: once for the canvas and again for the target it's really drawn to.
- Water reads the land from a texture. Each water shader carried a whole copy of the land's noise, about 8 of its 9 s.
createHeightCachebakes the land once, andVENDOR.mdhas the water's shader read only that. - No waiting on anything else. There are no textures: the land's look is all in the shaders.
Credits and licence
MIT: Patch's files, and ZyFou's library with its author's LICENSE unchanged in vendor/procedural-terrains/. CREDITS.txt lists everyone, LICENCE-NOTES.md has the details, and VENDOR.md lists what Patch changed in the library.