Lewy Blue před 6 roky
rodič
revize
946a65821a

+ 261 - 32
examples/webgl_gpgpu_water.html

@@ -46,6 +46,7 @@
 
 		<script src="js/GPUComputationRenderer.js"></script>
 
+
 		<!-- This is the 'compute shader' for the water heightmap: -->
 		<script id="heightmapFragmentShader" type="x-shader/x-fragment">
 
@@ -54,9 +55,7 @@
 			uniform vec2 mousePos;
 			uniform float mouseSize;
 			uniform float viscosityConstant;
-
-			#define deltaTime ( 1.0 / 60.0 )
-			#define GRAVITY_CONSTANT ( resolution.x * deltaTime * 3.0 )
+			uniform float heightCompensation;
 
 			void main()	{
 
@@ -64,8 +63,8 @@
 
 				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 );
 
@@ -75,20 +74,16 @@
 				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;
+				// https://web.archive.org/web/20080618181901/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
 
-				// 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;
+				newHeight += ( cos( mousePhase ) + 1.0 ) * 0.28;
+
+				heightmapValue.y = heightmapValue.x;
+				heightmapValue.x = newHeight;
 
 				gl_FragColor = heightmapValue;
 
@@ -122,6 +117,91 @@
 
 		</script>
 
+		<!-- 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="readWaterLevelFragmentShader" type="x-shader/x-fragment">
+
+			uniform vec2 point1;
+
+			uniform sampler2D texture;
+
+			// Integer to float conversion from https://stackoverflow.com/questions/17981163/webgl-read-pixels-from-floating-point-render-target
+
+			float shift_right( float v, float amt ) {
+
+				v = floor( v ) + 0.5;
+				return floor( v / exp2( amt ) );
+
+			}
+
+			float shift_left( float v, float amt ) {
+
+				return floor( v * exp2( amt ) + 0.5 );
+
+			}
+
+			float mask_last( float v, float bits ) {
+
+				return mod( v, shift_left( 1.0, bits ) );
+
+			}
+
+			float extract_bits( float num, float from, float to ) {
+
+				from = floor( from + 0.5 ); to = floor( to + 0.5 );
+				return mask_last( shift_right( num, from ), to - from );
+
+			}
+
+			vec4 encode_float( float val ) {
+				if ( val == 0.0 ) return vec4( 0, 0, 0, 0 );
+				float sign = val > 0.0 ? 0.0 : 1.0;
+				val = abs( val );
+				float exponent = floor( log2( val ) );
+				float biased_exponent = exponent + 127.0;
+				float fraction = ( ( val / exp2( exponent ) ) - 1.0 ) * 8388608.0;
+				float t = biased_exponent / 2.0;
+				float last_bit_of_biased_exponent = fract( t ) * 2.0;
+				float remaining_bits_of_biased_exponent = floor( t );
+				float byte4 = extract_bits( fraction, 0.0, 8.0 ) / 255.0;
+				float byte3 = extract_bits( fraction, 8.0, 16.0 ) / 255.0;
+				float byte2 = ( last_bit_of_biased_exponent * 128.0 + extract_bits( fraction, 16.0, 23.0 ) ) / 255.0;
+				float byte1 = ( sign * 128.0 + remaining_bits_of_biased_exponent ) / 255.0;
+				return vec4( byte4, byte3, byte2, byte1 );
+			}
+
+			void main()	{
+
+				vec2 cellSize = 1.0 / resolution.xy;
+
+				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 );
+
+				}
+
+			}
+
+		</script>
+
 		<!-- This is the water visualization shader, copied from the MeshPhongMaterial and modified: -->
 		<script id="waterVertexShader" type="x-shader/x-vertex">
 
@@ -214,6 +294,7 @@
 
 			// Water size in system units
 			var BOUNDS = 512;
+			var BOUNDS_HALF = BOUNDS * 0.5;
 
 			var container, stats;
 			var camera, scene, renderer;
@@ -227,6 +308,14 @@
 			var heightmapVariable;
 			var waterUniforms;
 			var smoothShader;
+			var readWaterLevelShader;
+			var readWaterLevelRenderTarget;
+			var readWaterLevelImage;
+			var waterNormal = new THREE.Vector3();
+
+			var NUM_SPHERES = 5;
+			var spheres = [];
+			var spheresEnabled = true;
 
 			var simplex = new SimplexNoise();
 
@@ -238,7 +327,8 @@
 				location.reload();
 				return false;
 
-			}
+}
+
 
 			var options = '';
 			for ( var i = 4; i < 10; i ++ ) {
@@ -246,7 +336,7 @@
 				var j = Math.pow( 2, i );
 				options += '<a href="#" onclick="return change(' + j + ')">' + j + 'x' + j + '</a> ';
 
-			}
+}
 			document.getElementById( 'options' ).innerHTML = options;
 
 			init();
@@ -298,24 +388,37 @@
 
 				window.addEventListener( 'resize', onWindowResize, false );
 
+
 				var gui = new dat.GUI();
 
 				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: function () {
 
 						smoothWater();
 
@@ -323,12 +426,16 @@
 				};
 				gui.add( buttonSmooth, 'smoothWater' );
 
+
 				initWater();
 
+				createSpheres();
+
 				valuesChanger();
 
 			}
 
+
 			function initWater() {
 
 				var materialColor = 0x0040C0;
@@ -397,7 +504,8 @@
 
 				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 );
 
 				var error = gpuCompute.init();
@@ -405,11 +513,33 @@
 
 				    console.error( error );
 
-				}
+	}
 
 				// Create compute shader to smooth the water surface and velocity
 				smoothShader = gpuCompute.createShaderMaterial( document.getElementById( 'smoothFragmentShader' ).textContent, { texture: { value: null } } );
 
+				// Create compute shader to read water level
+				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 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( 4, 1, {
+					wrapS: THREE.ClampToEdgeWrapping,
+					wrapT: THREE.ClampToEdgeWrapping,
+					minFilter: THREE.NearestFilter,
+					magFilter: THREE.NearestFilter,
+					format: THREE.RGBAFormat,
+					type: THREE.UnsignedByteType,
+					stencilBuffer: false,
+					depthBuffer: false
+				} );
+
 			}
 
 			function fillTexture( texture ) {
@@ -427,10 +557,10 @@
 						multR *= 0.53 + 0.025 * i;
 						mult *= 1.25;
 
-					}
+		}
 					return r;
 
-				}
+	}
 
 				var pixels = texture.image.data;
 
@@ -442,16 +572,16 @@
 						var x = i * 128 / WIDTH;
 						var y = j * 128 / WIDTH;
 
-					  pixels[ p + 0 ] = noise( x, y );
-						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;
 
 						p += 4;
 
-					}
+		}
 
-				}
+	}
 
 			}
 
@@ -472,6 +602,97 @@
 
 			}
 
+			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;
+
+				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;
+
+						}
+
+						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;
+
+						}
+
+					}
+
+				}
+
+			}
+
 			function onWindowResize() {
 
 				camera.aspect = window.innerWidth / window.innerHeight;
@@ -502,6 +723,7 @@
 
 					setMouseCoords( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
 
+
 				}
 
 			}
@@ -514,6 +736,7 @@
 
 					setMouseCoords( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
 
+
 				}
 
 			}
@@ -546,19 +769,25 @@
 
 					    uniforms.mousePos.value.set( 10000, 10000 );
 
-					}
+		}
 
 					mouseMoved = false;
 
-				} else {
+	} else {
 
 					uniforms.mousePos.value.set( 10000, 10000 );
 
-				}
+	}
 
 				// Do the gpu computation
 				gpuCompute.compute();
 
+				if ( spheresEnabled ) {
+
+					sphereDynamics();
+
+	}
+
 				// Get compute output in custom uniform
 				waterUniforms.heightmap.value = gpuCompute.getCurrentRenderTarget( heightmapVariable ).texture;
 
@@ -569,4 +798,4 @@
 
 		</script>
 	</body>
-</html>
+</html>

+ 8 - 7
examples/webgl_loader_gltf_extensions.html

@@ -115,9 +115,9 @@
 					url: './models/gltf/Monster/%s/Monster.gltf',
 					author: '3drt.com',
 					authorURL: 'http://www.3drt.com/downloads.htm',
-					cameraPos: new THREE.Vector3( 30, 10, 70 ),
-					objectScale: new THREE.Vector3( 0.4, 0.4, 0.4 ),
-					objectPosition: new THREE.Vector3( 2, 1, 0 ),
+					cameraPos: new THREE.Vector3( 3, 1, 7 ),
+					objectScale: new THREE.Vector3( 0.04, 0.04, 0.04 ),
+					objectPosition: new THREE.Vector3( 0.2, 0.1, 0 ),
 					objectRotation: new THREE.Euler( 0, - 3 * Math.PI / 4, 0 ),
 					animationTime: 3,
 					addLights: true,
@@ -208,9 +208,11 @@
 					scene.add( directionalLight );
 
 					spot1 = new THREE.SpotLight( 0xffffff, 1 );
-					spot1.position.set( 10, 20, 10 );
-					spot1.angle = 0.25;
+					spot1.position.set( 5, 10, 5 );
+					spot1.angle = 0.50;
 					spot1.penumbra = 0.75;
+					spot1.intensity = 100;
+					spot1.decay = 2;
 
 					if ( sceneInfo.shadows ) {
 
@@ -308,7 +310,6 @@
 
 						if ( spot1 ) {
 
-							spot1.position.set( sceneInfo.objectPosition.x - 100, sceneInfo.objectPosition.y + 200, sceneInfo.objectPosition.z - 100 );
 							spot1.target.position.copy( sceneInfo.objectPosition );
 
 						}
@@ -498,4 +499,4 @@
 		</script>
 
 	</body>
-</html>
+</html>