浏览代码

Messing with a new terrain generator demo.

Mr.doob 14 年之前
父节点
当前提交
041054283a
共有 4 个文件被更改,包括 450 次插入1 次删除
  1. 0 1
      examples/geometry_terrain_gl.html
  2. 347 0
      examples/geometry_terrain_gl2.html
  3. 11 0
      examples/js/PRNG.js
  4. 92 0
      examples/js/SimplexNoise.js

+ 0 - 1
examples/geometry_terrain_gl.html

@@ -37,7 +37,6 @@
 		<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/GeometryUtils.js"></script>
 		<script type="text/javascript" src="../src/extras/primitives/Plane.js"></script>
 
 		<script type="text/javascript">

+ 347 - 0
examples/geometry_terrain_gl2.html

@@ -0,0 +1,347 @@
+<!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/PRNG.js"></script>
+		<script type="text/javascript" src="js/SimplexNoise.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 ) ] * 10 ) + 200;
+				camera.target.position.y = camera.position.y;
+
+				var geometry = new Plane( 10000, 10000, 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( { scene: scene } );
+				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 generateHeight( width, height ) {
+
+				var x, y, h, quality = 100, size = width * height, octaves = 10, offsetX = 100, offsetY = 100, data = [],
+				prng = new PRNG(), simplex1 = new SimplexNoise( prng ), simplex2 = new SimplexNoise( prng ), simplex3 = new SimplexNoise( prng );
+
+				simplex1.setSeed( Math.random() * 100 );
+				simplex2.setSeed( Math.random() * 100 );
+				simplex3.setSeed( Math.random() * 100 );
+
+				// Base Terrain
+
+				for ( var i = 0; i < size; i ++ ) {
+
+					x = ( ( i % width ) + offsetX ) / quality;
+					y = ( ~~ ( i / width ) + offsetY ) / quality;
+					h = 32 - ( Math.abs( simplex1.noise( x, y, 0 ) + simplex2.noise( x, y, 0 ) ) - simplex3.noise( x, y, 0 ) ) * 64;
+
+					data[ i ] = h;
+
+				}
+
+				// Blur
+
+				for ( var i = 0; i < size; i ++ ) {
+
+					x = i % width;
+
+					if ( x == 0 ) continue;
+					if ( x = width - 1 ); continue;
+
+					data[ i ] += ( data[ i - 1 ] + data[ i + 1 ] ) * 0.5;
+					data[ i ] *= 0.5;
+
+				}
+
+				for ( var i = width; i < size - width; i ++ ) {
+
+					data[ i ] += ( data[ i - width ] + data[ i + width ] ) * 0.5;
+					data[ i ] *= 0.5;
+
+				}
+
+				for ( var j = 4; j < 100; j *= 2 ) {
+
+					for ( var i = 0; i < size; i ++ ) {
+
+						x = ( ( i % width ) + offsetX ) / j,
+						y = ( ~~ ( i / width ) + offsetY ) / j;
+
+						data[ i ] -= Math.abs( simplex1.noise( x, y, 0 ) * simplex2.noise( x, y, 0 ) * simplex3.noise( x, y, 0 ) ) * j;
+
+					}
+
+				}
+
+				return data;
+
+			}
+
+			function generateTexture( data, width, height ) {
+
+				var canvas, canvasScaled, context, image, imageData,
+				level, diff, texel, 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 ++ ) {
+
+					texel = data[ j ];
+
+					/*
+					imageData[ i ] = texel;
+					imageData[ i + 1 ] = texel;
+					imageData[ i + 2 ] = texel;
+					continue;
+					*/
+
+					shade = 0;
+
+					for ( var k = 1; k < 16; k ++ ) {
+
+						vector3.x = data[ j - k ] - data[ j + k ];
+						vector3.y = 1;
+						vector3.z = data[ j - width * k ] - data[ j + width * k ];
+						vector3.normalize();
+
+						shade += vector3.dot( sun ) * ( 16 - k );
+
+					}
+
+					imageData[ i ] = ( 128 + shade ) + texel;
+					imageData[ i + 1 ] = ( 64 + shade ) + texel;
+					imageData[ i + 2 ] = ( 32 + shade ) + texel;
+
+				}
+
+				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( - 10 );
+				if ( moveBackward ) camera.translateZ( 10 );
+
+				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>

+ 11 - 0
examples/js/PRNG.js

@@ -0,0 +1,11 @@
+// Park-Miller-Carta Pseudo-Random Number Generator
+// https://github.com/pnitsch/BitmapData.js/blob/master/js/BitmapData.js
+
+var PRNG = function () {
+
+	this.seed = 1;
+	this.next = function() { return (this.gen() / 2147483647); };
+	this.nextRange = function(min, max)	{ return min + ((max - min) * this.next()) };
+	this.gen = function() { return this.seed = (this.seed * 16807) % 2147483647; };
+
+};

+ 92 - 0
examples/js/SimplexNoise.js

@@ -0,0 +1,92 @@
+// Ported from Stefan Gustavson's java implementation
+// http://staffwww.itn.liu.se/~stegu/simplexnoise/simplexnoise.pdf
+// Sean McCullough [email protected]
+
+var SimplexNoise = function(gen) {
+	this.rand = gen;
+	this.grad3 = [
+		[1,1,0],[-1,1,0],[1,-1,0],[-1,-1,0], 
+		[1,0,1],[-1,0,1],[1,0,-1],[-1,0,-1], 
+		[0,1,1],[0,-1,1],[0,1,-1],[0,-1,-1]
+	]; 
+	
+	this.simplex = [ 
+		[0,1,2,3],[0,1,3,2],[0,0,0,0],[0,2,3,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,2,3,0], 
+		[0,2,1,3],[0,0,0,0],[0,3,1,2],[0,3,2,1],[0,0,0,0],[0,0,0,0],[0,0,0,0],[1,3,2,0], 
+		[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], 
+		[1,2,0,3],[0,0,0,0],[1,3,0,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,3,0,1],[2,3,1,0], 
+		[1,0,2,3],[1,0,3,2],[0,0,0,0],[0,0,0,0],[0,0,0,0],[2,0,3,1],[0,0,0,0],[2,1,3,0], 
+		[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0], 
+		[2,0,1,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,0,1,2],[3,0,2,1],[0,0,0,0],[3,1,2,0], 
+		[2,1,0,3],[0,0,0,0],[0,0,0,0],[0,0,0,0],[3,1,0,2],[0,0,0,0],[3,2,0,1],[3,2,1,0]
+	]; 
+};
+
+SimplexNoise.prototype.setSeed = function(seed) {
+	this.p = [];
+	this.rand.seed = seed;
+	
+	for (var i=0; i<256; i++) {
+		this.p[i] = Math.floor(this.rand.nextRange(0, 255));
+	}
+
+	this.perm = []; 
+	for(var i=0; i<512; i++) {
+		this.perm[i]=this.p[i & 255];
+	}
+}
+
+SimplexNoise.prototype.dot = function(g, x, y) {
+	return g[0]*x + g[1]*y;
+};
+
+SimplexNoise.prototype.noise = function(xin, yin) { 
+	var n0, n1, n2; 
+
+	var F2 = 0.5*(Math.sqrt(3.0)-1.0); 
+	var s = (xin+yin)*F2; 
+	var i = Math.floor(xin+s); 
+	var j = Math.floor(yin+s); 
+	var G2 = (3.0-Math.sqrt(3.0))/6.0; 
+	var t = (i+j)*G2; 
+	var X0 = i-t; 
+	var Y0 = j-t; 
+	var x0 = xin-X0; 
+	var y0 = yin-Y0; 
+
+	var i1, j1; 
+	if(x0>y0) {i1=1; j1=0;} 
+	else {i1=0; j1=1;}      
+
+	var x1 = x0 - i1 + G2; 
+	var y1 = y0 - j1 + G2; 
+	var x2 = x0 - 1.0 + 2.0 * G2;  
+	var y2 = y0 - 1.0 + 2.0 * G2; 
+
+	var ii = i & 255; 
+	var jj = j & 255; 
+	var gi0 = this.perm[ii+this.perm[jj]] % 12; 
+	var gi1 = this.perm[ii+i1+this.perm[jj+j1]] % 12; 
+	var gi2 = this.perm[ii+1+this.perm[jj+1]] % 12; 
+
+	var t0 = 0.5 - x0*x0-y0*y0; 
+	if(t0<0) n0 = 0.0; 
+	else { 
+		t0 *= t0; 
+		n0 = t0 * t0 * this.dot(this.grad3[gi0], x0, y0);  
+	} 
+	var t1 = 0.5 - x1*x1-y1*y1; 
+	if(t1<0) n1 = 0.0; 
+	else { 
+		t1 *= t1; 
+		n1 = t1 * t1 * this.dot(this.grad3[gi1], x1, y1); 
+	}
+	var t2 = 0.5 - x2*x2-y2*y2; 
+	if(t2<0) n2 = 0.0; 
+	else { 
+		t2 *= t2; 
+		n2 = t2 * t2 * this.dot(this.grad3[gi2], x2, y2); 
+	} 
+
+	return 70.0 * (n0 + n1 + n2); 
+};