import * as THREE from './threejs/build/three.module.js'; import { GLTFLoader } from './threejs/examples/jsm/loaders/GLTFLoader.js'; import { OrbitControls } from './threejs/examples/jsm/controls/OrbitControls.js'; import { Water } from './threejs/examples/jsm/objects/Water.js'; import { Sky } from './threejs/examples/jsm/objects/Sky.js'; import { Ship } from './Ship.js'; // scene size var WIDTH = window.innerWidth; var HEIGHT = window.innerHeight; // camera var VIEW_ANGLE = 45; var ASPECT = WIDTH / HEIGHT; var NEAR = 1; var FAR = 5000; var camera, scene, renderer, raycaster; var clock; var intersects; export var keys = []; var mouse; var controls; var ship, water, sun, sky; var parameters; var simpleWater, wireframe; function init() { raycaster = new THREE.Raycaster(); mouse = new THREE.Vector2(-1, -1); intersects = []; // renderer renderer = new THREE.WebGLRenderer(); renderer.setPixelRatio(window.devicePixelRatio); renderer.setSize(WIDTH, HEIGHT); renderer.shadowMap.enabled = true; renderer.shadowMap.type = THREE.PCFSoftShadowMap; // renderer.outputEncoding = THREE.sRGBEncoding; // scene scene = new THREE.Scene(); scene.fog = new THREE.Fog(0xFFFFFF, 50, 500); // camera camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR); camera.position.set(16, 22, 16); camera.lookAt(0, 0, 0); let container = document.getElementById('container'); container.appendChild(renderer.domElement); controls = new OrbitControls(camera, renderer.domElement); controls.maxPolarAngle = Math.PI * 0.495; controls.target.set(0, 0, 0); controls.minDistance = 10.0; controls.maxDistance = 100.0; controls.update(); clock = new THREE.Clock(); } function fillScene() { //sky let cubeloader = new THREE.CubeTextureLoader(); let texture = cubeloader.load([ './textures/cubemap/posx.png', './textures/cubemap/negx.png', './textures/cubemap/posy.png', './textures/cubemap/negy.png', './textures/cubemap/posz.png', './textures/cubemap/negz.png', ]); scene.background = texture; //ground // let textureLoader = new THREE.TextureLoader(); // let lawnTex = textureLoader.load("textures/lawn.jpg"); // var planeGeo = new THREE.PlaneBufferGeometry(1000, 1000, 32, 32); // let floorMat = new THREE.MeshPhongMaterial({ map: lawnTex }); // floorMat.flatShading = true; // floorMat.shininess = 0; // lawnTex.wrapS = lawnTex.wrapT = THREE.RepeatWrapping; // lawnTex.repeat.set(5, 5); // ground = new THREE.Mesh(planeGeo, floorMat); // ground.rotateX(- Math.PI / 2); // ground.receiveShadow = true; // scene.add(ground); // lights var sunlight = new THREE.PointLight(0xffffff, 1, 0, 2); sunlight.position.x = 400; sunlight.position.y = 400; sunlight.position.z = 100; sunlight.castShadow = true; sunlight.shadow.camera.far = 1600; scene.add(sunlight); // var mainLight = new THREE.HemisphereLight(0xB1E1FF, 0xB97A20, 0.8); // mainLight.position.x = 1; // mainLight.position.y = 1; // mainLight.position.z = 1; // mainLight.lookAt(0, 0, 0); // scene.add(mainLight); //ground // let textureLoader = new THREE.TextureLoader(); // let lawnTex = textureLoader.load("textures/lawn.jpg"); var planeGeo = new THREE.PlaneBufferGeometry(5000, 5000, 32, 32); water = new Water( planeGeo, { textureWidth: 512, textureHeight: 512, waterNormals: new THREE.TextureLoader().load('./js/threejs/examples/textures/waternormals.jpg', function (texture) { texture.wrapS = texture.wrapT = THREE.RepeatWrapping; //texture.repeat.set(5,5); }), alpha: 1.0, sunDirection: new THREE.Vector3(), sunColor: 0xffffff, waterColor: 0x001e0f, distortionScale: 3.7, fog: scene.fog !== undefined } ); water.rotation.x = - Math.PI / 2; water.material.uniforms['size'].value = 20; water.material.uniforms['eye'].value = new THREE.Vector3(16, 22, 16); // scene.add(water); //simple water // https://csantosbh.wordpress.com/2014/01/09/custom-shaders-with-three-js-uniforms-textures-and-lighting/ let simplePlane = new THREE.PlaneBufferGeometry(500, 500, 100, 100); let simpleMaterial = new THREE.MeshPhongMaterial({ color: 0x4080EE }); let simpleUniforms = new THREE.UniformsUtils.merge([ THREE.UniformsLib.lights, THREE.UniformsLib.fog, { waterColor: { value: new THREE.Color(0x0880AA) }, u_time: { value: 0 }, lightIntensity: { value: 1 }, } ] ); let shaderMaterial = new THREE.ShaderMaterial({ wireframe: false, lights: true, vertexShader: ` varying vec3 vecPos; varying vec3 vecNormal; uniform float u_time; void main() { vec3 p = position; p.z = p.z + 0.3 * (sin(2.5 * (u_time + (p.y * 0.1)))) + 0.5; p.z = p.z + 0.3 * (sin(3.5 * (u_time + (p.x * 0.1)))) + 0.5; // Since the light is in camera coordinates, // I'll need the vertex position in camera coords too vecPos = (modelViewMatrix * vec4(p, 1.0)).xyz; // That's NOT exacly how you should transform your // normals but this will work fine, since my model // matrix is pretty basic vecNormal = (modelViewMatrix * vec4(normal, 0.0)).xyz; gl_Position = projectionMatrix * vec4(vecPos, 1.0); }`, fragmentShader: ` precision highp float; varying vec3 vecPos; varying vec3 vecNormal; uniform float lightIntensity; uniform sampler2D textureSampler; uniform vec3 waterColor; struct PointLight { vec3 color; vec3 position; // light position, in camera coordinates float distance; // used for attenuation purposes. Since // we're writing our own shader, it can // really be anything we want (as long as // we assign it to our light in its // "distance" field }; uniform PointLight pointLights[NUM_POINT_LIGHTS]; void main(void) { // Pretty basic lambertian lighting... vec4 addedLights = vec4(0.0, 0.0, 0.0, 1.0); for(int l = 0; l < NUM_POINT_LIGHTS; l++) { vec3 lightDirection = normalize(vecPos - pointLights[l].position); addedLights.rgb += clamp(dot(-lightDirection, vecNormal), 0.0, 1.0) * pointLights[l].color * lightIntensity; } gl_FragColor = vec4(waterColor, 1.0) * addedLights; }`, uniforms: simpleUniforms, }); simpleWater = new THREE.Mesh(simplePlane, shaderMaterial); simpleWater.receiveShadow = true; simpleWater.rotation.x = -Math.PI / 2; wireframe = simpleWater.clone(); wireframe.position.y += 0.01; wireframe.material = wireframe.material.clone(); wireframe.material.wireframe = true; wireframe.material.uniforms['waterColor'].value = new THREE.Color(0x28A0CA); scene.add(simpleWater); scene.add(wireframe); Ship.loadShip().then(gltf => { ship = new Ship(gltf.scene); ship.init(); scene.add(gltf.scene); }); // sun = new THREE.Vector3(); // sky = new Sky(); // sky.scale.setScalar(10000); // scene.add(sky); // var uniforms = sky.material.uniforms; // uniforms['turbidity'].value = 1; // uniforms['rayleigh'].value = 2; // uniforms['mieCoefficient'].value = 0.005; // uniforms['mieDirectionalG'].value = 0.8; // parameters = { // inclination: -0.5, // azimuth: 0.205 // }; // updateSun(); update(); } function updateSun() { var pmremGenerator = new THREE.PMREMGenerator(renderer); var theta = Math.PI * (parameters.inclination - 0.5); var phi = 2 * Math.PI * (parameters.azimuth - 0.5); sun.x = Math.cos(phi); sun.y = Math.sin(phi) * Math.sin(theta); sun.z = Math.sin(phi) * Math.cos(theta); sky.material.uniforms['sunPosition'].value.copy(sun); water.material.uniforms['sunDirection'].value.copy(sun).normalize(); scene.environment = pmremGenerator.fromScene(sky).texture; } function update() { requestAnimationFrame(update); water.material.uniforms['time'].value += 1.0 / 60.0; if (simpleWater.material) { simpleWater.material.uniforms['u_time'].value = clock.getElapsedTime(); wireframe.material.uniforms['u_time'].value = clock.getElapsedTime(); // simpleWater.material.uniforms['eye'].value = camera.position; // simpleWater.material.uniforms['sunDirection'].value.copy(sun).normalize(); } // water.material.uniforms['eye'].value = new THREE.Vector3(scene.position.x, scene.position.y, scene.position.z); let cameraMoveSpeed = 1; //camera controls if (keys["ArrowLeft"]) { //left scene.translateX(cameraMoveSpeed); } if (keys["ArrowRight"]) { //right scene.translateX(-cameraMoveSpeed); } if (keys["ArrowUp"]) { //up scene.translateZ(cameraMoveSpeed); } if (keys["ArrowDown"]) { //down scene.translateZ(-cameraMoveSpeed); } ship.handleInput(); // if (keys["KeyF"]) { // scene.position.set(ship.position.x + 16, ship.position.y + 22, ship.position.z + 16); // } ship.updateModel(); // camera.lookAt(ship.scene.position); // parameters.inclination += 0.0005; // parameters.inclination %= 2 * Math.PI; // updateSun(); scene.position.set(-ship.position.x, 0, -ship.position.z); renderer.render(scene, camera); } document.addEventListener("DOMContentLoaded", () => { init(); fillScene(); }); window.addEventListener("keydown", e => keys[e.code] = true); window.addEventListener("keyup", e => keys[e.code] = false); window.addEventListener("mousemove", event => { mouse.x = normalize(event.clientX / window.innerWidth); mouse.y = -normalize(event.clientY / window.innerHeight); }); window.addEventListener("mousedown", event => { mouse.x = normalize(event.clientX / window.innerWidth); mouse.y = -normalize(event.clientY / window.innerHeight); }); window.addEventListener("contextmenu", event => { event.preventDefault(); }); window.addEventListener("mouseup", event => { }); window.addEventListener("wheel", event => { }); window.addEventListener("touchmove", event => { mouse.x = normalize(event.touches[0].clientX / window.innerWidth); mouse.y = -normalize(event.touches[0].clientY / window.innerHeight); }); window.addEventListener("resize", () => { WIDTH = window.innerWidth; HEIGHT = window.innerHeight; ASPECT = WIDTH / HEIGHT; camera.aspect = ASPECT; renderer.setSize(WIDTH, HEIGHT); camera.updateProjectionMatrix(); }); function normalize(value) { return (2 * value) - 1; }