瀏覽代碼

Changed water algorithm and added tennis balls

yomboprime 6 年之前
父節點
當前提交
71e474c75f
共有 1 個文件被更改,包括 145 次插入54 次删除
  1. 145 54
      examples/webgl_gpgpu_water.html

+ 145 - 54
examples/webgl_gpgpu_water.html

@@ -1,4 +1,4 @@
-<!DOCTYPE html>
+ <!DOCTYPE html>
 <html lang="en">
 	<head>
 		<title>three.js webgl - gpgpu - water</title>
@@ -57,17 +57,14 @@
 			uniform float viscosityConstant;
 			uniform float heightCompensation;
 
-			#define deltaTime ( 1.0 / 60.0 )
-			#define GRAVITY_CONSTANT ( resolution.x * deltaTime * 3.0 )
-
 			void main()	{
 
 				vec2 cellSize = 1.0 / resolution.xy;
 
 				vec2 uv = gl_FragCoord.xy * cellSize;
 
-				// heightmapValue.x == height
-				// heightmapValue.y == velocity
+				// heightmapValue.x == height from previous frame
+				// heightmapValue.y == height from penultimate frame
 				// heightmapValue.z, heightmapValue.w not used
 				vec4 heightmapValue = texture2D( heightmap, uv );
 
@@ -77,23 +74,14 @@
 				vec4 east = texture2D( heightmap, uv + vec2( cellSize.x, 0.0 ) );
 				vec4 west = texture2D( heightmap, uv + vec2( - cellSize.x, 0.0 ) );
 
-				float sump = north.x + south.x + east.x + west.x - 4.0 * heightmapValue.x;
-
-				float accel = sump * GRAVITY_CONSTANT;
-
-				// Dynamics
-				heightmapValue.y += accel;
-				heightmapValue.x += heightmapValue.y * deltaTime;
-
-				// Viscosity
-				heightmapValue.x += sump * viscosityConstant;
+				float newHeight = ( ( north.x + south.x + east.x + west.x ) * 0.5 - heightmapValue.y ) * viscosityConstant;
 
 				// Mouse influence
 				float mousePhase = clamp( length( ( uv - vec2( 0.5 ) ) * BOUNDS - vec2( mousePos.x, - mousePos.y ) ) * PI / mouseSize, 0.0, PI );
-				heightmapValue.x += cos( mousePhase ) + 1.0;
-
-				// Water height reset compensation
-				heightmapValue.x += heightCompensation;
+				newHeight += ( cos( mousePhase ) + 1.0 ) * 0.28;
+				
+				heightmapValue.y = heightmapValue.x;
+				heightmapValue.x = newHeight;
 
 				gl_FragColor = heightmapValue;
 
@@ -127,9 +115,11 @@
 
 		</script>
 		
-		<!-- This is a 'compute shader' to calculate the current level of water at the center -->
+		<!-- This is a 'compute shader' to read the current level and normal of water at a point -->
 		<!-- It is used with a variable of size 1x1 -->
-		<script id="sumFragmentShader" type="x-shader/x-fragment">
+		<script id="readWaterLevelFragmentShader" type="x-shader/x-fragment">
+
+			uniform vec2 point1;
 		
 			uniform sampler2D texture;
 		
@@ -180,9 +170,34 @@
 
 			void main()	{
 
-				float waterLevel = texture2D( texture, vec2( 0.5, 0.5 ) ).x;
+				vec2 cellSize = 1.0 / resolution.xy;
 
-				gl_FragColor = encode_float( waterLevel );
+				float waterLevel = texture2D( texture, point1 ).x;
+
+				vec2 normal = vec2(
+					( texture2D( texture, point1 + vec2( - cellSize.x, 0 ) ).x - texture2D( texture, point1 + vec2( cellSize.x, 0 ) ).x ) * WIDTH / BOUNDS,
+					( texture2D( texture, point1 + vec2( 0, - cellSize.y ) ).x - texture2D( texture, point1 + vec2( 0, cellSize.y ) ).x ) * WIDTH / BOUNDS );
+
+				if ( gl_FragCoord.x < 1.5 ) {
+
+					gl_FragColor = encode_float( waterLevel );
+				
+				}
+				else if ( gl_FragCoord.x < 2.5 ) {
+
+					gl_FragColor = encode_float( normal.x );
+
+				}
+				else if ( gl_FragCoord.x < 3.5 ) {
+
+					gl_FragColor = encode_float( normal.y );
+
+				}
+				else {
+
+					gl_FragColor = encode_float( 0.0 );
+
+				}
 
 			}
 
@@ -294,7 +309,11 @@
 			var readWaterLevelShader;
 			var readWaterLevelRenderTarget;
 			var readWaterLevelImage;
-			var numFrames = 0;
+			var waterNormal = new THREE.Vector3();
+			
+			var NUM_SPHERES = 5;
+			var spheres = [];
+			var spheresEnabled = true;
 
 			var simplex = new SimplexNoise();
 
@@ -372,27 +391,37 @@
 
 				var effectController = {
 					mouseSize: 20.0,
-					viscosity: 0.03
+					viscosity: 0.98,
+					spheresEnabled: spheresEnabled
 				};
 
 				var valuesChanger = function() {
 
 					heightmapVariable.material.uniforms.mouseSize.value = effectController.mouseSize;
 					heightmapVariable.material.uniforms.viscosityConstant.value = effectController.viscosity;
+					spheresEnabled = effectController.spheresEnabled;
+					for ( var i = 0; i < NUM_SPHERES; i++ ) {
+						if ( spheres[ i ] ) {
+							spheres[ i ].visible = spheresEnabled;
+						}
+					}
 
 				};
 
 				gui.add( effectController, "mouseSize", 1.0, 100.0, 1.0 ).onChange( valuesChanger );
-				gui.add( effectController, "viscosity", 0.0, 0.1, 0.001 ).onChange( valuesChanger );
+				gui.add( effectController, "viscosity", 0.9, 0.999, 0.001 ).onChange( valuesChanger );
+				gui.add( effectController, "spheresEnabled", 0, 1, 1 ).onChange( valuesChanger );
 				var buttonSmooth = {
 				    smoothWater: function() {
-					smoothWater();
+						smoothWater();
 				    }
 				};
 				gui.add( buttonSmooth, 'smoothWater' );
 
 
 				initWater();
+				
+				createSpheres();
 
 				valuesChanger();
 
@@ -467,7 +496,7 @@
 
 				heightmapVariable.material.uniforms.mousePos = { value: new THREE.Vector2( 10000, 10000 ) };
 				heightmapVariable.material.uniforms.mouseSize = { value: 20.0 };
-				heightmapVariable.material.uniforms.viscosityConstant = { value: 0.03 };
+				heightmapVariable.material.uniforms.viscosityConstant = { value: 0.98 };
 				heightmapVariable.material.uniforms.heightCompensation = { value: 0 };
 				heightmapVariable.material.defines.BOUNDS = BOUNDS.toFixed( 1 );
 
@@ -480,12 +509,17 @@
 				smoothShader = gpuCompute.createShaderMaterial( document.getElementById( 'smoothFragmentShader' ).textContent, { texture: { value: null } } );
 
 				// Create compute shader to read water level
-				readWaterLevelShader = gpuCompute.createShaderMaterial( document.getElementById( 'sumFragmentShader' ).textContent, { texture: { value: null } } );
+				readWaterLevelShader = gpuCompute.createShaderMaterial( document.getElementById( 'readWaterLevelFragmentShader' ).textContent, {
+					point1: { value: new THREE.Vector2() },
+					texture: { value: null }
+				} );
+				readWaterLevelShader.defines.WIDTH = WIDTH.toFixed( 1 );
+				readWaterLevelShader.defines.BOUNDS = BOUNDS.toFixed( 1 );
 				
-				// Create a 1x1 pixel image and a render target (Uint8, 4 channels, 1 byte per channel) to read water height
-				readWaterLevelImage = new Uint8Array( 1 * 1 * 4 );
+				// Create a 4x1 pixel image and a render target (Uint8, 4 channels, 1 byte per channel) to read water height and orientation
+				readWaterLevelImage = new Uint8Array( 4 * 1 * 4 );
 
-				readWaterLevelRenderTarget = new THREE.WebGLRenderTarget( 1, 1, {
+				readWaterLevelRenderTarget = new THREE.WebGLRenderTarget( 4, 1, {
 					wrapS: THREE.ClampToEdgeWrapping,
 					wrapT: THREE.ClampToEdgeWrapping,
 					minFilter: THREE.NearestFilter,
@@ -523,8 +557,8 @@
 						var x = i * 128 / WIDTH;
 						var y = j * 128 / WIDTH;
 
-					        pixels[ p + 0 ] = noise( x, y, 123.4 );
-						pixels[ p + 1 ] = 0;
+						pixels[ p + 0 ] = noise( x, y, 123.4 );
+						pixels[ p + 1 ] = pixels[ p + 0 ];
 						pixels[ p + 2 ] = 0;
 						pixels[ p + 3 ] = 1;
 
@@ -551,22 +585,86 @@
 				
 			}
 
-			function readWaterLevel() {
-				
-				// Returns current water level at the center
+			function createSpheres() {
+
+				var sphereTemplate = new THREE.Mesh( new THREE.SphereBufferGeometry( 4, 24, 12 ), new THREE.MeshPhongMaterial( { color: 0xFFFF00 } ) );
+
+				for ( var i = 0; i < NUM_SPHERES; i++ ) {
+
+					var sphere = sphereTemplate;
+					if ( i < NUM_SPHERES - 1 ) {
+						sphere = sphereTemplate.clone();
+					}
+
+					sphere.position.x = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
+					sphere.position.z = ( Math.random() - 0.5 ) * BOUNDS * 0.7;
+					
+					sphere.userData.velocity = new THREE.Vector3();
+
+					scene.add( sphere );
+
+					spheres[ i ] = sphere;
+
+				}
+
+			}
+
+			function sphereDynamics() {
 
 				var currentRenderTarget = gpuCompute.getCurrentRenderTarget( heightmapVariable );
 
 				readWaterLevelShader.uniforms.texture.value = currentRenderTarget.texture;
+				var gl = renderer.context;
 
-				gpuCompute.doRenderTarget( readWaterLevelShader, readWaterLevelRenderTarget );
+				for ( var i = 0; i < NUM_SPHERES; i++ ) {
+
+					var sphere = spheres[ i ];
+					
+					if ( sphere ) {
+
+						// Read water level and orientation
+						var u = 0.5 * sphere.position.x / BOUNDS_HALF + 0.5;
+						var v = 1 - ( 0.5 * sphere.position.z / BOUNDS_HALF + 0.5 );
+						readWaterLevelShader.uniforms.point1.value.set( u, v );
+						gpuCompute.doRenderTarget( readWaterLevelShader, readWaterLevelRenderTarget );
+						gl.readPixels( 0, 0, 4, 1, gl.RGBA, gl.UNSIGNED_BYTE, readWaterLevelImage );
+						var pixels = new Float32Array( readWaterLevelImage.buffer );
+						
+						// Get orientation
+						waterNormal.set( pixels[ 1 ], 0, - pixels[ 2 ] );
+						
+						var pos = sphere.position;
+						
+						// Set height
+						pos.y = pixels[ 0 ];
+						
+						// Move sphere
+						waterNormal.multiplyScalar( 0.1 );
+						sphere.userData.velocity.add( waterNormal );
+						sphere.userData.velocity.multiplyScalar( 0.998 );
+						pos.add( sphere.userData.velocity );
+						
+						if ( pos.x < - BOUNDS_HALF ) {
+							pos.x = - BOUNDS_HALF + 0.001;
+							sphere.userData.velocity.x *= - 0.3;
+						}
+						else if ( pos.x > BOUNDS_HALF ) {
+							pos.x = BOUNDS_HALF - 0.001;
+							sphere.userData.velocity.x *= - 0.3;
+						}
 
-				var gl = renderer.context;
-				gl.readPixels( 0, 0, 1, 1, gl.RGBA, gl.UNSIGNED_BYTE, readWaterLevelImage );
+						if ( pos.z < - BOUNDS_HALF ) {
+							pos.z = - BOUNDS_HALF + 0.001;
+							sphere.userData.velocity.z *= - 0.3;
+						}
+						else if ( pos.z > BOUNDS_HALF ) {
+							pos.z = BOUNDS_HALF - 0.001;
+							sphere.userData.velocity.z *= - 0.3;
+						}
 
-				var pixels = new Float32Array( readWaterLevelImage.buffer );
+					}
 
-				return pixels[ 0 ];
+				}
 
 			}
 
@@ -656,20 +754,13 @@
 					uniforms.mousePos.value.set( 10000, 10000 );
 				}
 
-				// Read height value once in a time, since it has some cost
-				if ( ++ numFrames > 120 ) {
-
-					numFrames = 0;
-
-					// Apply gradually height compensation to reset water level
-
-					heightmapVariable.material.uniforms.heightCompensation.value = - readWaterLevel() / 120;
-
-				}
-
 				// Do the gpu computation
 				gpuCompute.compute();
 
+				if ( spheresEnabled ) {
+					sphereDynamics();
+				}
+
 				// Get compute output in custom uniform
 				waterUniforms.heightmap.value = gpuCompute.getCurrentRenderTarget( heightmapVariable ).texture;