Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗A real-time, explorable 3D biome engine built on Three.js (r160, WebGL2). It ships with seventeen complete, procedurally built worlds: the Amazon rainforest, tropical Sea islands, Ancient Egypt on the Nile, a frost-bitten Arctic archipelago, an erupting Volcanic valley, a divine Haven of floating islands above a sea of clouds, an Ancient Temperate Forest, a Boreal Taiga, a Savanna, a Canyon and its Badlands, Alpine Highlands, a fog-bound Marsh, an Underwater Kingdom, Subterranean Caverns, a walled Village in its farmland, the Ruins of a lost civilization, and an Urban and Industrial district. Every world runs a dynamic surface and environmental state layer: weather, fire and the Touch tools leave the ground wet, snowed on, frozen, scorched or trampled, and fire spreads with the wind. New biomes plug in as packages for open worlds, games and apps. Every world runs on to the horizon and keeps the viewer inside its playable area.
Run it: serve the folder with any static host (GitHub Pages, Vercel, Netlify, or npx serve locally) and open
index.html. There is no build step; three.js loads from jsDelivr through an import map. Opening the file directly
from disk also works, but browsers refuse local module imports there, so the surface layer (fire, impacts, footprints in
the state grid) stays inert and only the weather-driven wet and snow cover is shown.
| Amazon (aerial, dawn) | Amazon (river, lily pads and lianas) |
|---|---|
| Amazon (understory) | Egypt (Nile, temple, pyramids) |
| Egypt (crop fields, mud-brick village) | Egypt (stepped limestone pyramids) |
| Sea (islands at dusk) | Sea (reef and shoreline foam) |
| Sea (storm, rain with volumetric cloud deck) | Arctic (tidewater glacier and icebergs) |
| Arctic (aurora at night) | Arctic (cabin, inukshuks, whale skeleton) |
| Volcanic (lava river and ash plume) | Volcanic (night) |
| Volcanic (valley from above) | Haven (sky bridge to the sanctuary) |
| Haven (tholos, halo, blossom groves) | Haven (sea of clouds) |
| Forest (autumn stream at dusk) | Forest (elder oak with root buttresses) |
| Forest (winter) | Taiga (frozen lake at dawn) |
| Taiga (blizzard) | Taiga (cracked and broken lake ice, footprints) |
| Savanna (controlled burn at dusk) | Savanna (wildfire front at night) |
| Savanna (zebra herd) | Canyon (from the rim at golden hour) |
| Canyon (haboob dust storm) | Canyon (cliff ledge trail) |
| Alpine (meadow, rope bridge, glacier) | Alpine (avalanche) |
| Alpine (storm: rain below the freezing level) | Marsh (boardwalk through the cypress at dusk) |
| Marsh (the bird hide at night) | Marsh (rain rings on the lily pool, water risen) |
| Underwater (sunken city under sun shafts) | Underwater (kelp forest and Snell's window) |
| Underwater (black smoker in the trench, diver's lamp) | Caverns (Great Hall: sinkhole shaft and waterfall) |
| Caverns (Old Workings shrine by torchlight, glowing fungus) | Caverns (Force strike: stalactites falling in the lamp beam) |
| Village (walled village over the strip fields, dusk) | Village (market square at midday) |
| Village (inside the tavern at night) | Ruins (overgrown pyramid and the dig at dusk) |
| Ruins (the west colonnade toppling after a Force strike) | Ruins (restored: the city as built, from the causeway) |
| Urban (rail viaduct at dusk, towers beyond) | Urban (substation tripped: a district blacked out at night) |
| Urban (rain at night: a downed line electrifying the puddles) | |
| Input | Action |
|---|---|
| WASD / arrows | Move |
| E / Space, Q / C | Rise, sink |
| Shift | Move fast |
| Drag | Look |
| Double-click | Mouse lock |
| 1–9, 0, −, = | Switch biome (the first twelve); [ and ] step through all of them, or use the Biome menu |
| Touch + click, or F | Strike the ground (fire, frost, water, storm, grow, force) where you click, or at the screen centre |
| H | Hide controls |
| Touch joystick | Move on phones |
The HUD also switches time of day (dawn, noon, dusk, night), weather (clear, rain with lightning, fog)
and quality (Fast, Balanced, Ultra). Phones start on Fast. Some worlds add their own controls (the Forest's seasons).
The line under the title reports what the world is doing (temperature, fire, canopy, wind, ice, climbing…).
Deep links: index.html#amazon, #sea, #egypt, #arctic, #volcano, #haven, #forest, #taiga, #savanna,
#canyon, #alpine, #marsh, #underwater, #caverns, #village, #ruins, #urban.
All code lives in index.html, grouped into these sections:
Sky.js) that also feeds image-based lighting through PMREM.post:{ bloom, thresh, shafts, ao, aoR, sat, contrast, warm, vig, tint } in its definition, or live with active.post.clouds: { cover, base, top, cirrus, dark }; rain thickens and darkens the deck, fog hides it, and the planar water
reflection gets the same sky at half the march steps. The slab is marched from whichever side the viewer is on,
so a biome can put it below the camera (the Haven's sea of clouds).precip (a world can swap it at runtime, e.g. rain below the freezing level and snow above). fog.storm and
fog.stormTint turn the Rain weather into a blizzard or a dust storm.extensions/surface-state): every world runs a deterministic, sparse
state grid (3 m cells, 10 Hz) holding wetness, snow, ice, temperature, burn, scorch, integrity, growth, charge and
more. The engine feeds it the HUD weather, the world's climate (air temperature with a lapse rate by altitude) and a
veering prevailing wind; untouched ground follows an ambient field, so cells only exist where something happened.
Fire spreads cell to cell through dry fuel, faster downwind, and dies out as the fuel runs out or rain falls. The
cells around the viewer are mirrored into a 128×128 texture window that the terrain, ground cover, trees and
impostors read: ground goes rain-dark and glossy, snow settles where the air is freezing, frost glazes it, fire
leaves char and glowing embers, plants flatten when trampled and burn away, canopies are stripped and trunks charred.
Live fire is drawn as flames, drifting smoke and flickering firelight. The Touch tools strike the layer with fire,
frost, water, storm (with a lightning bolt), growth or force; worlds react through .Beyond the fields shown below, a world can declare climate: { temp, lapse, night }, surface: { cellSize, humidity, spread, amb }, options (extra HUD buttons, delivered to setOption) and windy, and its build can return
materialAt(x, z) (surface material per cell: soil, sand, stone, vegetation, organic, wood, snow, ice, water…),
status(env) (the line under the title), onImpact(element, point, radius), onSurfaceEvent(event),
locomote(S, forward, right, dt, keys) (take over movement, e.g. climbing), and live precip, fogK, fogTint,
windK and tempOffset properties, floorAt(x, z, y) / ceilingAt(x, z, y) for layered worlds, and live lightK,
envK, envTint, hemiTint and exposureK to dim daylight underground or under water.
Helios.registerBiome({
id: 'tundra', label: 'Tundra',
sky: { turbidity: 4, rayleigh: 1.5, mie: 0.005, mieG: 0.8 },
fog: { density: 0.0012, tint: [0.9, 0.95, 1.0] },
ground: [0.2, 0.22, 0.18], // hemisphere bounce colour
camera: { pos: [0, 12, 0], look: [0, 8, -200] }, // or a function returning that
clouds: { cover: 0.4, base: 1500, top: 3000, cirrus: 0.3 }, // volumetric sky for this biome
precip: { kind: 'snow', base: 0.1, storm: 0.9, rainLines: false }, // optional snow / ash / motes
bounds: [0, 0, 950], // viewer stays within 950 m of the centre
build(k) {
const heightAt = (x, z) => 20 * k.noise.fbm(x * 0.003, z * 0.003, 5);
const group = new k.THREE.Group();
group.add(k.makeTerrain({ size: 2000, seg: 400, height: heightAt, apron: { size: 9000 },
detail: { rock: [0.4, 0.37, 0.33], rockLo: 0.3, bump: 14, wet: 0.5 }, // optional: ripple, rows, foam, caustic
color: (c, x, y, z, ny) => k.srgb(c, 0.3, 0.32, 0.25) }));
group.add(k.makeWater({ size: 2000, color: 0x123040 }));
// Define your own species (or reuse canopyA, canopyB, kapok, cecropia,
// shrub, island, datePalm, coconut, acai)
k.defineSpecies('spruce', { seed: 1, bark: 0x6b5a4a,
leaves: () => k.sprayAtlas(2, { hue: 0.33, sat: 0.4, light: 0.2, len: [30, 50], width: 0.12, count: [30, 40], twig: '#3a2e20' }),
tree: { h: 30, r: 0.5, buttress: 0, lean: 0.03, wobble: 0.3, trunkTop: 1.0, bStart: 0.2, branches: 18,
el: [-0.2, 0.2], bLen: 0.12, curl: 0.05, subs: 1, cR: 1.6, flatY: 0.6, cards: 18, card: 1.4, droop: 0.4 } });
const F = k.forest();
for (let i = 0; i < 5000; i++) {
const x = (Math.random() - 0.5) * 1800, z = (Math.random() - 0.5) * 1800;
F.add('spruce', x, heightAt(x, z), z, 0.8 + Math.random() * 0.4, Math.random() * 6.28);
}
group.add(F.build());
// Streaming ground cover: place() returns { y, s, c? } or null for each candidate position
const tuft = k.bladeClump({ blades: 8, seg: 3, h: [0.3, 0.7], w: 0.035, spread: 0.2, bend: 0.6,
colA: k.lin(0.1, 0.2, 0.06), colB: k.lin(0.4, 0.55, 0.2), seed: 1 });
const grass = k.scatter({ geo: tuft, mat: k.plantMat({ wind: 0.05 }), radius: 70, per: 0.8,
place: (x, z, r) => ({ y: heightAt(x, z), s: 0.7 + r() * 0.8 }) });
group.add(grass.group);
return { group, heightAt, update(dt, env) {} }; // scatter tiles follow the viewer automatically
}
});
The biome appears in the HUD automatically and inherits time of day, weather, quality levels, LOD, impostors, volumetric clouds and the walk floor.
tools/render-harness renders views headlessly, using SwiftShader (software WebGL), so changes can be
checked without a GPU:
cd tools/render-harness
npm install
node shot.mjs ../../index.html out/check \
'[{"biome":"amazon","q":"balanced"},{"cam":[60,75,250,0,-0.25]},{"biome":"egypt"},{"biome":"sea"}]' 960 540
Each job saves a JPEG and prints the triangle count and draw calls. A job accepts biome, q, tod,
wx (clear, rain, fog), cam ([x, y, z, yaw, pitch]), opt (["season", "winter"]), strike
([["fire", x, z, radius, intensity], ...]), sim (seconds of weather, surface and wildlife to advance before the
frame), post (merged into the world's post settings; {"debug":1}, 2 or 3 shows the bloom, AO or shaft
buffer) and steps. The engine exposes window.__helios (load, setQuality, snap, stats, post, sim, strike,
option, status) only for this harness. PORT sets the harness server port.
The surface package has its own tests: cd extensions/surface-state && npm test (Node 18+).
index.html The engine and the seventeen biomes (single file, no build)
extensions/surface-state/ Dynamic Surface & Environmental State layer (ES modules, zero dependencies, tests)
docs/renders/ Reference frames from the render harness
tools/render-harness/ Headless screenshot tool (puppeteer + SwiftShader)
legacy/ The original GLSL ES 3.00 raymarched version this project grew from
window.__helios.post({ bloom: 0, ao: 0 }) now writes to a separate harness
layer. For every supported postprocessing control, precedence is harness
override → live world setting → biome definition → engine default. Zero is
an explicit value. Live updates cannot replace it, and biome definitions are
never mutated. The quality preset still controls whether an effect is available
(for example, AO remains unavailable at a quality level with zero AO samples).
Calls merge partial overrides. post({ bloom: null }) (or undefined) releases
that control; post(null) clears the layer. Use tint: [1, 1, 1] for a neutral
tint. Loading or reloading a biome clears the layer, and shot.mjs clears it
before each job, including jobs that reuse a biome. Apply overrides after load.
postState() returns detached overrides, resolved effective settings, and
applied settings from the most recent rendered frame for verification.
Regression checks:
node --test tools/render-harness/tests/*.test.mjs extensions/surface-state/tests/*.test.mjs
cd tools/render-harness
npm ci
npm run verify:post
The browser check uses Chromium/SwiftShader at 640×360, balanced quality. It
checks all 17 environments, exercises every supported control after simulation
updates, verifies reload isolation and default restoration, and compares frozen
scenes with bloom and AO individually disabled/enabled. Results and Caverns /
Underwater PNG comparisons are written to docs/post-override-evidence/.
onImpactmakeTerrain({ apron }) adds a coarse ring sampled from the same height and colour functions
(optionally lifted, e.g. into a distant canopy) out to the horizon; bounds, floor and ceiling keep the
viewer inside the playable area.terrainMaterial): the ground texture is sampled triplanar, so cliffs do not smear; world-space noise breaks up vertex colour at three scales, slopes blend to
banded strata rock, shorelines go wet and glossy, and per-biome options add wind-rippled sand, crop rows,
animated shoreline foam, underwater caustics, snow glints, glowing lava-crust cracks and glacier ice
(selected by a per-vertex kind); band adds per-pixel rock banding on steep faces. The bump is an analytic finite-difference normal
perturbation with each octave faded by distance (no screen-derivative blockiness).scatter): instanced tiles are generated around the viewer from a deterministic place()
callback and dropped behind it, so density is not limited by what the world can hold up front. Plants are real
geometry (bladeClump with seed heads, papyrusClump, fernPlant with leaflet pairs, broadleafPlant,
lianaGeo, lilyGeo, coral, boulders, logs) with wind sway, backlit translucency and a distance shrink fade.flock): birds and fish fly as boids (separation, alignment, cohesion) around wandering home goals,
shy of the viewer and scattered by strikes.floorAt(x, z, y) and ceilingAt(x, z, y) give the walk floor and roof
for the viewer's height (boardwalks, upper floors, rooftops, tunnels, cave levels), and lightK, envK, envTint,
hemiTint and exposureK dim daylight, sky light and exposure where the sky does not reach (caves, the deep sea).Water.js on Balanced and Ultra; a cheap normal-mapped PBR surface on Fast.genTree(spec, seed): procedural broadleaf trees with buttressed trunks, branches, sub-branches and
leaf-spray cards that radiate from the twigs, using spherical crown normals and baked occlusion.genPalm(spec, seed): palms with curved trunks, a leaf-base boot and V-folded, drooping pinnate fronds.sprayAtlas() / frondTexture(): leaf textures painted leaf by leaf (midribs, veins, fold shading),
uploaded as DataTextures so mipmaps have no dark fringes.Helios.registerBiome({...}).