mirror of
https://github.com/wahyd4/three.js.git
synced 2026-08-25 04:46:02 +10:00
Code is still rough around edges (it can be made nicer), though it should already produce one-to-one results with ubershader. Side-effect is that scene is no longer required as WebGLRenderer constructor parameter. Shader programs are now constructed upon first frame render and only then scene lights are examined.
270 lines
6.6 KiB
HTML
270 lines
6.6 KiB
HTML
<!DOCTYPE HTML>
|
|
<html lang="en">
|
|
<head>
|
|
<title>three.js - geometry - webgl terrain</title>
|
|
<meta charset="utf-8">
|
|
<style type="text/css">
|
|
body {
|
|
color: #61443e;
|
|
font-family:Monospace;
|
|
font-size:13px;
|
|
text-align:center;
|
|
|
|
background-color: #bfd1e5;
|
|
margin: 0px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#info {
|
|
position: absolute;
|
|
top: 0px; width: 100%;
|
|
padding: 5px;
|
|
}
|
|
|
|
a {
|
|
|
|
color: #a06851;
|
|
}
|
|
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="container"><br /><br /><br /><br /><br />Generating world...</div>
|
|
<div id="info"><a href="http://github.com/mrdoob/three.js" target="_blank">three.js</a> - webgl terrain demo<br />(left click: forward, right click: backward)</div>
|
|
|
|
<script type="text/javascript" src="js/Stats.js"></script>
|
|
<script type="text/javascript" src="js/ImprovedNoise.js"></script>
|
|
|
|
<script type="text/javascript" src="../build/Three.js"></script>
|
|
<script type="text/javascript" src="../src/extras/primitives/Plane.js"></script>
|
|
|
|
<script type="text/javascript">
|
|
|
|
var container, stats;
|
|
|
|
var camera, scene, renderer;
|
|
|
|
var mesh, texture;
|
|
|
|
var worldWidth = 256, worldDepth = 256,
|
|
worldHalfWidth = worldWidth / 2, worldHalfDepth = worldDepth / 2;
|
|
|
|
var mouseX = 0, mouseY = 0,
|
|
lat = 0, lon = 0, phy = 0, theta = 0;
|
|
|
|
var direction = new THREE.Vector3(),
|
|
moveForward = false, moveBackward = false;
|
|
|
|
var windowHalfX = window.innerWidth / 2;
|
|
var windowHalfY = window.innerHeight / 2;
|
|
|
|
|
|
init();
|
|
setInterval( loop, 1000 / 60 );
|
|
|
|
|
|
function init() {
|
|
|
|
container = document.getElementById( 'container' );
|
|
|
|
camera = new THREE.Camera( 60, window.innerWidth / window.innerHeight, 1, 20000 );
|
|
camera.target.position.z = - 100;
|
|
|
|
scene = new THREE.Scene();
|
|
|
|
data = generateHeight( worldWidth, worldDepth );
|
|
|
|
camera.position.y = data[ worldHalfWidth + worldHalfDepth * worldWidth ] + 500;
|
|
camera.target.position.y = camera.position.y;
|
|
|
|
var geometry = new Plane( 7500, 7500, worldWidth - 1, worldDepth - 1 );
|
|
|
|
for ( var i = 0, l = geometry.vertices.length; i < l; i ++ ) {
|
|
|
|
geometry.vertices[ i ].position.z = data[ i ] * 10;
|
|
|
|
}
|
|
|
|
texture = new THREE.Texture( generateTexture( data, worldWidth, worldDepth ), new THREE.UVMapping(), THREE.ClampToEdgeWrapping, THREE.ClampToEdgeWrapping );
|
|
|
|
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial( { map: texture } ) );
|
|
mesh.rotation.x = - 90 * Math.PI / 180;
|
|
scene.addObject( mesh );
|
|
|
|
renderer = new THREE.WebGLRenderer();
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
|
container.innerHTML = "";
|
|
|
|
container.appendChild( renderer.domElement );
|
|
|
|
stats = new Stats();
|
|
stats.domElement.style.position = 'absolute';
|
|
stats.domElement.style.top = '0px';
|
|
container.appendChild( stats.domElement );
|
|
|
|
document.addEventListener( 'mousedown', onDocumentMouseDown, false );
|
|
document.addEventListener( 'mouseup', onDocumentMouseUp, false );
|
|
document.addEventListener( 'mousemove', onDocumentMouseMove, false );
|
|
document.addEventListener( 'contextmenu', function ( event ) { event.preventDefault(); }, false );
|
|
|
|
}
|
|
|
|
function generateHeight( width, height ) {
|
|
|
|
var size = width * height, data = new Float32Array( size ),
|
|
perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 100;
|
|
|
|
for ( var i = 0; i < size; i ++ ) {
|
|
|
|
data[ i ] = 0
|
|
|
|
}
|
|
|
|
for ( var j = 0; j < 4; j ++ ) {
|
|
|
|
for ( var i = 0; i < size; i ++ ) {
|
|
|
|
var x = i % width, y = ~~ ( i / width );
|
|
data[ i ] += Math.abs( perlin.noise( x / quality, y / quality, z ) * quality * 1.75 );
|
|
|
|
|
|
}
|
|
|
|
quality *= 5;
|
|
|
|
}
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
function generateTexture( data, width, height ) {
|
|
|
|
var canvas, canvasScaled, context, image, imageData,
|
|
level, diff, 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;
|
|
canvasScaled.loaded = true;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
function onDocumentMouseDown( event ) {
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
switch ( event.button ) {
|
|
|
|
case 0: moveForward = true; break;
|
|
case 2: moveBackward = true; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onDocumentMouseUp( event ) {
|
|
|
|
event.preventDefault();
|
|
event.stopPropagation();
|
|
|
|
switch ( event.button ) {
|
|
|
|
case 0: moveForward = false; break;
|
|
case 2: moveBackward = false; break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onDocumentMouseMove(event) {
|
|
|
|
mouseX = event.clientX - windowHalfX;
|
|
mouseY = event.clientY - windowHalfY;
|
|
|
|
}
|
|
|
|
function loop() {
|
|
|
|
if ( moveForward ) camera.translateZ( - 15 );
|
|
if ( moveBackward ) camera.translateZ( 15 );
|
|
|
|
lon += mouseX * 0.004;
|
|
lat -= mouseY * 0.004;
|
|
|
|
lat = Math.max( - 85, Math.min( 85, lat ) );
|
|
phi = ( 90 - lat ) * Math.PI / 180;
|
|
theta = lon * Math.PI / 180;
|
|
|
|
camera.target.position.x = 100 * Math.sin( phi ) * Math.cos( theta ) + camera.position.x;
|
|
camera.target.position.y = 100 * Math.cos( phi ) + camera.position.y;
|
|
camera.target.position.z = 100 * Math.sin( phi ) * Math.sin( theta ) + camera.position.z;
|
|
|
|
renderer.render(scene, camera);
|
|
stats.update();
|
|
|
|
}
|
|
|
|
</script>
|
|
|
|
</body>
|
|
</html>
|