|
@@ -0,0 +1,567 @@
|
|
|
+<!DOCTYPE html>
|
|
|
+<html lang="en">
|
|
|
+ <head>
|
|
|
+ <title>three.js webgl - gpgpu - water</title>
|
|
|
+ <meta charset="utf-8">
|
|
|
+ <meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
|
|
|
+ <style>
|
|
|
+ body {
|
|
|
+ background-color: #000000;
|
|
|
+ margin: 0px;
|
|
|
+ overflow: hidden;
|
|
|
+ font-family:Monospace;
|
|
|
+ font-size:13px;
|
|
|
+ text-align:center;
|
|
|
+ text-align:center;
|
|
|
+ }
|
|
|
+
|
|
|
+ a {
|
|
|
+ color:#0078ff;
|
|
|
+ }
|
|
|
+
|
|
|
+ #info {
|
|
|
+ color: #ffffff;
|
|
|
+ position: absolute;
|
|
|
+ top: 10px;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ </style>
|
|
|
+ </head>
|
|
|
+ <body>
|
|
|
+
|
|
|
+ <div id="info">
|
|
|
+ <a href="http://threejs.org" target="_blank">three.js</a> - <span id="waterSize"></span> webgl gpgpu water<br/>
|
|
|
+ Select <span id="options"></span> water size<br/>
|
|
|
+ Move mouse to disturb water.<br>
|
|
|
+ Press mouse button to orbit around. 'W' key toggles wireframe.
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <script src="../build/three.js"></script>
|
|
|
+ <script src="js/Detector.js"></script>
|
|
|
+ <script src="js/libs/stats.min.js"></script>
|
|
|
+ <script src="js/libs/dat.gui.min.js"></script>
|
|
|
+ <script src="js/controls/OrbitControls.js"></script>
|
|
|
+ <script src="js/SimplexNoise.js"></script>
|
|
|
+
|
|
|
+ <script src="js/GPUComputationRenderer.js"></script>
|
|
|
+
|
|
|
+
|
|
|
+ <!-- This is the 'compute shader' for the water heightmap: -->
|
|
|
+ <script id="heightmapFragmentShader" type="x-shader/x-fragment">
|
|
|
+
|
|
|
+ #include <common>
|
|
|
+
|
|
|
+ uniform vec2 mousePos;
|
|
|
+ uniform float mouseSize;
|
|
|
+ uniform float viscosityConstant;
|
|
|
+
|
|
|
+ #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.z, heightmapValue.w not used
|
|
|
+ vec4 heightmapValue = texture2D( heightmap, uv );
|
|
|
+
|
|
|
+ // Get neighbours
|
|
|
+ vec4 north = texture2D( heightmap, uv + vec2( 0.0, cellSize.y ) );
|
|
|
+ vec4 south = texture2D( heightmap, uv + vec2( 0.0, - cellSize.y ) );
|
|
|
+ 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;
|
|
|
+
|
|
|
+ // 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;
|
|
|
+
|
|
|
+ gl_FragColor = heightmapValue;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <!-- This is just a smoothing 'compute shader' for using manually: -->
|
|
|
+ <script id="smoothFragmentShader" type="x-shader/x-fragment">
|
|
|
+
|
|
|
+ uniform sampler2D texture;
|
|
|
+
|
|
|
+ void main() {
|
|
|
+
|
|
|
+ vec2 cellSize = 1.0 / resolution.xy;
|
|
|
+
|
|
|
+ vec2 uv = gl_FragCoord.xy * cellSize;
|
|
|
+
|
|
|
+ // Computes the mean of texel and 4 neighbours
|
|
|
+ vec4 textureValue = texture2D( texture, uv );
|
|
|
+ textureValue += texture2D( texture, uv + vec2( 0.0, cellSize.y ) );
|
|
|
+ textureValue += texture2D( texture, uv + vec2( 0.0, - cellSize.y ) );
|
|
|
+ textureValue += texture2D( texture, uv + vec2( cellSize.x, 0.0 ) );
|
|
|
+ textureValue += texture2D( texture, uv + vec2( - cellSize.x, 0.0 ) );
|
|
|
+
|
|
|
+ textureValue /= 5.0;
|
|
|
+
|
|
|
+ gl_FragColor = textureValue;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <!-- This is the water visualization shader, copied from the MeshPhongMaterial and modified: -->
|
|
|
+ <script id="waterVertexShader" type="x-shader/x-vertex">
|
|
|
+
|
|
|
+ uniform sampler2D heightmap;
|
|
|
+
|
|
|
+ #define PHONG
|
|
|
+
|
|
|
+ varying vec3 vViewPosition;
|
|
|
+
|
|
|
+ #ifndef FLAT_SHADED
|
|
|
+
|
|
|
+ varying vec3 vNormal;
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
+ #include <common>
|
|
|
+ #include <uv_pars_vertex>
|
|
|
+ #include <uv2_pars_vertex>
|
|
|
+ #include <displacementmap_pars_vertex>
|
|
|
+ #include <envmap_pars_vertex>
|
|
|
+ #include <color_pars_vertex>
|
|
|
+ #include <morphtarget_pars_vertex>
|
|
|
+ #include <skinning_pars_vertex>
|
|
|
+ #include <shadowmap_pars_vertex>
|
|
|
+ #include <logdepthbuf_pars_vertex>
|
|
|
+ #include <clipping_planes_pars_vertex>
|
|
|
+
|
|
|
+ void main() {
|
|
|
+
|
|
|
+ vec2 cellSize = vec2( 1.0 / WIDTH, 1.0 / WIDTH );
|
|
|
+
|
|
|
+ #include <uv_vertex>
|
|
|
+ #include <uv2_vertex>
|
|
|
+ #include <color_vertex>
|
|
|
+
|
|
|
+ // # include <beginnormal_vertex>
|
|
|
+ // Compute normal from heightmap
|
|
|
+ vec3 objectNormal = vec3(
|
|
|
+ ( texture2D( heightmap, uv + vec2( - cellSize.x, 0 ) ).x - texture2D( heightmap, uv + vec2( cellSize.x, 0 ) ).x ) * WIDTH / BOUNDS,
|
|
|
+ ( texture2D( heightmap, uv + vec2( 0, - cellSize.y ) ).x - texture2D( heightmap, uv + vec2( 0, cellSize.y ) ).x ) * WIDTH / BOUNDS,
|
|
|
+ 1.0 );
|
|
|
+ //<beginnormal_vertex>
|
|
|
+
|
|
|
+ #include <morphnormal_vertex>
|
|
|
+ #include <skinbase_vertex>
|
|
|
+ #include <skinnormal_vertex>
|
|
|
+ #include <defaultnormal_vertex>
|
|
|
+
|
|
|
+ #ifndef FLAT_SHADED // Normal computed with derivatives when FLAT_SHADED
|
|
|
+
|
|
|
+ vNormal = normalize( transformedNormal );
|
|
|
+
|
|
|
+ #endif
|
|
|
+
|
|
|
+ //# include <begin_vertex>
|
|
|
+ float heightValue = texture2D( heightmap, uv ).x;
|
|
|
+ vec3 transformed = vec3( position.x, position.y, heightValue );
|
|
|
+ //<begin_vertex>
|
|
|
+
|
|
|
+ #include <displacementmap_vertex>
|
|
|
+ #include <morphtarget_vertex>
|
|
|
+ #include <skinning_vertex>
|
|
|
+ #include <project_vertex>
|
|
|
+ #include <logdepthbuf_vertex>
|
|
|
+ #include <clipping_planes_vertex>
|
|
|
+
|
|
|
+ vViewPosition = - mvPosition.xyz;
|
|
|
+
|
|
|
+ #include <worldpos_vertex>
|
|
|
+ #include <envmap_vertex>
|
|
|
+ #include <shadowmap_vertex>
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+
|
|
|
+ <script>
|
|
|
+
|
|
|
+ if ( ! Detector.webgl ) Detector.addGetWebGLMessage();
|
|
|
+
|
|
|
+ var hash = document.location.hash.substr( 1 );
|
|
|
+ if ( hash ) hash = parseInt( hash, 0 );
|
|
|
+
|
|
|
+ // Texture width for simulation
|
|
|
+ var WIDTH = hash || 128;
|
|
|
+ var NUM_TEXELS = WIDTH * WIDTH;
|
|
|
+
|
|
|
+ // Water size in system units
|
|
|
+ var BOUNDS = 512;
|
|
|
+ var BOUNDS_HALF = BOUNDS * 0.5;
|
|
|
+
|
|
|
+ var container, stats;
|
|
|
+ var camera, scene, renderer, controls;
|
|
|
+ var mouseMoved = false;
|
|
|
+ var mouseCoords = new THREE.Vector2();
|
|
|
+ var raycaster = new THREE.Raycaster();
|
|
|
+
|
|
|
+ var waterMesh;
|
|
|
+ var meshRay;
|
|
|
+ var gpuCompute;
|
|
|
+ var heightmapVariable;
|
|
|
+ var waterUniforms;
|
|
|
+ var smoothShader;
|
|
|
+
|
|
|
+ var simplex = new SimplexNoise();
|
|
|
+
|
|
|
+ var windowHalfX = window.innerWidth / 2;
|
|
|
+ var windowHalfY = window.innerHeight / 2;
|
|
|
+
|
|
|
+ document.getElementById( 'waterSize' ).innerText = WIDTH + ' x ' + WIDTH;
|
|
|
+
|
|
|
+ function change(n) {
|
|
|
+ location.hash = n;
|
|
|
+ location.reload();
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ var options = '';
|
|
|
+ for ( var i = 4; i < 10; i++ ) {
|
|
|
+ var j = Math.pow( 2, i );
|
|
|
+ options += '<a href="#" onclick="return change(' + j + ')">' + j + 'x' + j + '</a> ';
|
|
|
+ }
|
|
|
+ document.getElementById('options').innerHTML = options;
|
|
|
+
|
|
|
+ init();
|
|
|
+ animate();
|
|
|
+
|
|
|
+ function init() {
|
|
|
+
|
|
|
+ container = document.createElement( 'div' );
|
|
|
+ document.body.appendChild( container );
|
|
|
+
|
|
|
+ camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 3000 );
|
|
|
+ camera.position.set( 0, 200, 350 );
|
|
|
+
|
|
|
+ scene = new THREE.Scene();
|
|
|
+
|
|
|
+ var sun = new THREE.DirectionalLight( 0xFFFFFF, 1.0 );
|
|
|
+ sun.position.set( 300, 400, 175 );
|
|
|
+ scene.add( sun );
|
|
|
+
|
|
|
+ var sun2 = new THREE.DirectionalLight( 0x40A040, 0.6 );
|
|
|
+ sun2.position.set( -100, 350, -200 );
|
|
|
+ scene.add( sun2 );
|
|
|
+
|
|
|
+ renderer = new THREE.WebGLRenderer();
|
|
|
+ renderer.setClearColor( 0x000000 );
|
|
|
+ renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+ container.appendChild( renderer.domElement );
|
|
|
+
|
|
|
+ controls = new THREE.OrbitControls( camera, renderer.domElement );
|
|
|
+
|
|
|
+
|
|
|
+ stats = new Stats();
|
|
|
+ container.appendChild( stats.dom );
|
|
|
+
|
|
|
+ document.addEventListener( 'mousemove', onDocumentMouseMove, false );
|
|
|
+ document.addEventListener( 'touchstart', onDocumentTouchStart, false );
|
|
|
+ document.addEventListener( 'touchmove', onDocumentTouchMove, false );
|
|
|
+
|
|
|
+ document.addEventListener( 'keydown', function( event ) {
|
|
|
+
|
|
|
+ // W Pressed: Toggle wireframe
|
|
|
+ if ( event.keyCode === 87 ) {
|
|
|
+
|
|
|
+ waterMesh.material.wireframe = ! waterMesh.material.wireframe;
|
|
|
+ waterMesh.material.needsUpdate = true;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ } , false );
|
|
|
+
|
|
|
+ window.addEventListener( 'resize', onWindowResize, false );
|
|
|
+
|
|
|
+
|
|
|
+ var gui = new dat.GUI();
|
|
|
+
|
|
|
+ var effectController = {
|
|
|
+ mouseSize: 20.0,
|
|
|
+ viscosity: 0.03
|
|
|
+ };
|
|
|
+
|
|
|
+ var valuesChanger = function() {
|
|
|
+
|
|
|
+ heightmapVariable.material.uniforms.mouseSize.value = effectController.mouseSize;
|
|
|
+ heightmapVariable.material.uniforms.viscosityConstant.value = effectController.viscosity;
|
|
|
+
|
|
|
+ };
|
|
|
+
|
|
|
+ gui.add( effectController, "mouseSize", 1.0, 100.0, 1.0 ).onChange( valuesChanger );
|
|
|
+ gui.add( effectController, "viscosity", 0.0, 0.1, 0.001 ).onChange( valuesChanger );
|
|
|
+ var buttonSmooth = {
|
|
|
+ smoothWater: function() {
|
|
|
+ smoothWater();
|
|
|
+ }
|
|
|
+ };
|
|
|
+ gui.add( buttonSmooth, 'smoothWater' );
|
|
|
+
|
|
|
+
|
|
|
+ initWater();
|
|
|
+
|
|
|
+ valuesChanger();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function initWater() {
|
|
|
+
|
|
|
+ var materialColor = 0x0040C0;
|
|
|
+
|
|
|
+ var geometry = new THREE.PlaneBufferGeometry( BOUNDS, BOUNDS, WIDTH - 1, WIDTH -1 );
|
|
|
+
|
|
|
+ // material: make a ShaderMaterial clone of MeshPhongMaterial, with customized vertex shader
|
|
|
+ var material = new THREE.ShaderMaterial( {
|
|
|
+ uniforms: THREE.UniformsUtils.merge( [
|
|
|
+ THREE.ShaderLib[ 'phong' ].uniforms,
|
|
|
+ {
|
|
|
+ heightmap: { type: "t", value: null },
|
|
|
+ }
|
|
|
+ ] ),
|
|
|
+ vertexShader: document.getElementById( 'waterVertexShader' ).textContent,
|
|
|
+ fragmentShader: THREE.ShaderChunk[ 'meshphong_frag' ]
|
|
|
+
|
|
|
+ } );
|
|
|
+
|
|
|
+ material.lights = true;
|
|
|
+
|
|
|
+ // Material attributes from MeshPhongMaterial
|
|
|
+ material.color = new THREE.Color( materialColor );
|
|
|
+ material.specular = new THREE.Color( 0x111111 );
|
|
|
+ material.shininess = 50;
|
|
|
+
|
|
|
+ // Sets the uniforms with the material values
|
|
|
+ material.uniforms.diffuse.value = material.color;
|
|
|
+ material.uniforms.specular.value = material.specular;
|
|
|
+ material.uniforms.shininess.value = Math.max( material.shininess, 1e-4 );
|
|
|
+ material.uniforms.opacity.value = material.opacity;
|
|
|
+
|
|
|
+ // Defines
|
|
|
+ material.defines.WIDTH = WIDTH.toFixed( 1 );
|
|
|
+ material.defines.BOUNDS = BOUNDS.toFixed( 1 );
|
|
|
+
|
|
|
+ waterUniforms = material.uniforms;
|
|
|
+
|
|
|
+ waterMesh = new THREE.Mesh( geometry, material );
|
|
|
+ waterMesh.rotation.x = - Math.PI / 2;
|
|
|
+ waterMesh.matrixAutoUpdate = false;
|
|
|
+ waterMesh.updateMatrix();
|
|
|
+
|
|
|
+ scene.add( waterMesh );
|
|
|
+
|
|
|
+ // Mesh just for mouse raycasting
|
|
|
+ var geometryRay = new THREE.PlaneBufferGeometry( BOUNDS, BOUNDS, 1, 1 );
|
|
|
+ meshRay = new THREE.Mesh( geometryRay, new THREE.MeshBasicMaterial( { color: 0xFFFFFF, visible: false } ) );
|
|
|
+ meshRay.rotation.x = - Math.PI / 2;
|
|
|
+ meshRay.matrixAutoUpdate = false;
|
|
|
+ meshRay.updateMatrix();
|
|
|
+ scene.add( meshRay );
|
|
|
+
|
|
|
+
|
|
|
+ // Creates the gpu computation class and sets it up
|
|
|
+
|
|
|
+ gpuCompute = new GPUComputationRenderer( WIDTH, WIDTH, renderer );
|
|
|
+
|
|
|
+ var heightmap0 = gpuCompute.createTexture();
|
|
|
+
|
|
|
+ fillTexture( heightmap0 );
|
|
|
+
|
|
|
+ heightmapVariable = gpuCompute.addVariable( "heightmap", document.getElementById( 'heightmapFragmentShader' ).textContent, heightmap0 );
|
|
|
+
|
|
|
+ gpuCompute.setVariableDependencies( heightmapVariable, [ heightmapVariable ] );
|
|
|
+
|
|
|
+ heightmapVariable.material.uniforms.mousePos = { type: "v2", value: new THREE.Vector2( 10000, 10000 ) };
|
|
|
+ heightmapVariable.material.uniforms.mouseSize = { type: "f", value: 20.0 };
|
|
|
+ heightmapVariable.material.uniforms.viscosityConstant = { type: "f", value: 0.03 };
|
|
|
+ heightmapVariable.material.defines.BOUNDS = BOUNDS.toFixed( 1 );
|
|
|
+
|
|
|
+ var error = gpuCompute.init();
|
|
|
+ if ( error !== null ) {
|
|
|
+ console.error( error );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Create compute shader to smooth the water surface and velocity
|
|
|
+ smoothShader = gpuCompute.createShaderMaterial( document.getElementById( 'smoothFragmentShader' ).textContent, { texture: { type: "t", value: null } } );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function fillTexture( texture ) {
|
|
|
+
|
|
|
+ var waterMaxHeight = 10;
|
|
|
+
|
|
|
+ function noise( x, y, z ) {
|
|
|
+ var multR = waterMaxHeight;
|
|
|
+ var mult = 0.025;
|
|
|
+ var r = 0;
|
|
|
+ for ( var i = 0; i < 15; i++ ) {
|
|
|
+ r += multR * simplex.noise3d( x * mult, y * mult, z * mult );
|
|
|
+ multR *= 0.53 + 0.025 * i;
|
|
|
+ mult *= 1.25;
|
|
|
+ }
|
|
|
+ return r;
|
|
|
+ }
|
|
|
+
|
|
|
+ var pixels = texture.image.data;
|
|
|
+
|
|
|
+ var p = 0;
|
|
|
+ for ( var j = 0; j < WIDTH; j++ ) {
|
|
|
+ for ( var i = 0; i < WIDTH; i++ ) {
|
|
|
+
|
|
|
+ var x = i * 128 / WIDTH;
|
|
|
+ var y = j * 128 / WIDTH;
|
|
|
+
|
|
|
+ pixels[ p + 0 ] = noise( x, y, 0 );
|
|
|
+ pixels[ p + 1 ] = 0;
|
|
|
+ pixels[ p + 2 ] = 0;
|
|
|
+ pixels[ p + 3 ] = 1;
|
|
|
+
|
|
|
+ p += 4;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function smoothWater() {
|
|
|
+
|
|
|
+ var currentRenderTarget = gpuCompute.getCurrentRenderTarget( heightmapVariable );
|
|
|
+ var alternateRenderTarget = gpuCompute.getAlternateRenderTarget( heightmapVariable );
|
|
|
+
|
|
|
+ for ( var i = 0; i < 10; i++ ) {
|
|
|
+
|
|
|
+ smoothShader.uniforms.texture.value = currentRenderTarget.texture;
|
|
|
+ gpuCompute.doRenderTarget( smoothShader, alternateRenderTarget );
|
|
|
+
|
|
|
+ smoothShader.uniforms.texture.value = alternateRenderTarget.texture;
|
|
|
+ gpuCompute.doRenderTarget( smoothShader, currentRenderTarget );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ function onWindowResize() {
|
|
|
+
|
|
|
+ windowHalfX = window.innerWidth / 2;
|
|
|
+ windowHalfY = window.innerHeight / 2;
|
|
|
+
|
|
|
+ camera.aspect = window.innerWidth / window.innerHeight;
|
|
|
+ camera.updateProjectionMatrix();
|
|
|
+
|
|
|
+ renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function setMouseCoords( x, y ) {
|
|
|
+
|
|
|
+ mouseCoords.set( ( x / renderer.domElement.width ) * 2 - 1, - ( y / renderer.domElement.height ) * 2 + 1 );
|
|
|
+ mouseMoved = true;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function onDocumentMouseMove( event ) {
|
|
|
+
|
|
|
+ setMouseCoords( event.clientX, event.clientY );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function onDocumentTouchStart( event ) {
|
|
|
+
|
|
|
+ if ( event.touches.length === 1 ) {
|
|
|
+
|
|
|
+ event.preventDefault();
|
|
|
+
|
|
|
+ setMouseCoords( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function onDocumentTouchMove( event ) {
|
|
|
+
|
|
|
+ if ( event.touches.length === 1 ) {
|
|
|
+
|
|
|
+ event.preventDefault();
|
|
|
+
|
|
|
+ setMouseCoords( event.touches[ 0 ].pageX, event.touches[ 0 ].pageY );
|
|
|
+
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function animate() {
|
|
|
+
|
|
|
+ requestAnimationFrame( animate );
|
|
|
+
|
|
|
+ render();
|
|
|
+ stats.update();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ function render() {
|
|
|
+
|
|
|
+ // Set uniforms: mouse interaction
|
|
|
+ var uniforms = heightmapVariable.material.uniforms;
|
|
|
+ if ( mouseMoved ) {
|
|
|
+
|
|
|
+ this.raycaster.setFromCamera( mouseCoords, camera );
|
|
|
+
|
|
|
+ var intersects = this.raycaster.intersectObject( meshRay );
|
|
|
+
|
|
|
+ if ( intersects.length > 0 ) {
|
|
|
+ var point = intersects[ 0 ].point;
|
|
|
+ uniforms.mousePos.value.set( point.x, point.z );
|
|
|
+
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ uniforms.mousePos.value.set( 10000, 10000 );
|
|
|
+ }
|
|
|
+
|
|
|
+ mouseMoved = false;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ uniforms.mousePos.value.set( 10000, 10000 );
|
|
|
+ }
|
|
|
+
|
|
|
+ // Do the gpu computation
|
|
|
+ gpuCompute.compute();
|
|
|
+
|
|
|
+ // Get compute output in custom uniform
|
|
|
+ waterUniforms.heightmap.value = gpuCompute.getCurrentRenderTarget( heightmapVariable ).texture;
|
|
|
+
|
|
|
+ // Render
|
|
|
+ renderer.render( scene, camera );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ </script>
|
|
|
+ </body>
|
|
|
+</html>
|