|
|
@@ -10,7 +10,9 @@
|
|
|
|
|
|
<div id="container"></div>
|
|
|
<div id="info">
|
|
|
- <a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> - water
|
|
|
+ <a href="https://threejs.org" target="_blank" rel="noopener noreferrer">three.js</a> - water<br />
|
|
|
+ <a href="https://skfb.ly/6WOOR" target="_blank" rel="noopener">The Night Pool</a> by
|
|
|
+ <a href="https://sketchfab.com/syntheticplants" target="_blank" rel="noopener">syntheticplants</a> is licensed under <a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener">Creative Commons Attribution</a>.<br />
|
|
|
</div>
|
|
|
|
|
|
<script type="importmap">
|
|
|
@@ -28,81 +30,64 @@
|
|
|
|
|
|
import * as THREE from 'three';
|
|
|
|
|
|
+ import { pass, mrt, output, emissive, color, screenUV } from 'three/tsl';
|
|
|
+ import { bloom } from 'three/addons/tsl/display/BloomNode.js';
|
|
|
+
|
|
|
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
|
|
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
|
import { WaterMesh } from 'three/addons/objects/Water2Mesh.js';
|
|
|
+ import { DRACOLoader } from 'three/addons/loaders/DRACOLoader.js';
|
|
|
+ import { GLTFLoader } from 'three/addons/loaders/GLTFLoader.js';
|
|
|
|
|
|
- let scene, camera, clock, renderer, water;
|
|
|
-
|
|
|
- let torusKnot;
|
|
|
+ let scene, camera, renderer, water, postProcessing, controls;
|
|
|
|
|
|
const params = {
|
|
|
color: '#ffffff',
|
|
|
- scale: 4,
|
|
|
- flowX: 1,
|
|
|
- flowY: 1
|
|
|
+ scale: 2,
|
|
|
+ flowX: 0.25,
|
|
|
+ flowY: 0.25
|
|
|
};
|
|
|
|
|
|
init();
|
|
|
|
|
|
- function init() {
|
|
|
+ async function init() {
|
|
|
|
|
|
// scene
|
|
|
|
|
|
scene = new THREE.Scene();
|
|
|
+ scene.backgroundNode = screenUV.distance( .5 ).remap( 0, 0.5 ).mix( color( 0x666666 ), color( 0x111111 ) );
|
|
|
|
|
|
// camera
|
|
|
|
|
|
camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 0.1, 200 );
|
|
|
- camera.position.set( - 15, 7, 15 );
|
|
|
+ camera.position.set( - 25, 10, - 25 );
|
|
|
camera.lookAt( scene.position );
|
|
|
|
|
|
- // clock
|
|
|
-
|
|
|
- clock = new THREE.Clock();
|
|
|
-
|
|
|
- // mesh
|
|
|
-
|
|
|
- const torusKnotGeometry = new THREE.TorusKnotGeometry( 3, 1, 256, 32 );
|
|
|
- const torusKnotMaterial = new THREE.MeshNormalMaterial();
|
|
|
-
|
|
|
- torusKnot = new THREE.Mesh( torusKnotGeometry, torusKnotMaterial );
|
|
|
- torusKnot.position.y = 4;
|
|
|
- torusKnot.scale.set( 0.5, 0.5, 0.5 );
|
|
|
- scene.add( torusKnot );
|
|
|
+ // asset loading
|
|
|
|
|
|
- // ground
|
|
|
-
|
|
|
- const groundGeometry = new THREE.PlaneGeometry( 20, 20 );
|
|
|
- const groundMaterial = new THREE.MeshStandardMaterial( { roughness: 0.8, metalness: 0.4 } );
|
|
|
- const ground = new THREE.Mesh( groundGeometry, groundMaterial );
|
|
|
- ground.rotation.x = Math.PI * - 0.5;
|
|
|
- scene.add( ground );
|
|
|
+ const dracoLoader = new DRACOLoader();
|
|
|
+ dracoLoader.setDecoderPath( 'jsm/libs/draco/gltf/' );
|
|
|
|
|
|
+ const gltfLoader = new GLTFLoader();
|
|
|
+ gltfLoader.setDRACOLoader( dracoLoader );
|
|
|
+
|
|
|
const textureLoader = new THREE.TextureLoader();
|
|
|
- textureLoader.load( 'textures/hardwood2_diffuse.jpg', function ( map ) {
|
|
|
-
|
|
|
- map.wrapS = THREE.RepeatWrapping;
|
|
|
- map.wrapT = THREE.RepeatWrapping;
|
|
|
- map.anisotropy = 16;
|
|
|
- map.repeat.set( 4, 4 );
|
|
|
- map.colorSpace = THREE.SRGBColorSpace;
|
|
|
- groundMaterial.map = map;
|
|
|
- groundMaterial.needsUpdate = true;
|
|
|
|
|
|
- } );
|
|
|
+ const [ gltf, normalMap0, normalMap1 ] = await Promise.all( [
|
|
|
+ gltfLoader.loadAsync( 'models/gltf/pool.glb' ),
|
|
|
+ textureLoader.loadAsync( 'textures/water/Water_1_M_Normal.jpg' ),
|
|
|
+ textureLoader.loadAsync( 'textures/water/Water_2_M_Normal.jpg' )
|
|
|
+ ] );
|
|
|
|
|
|
- //
|
|
|
+ gltf.scene.scale.setScalar( 0.1 );
|
|
|
+ scene.add( gltf.scene );
|
|
|
|
|
|
- const normalMap0 = textureLoader.load( 'textures/water/Water_1_M_Normal.jpg' );
|
|
|
- const normalMap1 = textureLoader.load( 'textures/water/Water_2_M_Normal.jpg' );
|
|
|
+ // water
|
|
|
|
|
|
normalMap0.wrapS = normalMap0.wrapT = THREE.RepeatWrapping;
|
|
|
normalMap1.wrapS = normalMap1.wrapT = THREE.RepeatWrapping;
|
|
|
|
|
|
- // water
|
|
|
-
|
|
|
- const waterGeometry = new THREE.PlaneGeometry( 20, 20 );
|
|
|
+ const waterGeometry = new THREE.PlaneGeometry( 30, 40 );
|
|
|
|
|
|
water = new WaterMesh( waterGeometry, {
|
|
|
color: params.color,
|
|
|
@@ -112,29 +97,17 @@
|
|
|
normalMap1: normalMap1
|
|
|
} );
|
|
|
|
|
|
- water.position.y = 1;
|
|
|
+ water.position.set( 0, 0.2, - 2 );
|
|
|
water.rotation.x = Math.PI * - 0.5;
|
|
|
+ water.renderOrder = Infinity;
|
|
|
scene.add( water );
|
|
|
|
|
|
- // skybox
|
|
|
-
|
|
|
- const cubeTextureLoader = new THREE.CubeTextureLoader();
|
|
|
- cubeTextureLoader.setPath( 'textures/cube/Park2/' );
|
|
|
-
|
|
|
- const cubeTexture = cubeTextureLoader.load( [
|
|
|
- 'posx.jpg', 'negx.jpg',
|
|
|
- 'posy.jpg', 'negy.jpg',
|
|
|
- 'posz.jpg', 'negz.jpg'
|
|
|
- ] );
|
|
|
-
|
|
|
- scene.background = cubeTexture;
|
|
|
-
|
|
|
// light
|
|
|
|
|
|
- const ambientLight = new THREE.AmbientLight( 0xe7e7e7, 1.2 );
|
|
|
+ const ambientLight = new THREE.AmbientLight( 0xccccccc, 0.4 );
|
|
|
scene.add( ambientLight );
|
|
|
|
|
|
- const directionalLight = new THREE.DirectionalLight( 0xffffff, 2 );
|
|
|
+ const directionalLight = new THREE.DirectionalLight( 0xf435ab, 3 );
|
|
|
directionalLight.position.set( - 1, 1, 1 );
|
|
|
scene.add( directionalLight );
|
|
|
|
|
|
@@ -144,8 +117,24 @@
|
|
|
renderer.setSize( window.innerWidth, window.innerHeight );
|
|
|
renderer.setPixelRatio( window.devicePixelRatio );
|
|
|
renderer.setAnimationLoop( animate );
|
|
|
+ renderer.toneMapping = THREE.NeutralToneMapping;
|
|
|
document.body.appendChild( renderer.domElement );
|
|
|
|
|
|
+ postProcessing = new THREE.PostProcessing( renderer );
|
|
|
+
|
|
|
+ const scenePass = pass( scene, camera );
|
|
|
+ scenePass.setMRT( mrt( {
|
|
|
+ output,
|
|
|
+ emissive
|
|
|
+ } ) );
|
|
|
+
|
|
|
+ const outputPass = scenePass.getTextureNode();
|
|
|
+ const emissivePass = scenePass.getTextureNode( 'emissive' );
|
|
|
+
|
|
|
+ const bloomPass = bloom( emissivePass );
|
|
|
+
|
|
|
+ postProcessing.outputNode = outputPass.add( bloomPass );
|
|
|
+
|
|
|
// gui
|
|
|
|
|
|
const gui = new GUI();
|
|
|
@@ -178,9 +167,10 @@
|
|
|
|
|
|
//
|
|
|
|
|
|
- const controls = new OrbitControls( camera, renderer.domElement );
|
|
|
+ controls = new OrbitControls( camera, renderer.domElement );
|
|
|
controls.minDistance = 5;
|
|
|
controls.maxDistance = 50;
|
|
|
+ controls.enableDamping = true;
|
|
|
|
|
|
//
|
|
|
|
|
|
@@ -198,12 +188,9 @@
|
|
|
|
|
|
function animate() {
|
|
|
|
|
|
- const delta = clock.getDelta();
|
|
|
-
|
|
|
- torusKnot.rotation.x += delta;
|
|
|
- torusKnot.rotation.y += delta * 0.5;
|
|
|
+ controls.update();
|
|
|
|
|
|
- renderer.render( scene, camera );
|
|
|
+ postProcessing.render();
|
|
|
|
|
|
}
|
|
|
|