Repository: ma77os/InteractiveLandscape Branch: master Commit: 8d840044b3e1 Files: 10 Total size: 65.7 KB Directory structure: gitextract_ykqzmffm/ ├── .gitignore ├── README.md ├── css/ │ └── base.css ├── index.html ├── index2.html ├── index3.html └── js/ ├── demo1.js ├── demo2.js ├── demo3.js └── vendor/ └── Sky.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ *.DS_Store ================================================ FILE: README.md ================================================ # Interactive Landscape An exploration of an animated interactive landscape built with three.js. By André Mattos. ![Interactive Landscape](https://tympanus.net/codrops/wp-content/uploads/2018/12/landscape_featured.jpg) [Article on Codrops](https://tympanus.net/codrops/?p=36470) [Demo](https://tympanus.net/Development/InteractiveLandscape/) ## Credits * [three.js](https://threejs.org/) * [WebGL noise](https://github.com/stegu/webgl-noise) by Stefan Gustavson * [sky + sun shader](https://threejs.org/examples/?q=sky#webgl_shaders_sky) by [@blurspline](https://twitter.com/blurspline) * [Desmos](https://www.desmos.com/calculator) * [Coolors](https://coolors.co) * [TweenMax](https://greensock.com/tweenmax) ## License This resource can be used freely if integrated or build upon in personal or commercial projects such as websites, web apps and web templates intended for sale. It is not allowed to take the resource "as-is" and sell it, redistribute, re-publish it, or sell "pluginized" versions of it. Free plugins built using this resource should have a visible mention and link to the original work. Always consider the licenses of all included libraries, scripts and images used. ## Misc Follow André: [GitHub](https://github.com/ma77os), [Codepen](https://codepen.io/ma77os/), [Instagram](https://www.instagram.com/ma77os/), [LinkedIn](https://www.linkedin.com/in/andremattos/), [Behance](https://behance.net/ma77os) Follow Codrops: [Twitter](http://www.twitter.com/codrops), [Facebook](http://www.facebook.com/codrops), [Google+](https://plus.google.com/101095823814290637419), [GitHub](https://github.com/codrops), [Pinterest](http://www.pinterest.com/codrops/), [Instagram](https://www.instagram.com/codropsss/) [© Codrops 2018](http://www.codrops.com) ================================================ FILE: css/base.css ================================================ article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}pre{white-space:pre-wrap;}q{quotes:"\201C" "\201D" "\2018" "\2019";}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;} *, *::after, *::before { box-sizing: border-box; } :root { font-size: 16px; } body { --color-text: #fff; --color-bg: #0e0e0f; --color-link: #EC8F7B; --color-link-hover: #fff; --color-title: #fff; --color-subtitle: #fff; color: var(--color-text); background-color: var(--color-bg); font-family: Barlow, Arial, sans-serif; overflow: hidden; height: 100vh; } .demo-2 { --color-text: #fff; --color-bg: #0e0e0f; --color-link: #854aa5; --color-link-hover: #fff; --color-title: #fff; --color-subtitle: #fff; } .demo-3 { --color-text: #fff; --color-bg: #0e0e0f; --color-link: #E5CA56; --color-link-hover: #fff; --color-title: #fff; --color-subtitle: #fff; } /* Page Loader */ .js .loading::before { content: ''; position: fixed; z-index: 100000; top: 0; left: 0; width: 100%; height: 100%; background: var(--color-bg); } .js .loading::after { content: ''; position: fixed; z-index: 100000; top: 50%; left: 50%; width: 60px; height: 60px; margin: -30px 0 0 -30px; pointer-events: none; border-radius: 50%; opacity: 0.4; background: var(--color-link); animation: loaderAnim 0.7s linear infinite alternate forwards; } @keyframes loaderAnim { to { opacity: 1; transform: scale3d(0.5,0.5,1); } } a { text-decoration: none; color: var(--color-link); outline: none; } a:hover, a:focus { color: var(--color-link-hover); outline: none; } .frame { padding: 3rem 5vw; text-align: center; position: relative; z-index: 1000; } .frame__title { font-size: 1rem; margin: 0 0 1rem; font-weight: normal; line-height: 1; } .frame__article { line-height: 1; } .frame__github, .frame__demos a:not(:last-child) { margin-right: 1rem; } .frame__demos { margin: 1rem 0; } .frame__demo--current, .frame__demo--current:hover { color: var(--color-text); } .content { display: flex; flex-direction: column; width: 100vw; height: calc(100vh - 13rem); position: relative; justify-content: center; align-items: center; } .content__title { position: relative; color: var(--color-title); font-size: 10vw; text-transform: uppercase; margin: 0; perspective: 1000px; } .content__title span { display: inline-block; white-space: pre; transform-origin: 50% -50%; } .content__subtitle { position: relative; margin: 0; color: var(--color-subtitle); } .landscape { position: absolute; top: 0; left: 0; } .overlay { position: fixed; width: 100%; height: 100%; top: 0; left: 0; background: #000; } @media screen and (min-width: 53em) { .frame { position: fixed; text-align: left; z-index: 10000; top: 0; left: 0; display: grid; align-content: space-between; width: 100%; max-width: none; height: 100vh; padding: 2rem; pointer-events: none; grid-template-columns: 25% 50% 25%; grid-template-rows: auto auto auto; grid-template-areas: 'previous title github' '... ... ...' '... demos ...'; } .frame__title-wrap { grid-area: title; justify-self: center; display: flex; } .frame__title { margin: 0 0.5rem 0 0; padding: 0 0.5rem 0 0; position: relative; } .frame__title::after { content: ''; width: 1px; height: 1.1rem; position: absolute; right: 0; top: 50%; margin-top: -0.55rem; background: currentColor; } .frame__github { grid-area: github; justify-self: end; margin: 0; } .frame__demos { margin: 0; grid-area: demos; justify-self: center; } .frame__previous { grid-area: previous; padding: 0; justify-self: start; } .frame a { pointer-events: auto; } .content { height: 100vh; padding: 0 0 10rem 0; justify-content: flex-end; } } ================================================ FILE: index.html ================================================ Interactive Landscape | Demo 1 | Codrops

Interactive Landscape

Article
GitHub Previous demo

Upgrade

Version 5.5

================================================ FILE: index2.html ================================================ Interactive Landscape | Demo 2 | Codrops

Interactive Landscape

Article
GitHub Previous demo

Upgrade

Version 5.5

================================================ FILE: index3.html ================================================ Interactive Landscape | Demo 3 | Codrops

Interactive Landscape

Article
GitHub Previous demo

Upgrade

Version 5.5

================================================ FILE: js/demo1.js ================================================ createLandscape({ palleteImage:'img/pallete5.png' }) function createLandscape(params){ var container = document.querySelector(".landscape") var width = window.innerWidth; var height = window.innerHeight; var scene, renderer, camera; var terrain; var mouse = { x:0, y:0, xDamped:0, yDamped:0 }; var isMobile = typeof window.orientation !== 'undefined' init(); function init(){ sceneSetup(); sceneElements(); sceneTextures(); render(); if(isMobile) window.addEventListener("touchmove", onInputMove, {passive:false}) else window.addEventListener("mousemove", onInputMove) window.addEventListener("resize", resize) resize() } function sceneSetup(){ scene = new THREE.Scene(); var fogColor = new THREE.Color( 0xffffff ) scene.background = fogColor; scene.fog = new THREE.Fog(fogColor, 10, 400); sky() camera = new THREE.PerspectiveCamera(60, width / height, .1, 10000); camera.position.y = 8; camera.position.z = 4; ambientLight = new THREE.AmbientLight(0xffffff, 1); scene.add(ambientLight) renderer = new THREE.WebGLRenderer( { canvas:container, antialias:true } ); renderer.setPixelRatio = devicePixelRatio; renderer.setSize(width, height); } function sceneElements(){ var geometry = new THREE.PlaneBufferGeometry(100, 400, 400, 400); var uniforms = { time: { type: "f", value: 0.0 }, distortCenter: { type: "f", value: 0.1 }, roadWidth: { type: "f", value: 0.5 }, pallete:{ type: "t", value: null}, speed: { type: "f", value: 0.5 }, maxHeight: { type: "f", value: 10.0 }, color:new THREE.Color(1, 1, 1) } var material = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.merge([ THREE.ShaderLib.basic.uniforms, uniforms ]), vertexShader: document.getElementById( 'custom-vertex' ).textContent, fragmentShader: document.getElementById( 'custom-fragment' ).textContent, wireframe:false, fog:true }); terrain = new THREE.Mesh(geometry, material); terrain.position.z = -180; terrain.rotation.x = -Math.PI / 2 scene.add(terrain) } function sceneTextures(){ // pallete new THREE.TextureLoader().load( params.palleteImage, function(texture){ terrain.material.uniforms.pallete.value = texture; terrain.material.needsUpdate = true; }); } function sky(){ sky = new THREE.Sky(); sky.scale.setScalar( 450000 ); sky.material.uniforms.turbidity.value = 20; sky.material.uniforms.rayleigh.value = 0; sky.material.uniforms.luminance.value = 1; sky.material.uniforms.mieCoefficient.value = 0.01; sky.material.uniforms.mieDirectionalG.value = 0.8; scene.add( sky ); sunSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 20000, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ); sunSphere.visible = false; scene.add( sunSphere ); var theta = Math.PI * ( -0.02 ); var phi = 2 * Math.PI * ( -.25 ); sunSphere.position.x = 400000 * Math.cos( phi ); sunSphere.position.y = 400000 * Math.sin( phi ) * Math.sin( theta ); sunSphere.position.z = 400000 * Math.sin( phi ) * Math.cos( theta ); sky.material.uniforms.sunPosition.value.copy( sunSphere.position ); } function resize(){ width = window.innerWidth height = window.innerHeight camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize( width, height ); } function onInputMove(e){ e.preventDefault(); var x, y if(e.type == "mousemove"){ x = e.clientX; y = e.clientY; }else{ x = e.changedTouches[0].clientX y = e.changedTouches[0].clientY } mouse.x = x; mouse.y = y; } function render(){ requestAnimationFrame(render) // damping mouse for smoother interaction mouse.xDamped = lerp(mouse.xDamped, mouse.x, 0.1); mouse.yDamped = lerp(mouse.yDamped, mouse.y, 0.1); var time = performance.now() * 0.001 terrain.material.uniforms.time.value = time; terrain.material.uniforms.distortCenter.value = map(mouse.xDamped, 0, width, -0.1, 0.1); terrain.material.uniforms.roadWidth.value = map(mouse.yDamped, 0, height, -0.5, 2.5); renderer.render(scene, camera) } function map (value, start1, stop1, start2, stop2) { return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)) } function lerp (start, end, amt){ return (1 - amt) * start + amt * end } } const getRandomNumber = (min, max) => (Math.random() * (max - min) + min); animateTitles(); function animateTitles() { const overlay = document.querySelector('.overlay'); const title = document.querySelector('.content__title'); charming(title); const titleLetters = Array.from(title.querySelectorAll('span')); TweenMax.to(overlay, 2, { ease: Quad.easeOut, opacity: 0 }); TweenMax.set(titleLetters, {opacity: 0}); TweenMax.staggerTo(titleLetters, 1.5, { ease: Expo.easeOut, startAt: {rotationX: -100, z: -1000}, opacity: 1, rotationX: 0, z: 0 }, 0.1); const subtitle = document.querySelector('.content__subtitle'); TweenMax.set(subtitle, {opacity: 0}); TweenMax.to(subtitle, 1.5, { ease: Expo.easeOut, startAt: {y: 30}, opacity: 1, y: 0 }); const glitch = (el,cycles) => { if ( cycles === 0 || cycles > 3 ) return; TweenMax.set(el, { x: getRandomNumber(-20,20), y: getRandomNumber(-20,20), color: ['#95dc77','#f3eb8a','#f9b97f'][cycles-1] }); setTimeout(() => { TweenMax.set(el, {x: 0, y: 0, color: '#fff'}); glitch(el, cycles-1); }, getRandomNumber(20,100)); }; const loop = (startAt) => { this.timeout = setTimeout(() => { const titleLettersShuffled = titleLetters.sort((a,b) => 0.5 - Math.random()); const lettersSet = titleLettersShuffled.slice(0, getRandomNumber(1,titleLetters.length+1)); for (let i = 0, len = lettersSet.length; i < len-1; ++i) { glitch(lettersSet[i], 3); } loop(); }, startAt || getRandomNumber(500, 3000)); } loop(1500); } ================================================ FILE: js/demo2.js ================================================ createLandscape({ palleteImage:'img/pallete6.png' }) function createLandscape(params){ var container = document.querySelector(".landscape") var width = window.innerWidth; var height = window.innerHeight; var scene, renderer, camera; var terrain; var mouse = { x:0, y:0, xDamped:0, yDamped:0 }; var isMobile = typeof window.orientation !== 'undefined' init(); function init(){ sceneSetup(); sceneElements(); sceneTextures(); render(); if(isMobile) window.addEventListener("touchmove", onInputMove, {passive:false}) else window.addEventListener("mousemove", onInputMove) window.addEventListener("resize", resize) resize() } function sceneSetup(){ scene = new THREE.Scene(); var fogColor = new THREE.Color( 0x000000 ) scene.background = fogColor; scene.fog = new THREE.Fog(fogColor, 10, 400); sky() camera = new THREE.PerspectiveCamera(60, width / height, .1, 10000); camera.position.y = 8; camera.position.z = 4; ambientLight = new THREE.AmbientLight(0xffffff, 1); scene.add(ambientLight) renderer = new THREE.WebGLRenderer( { canvas:container, antialias:true } ); renderer.setPixelRatio = devicePixelRatio; renderer.setSize(width, height); } function sceneElements(){ var geometry = new THREE.PlaneBufferGeometry(100, 400, 400, 400); var uniforms = { time: { type: "f", value: 0.0 }, distortCenter: { type: "f", value: 0.1 }, roadWidth: { type: "f", value: 0.5 }, pallete:{ type: "t", value: null}, speed: { type: "f", value: 1 }, maxHeight: { type: "f", value: 10.0 }, color:new THREE.Color(1, 1, 1) } var material = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.merge([ THREE.ShaderLib.basic.uniforms, uniforms ]), vertexShader: document.getElementById( 'custom-vertex' ).textContent, fragmentShader: document.getElementById( 'custom-fragment' ).textContent, wireframe:false, fog:true }); terrain = new THREE.Mesh(geometry, material); terrain.position.z = -180; terrain.rotation.x = -Math.PI / 2 scene.add(terrain) } function sceneTextures(){ // pallete new THREE.TextureLoader().load( params.palleteImage, function(texture){ terrain.material.uniforms.pallete.value = texture; terrain.material.needsUpdate = true; }); } function sky(){ sky = new THREE.Sky(); sky.scale.setScalar( 450000 ); sky.material.uniforms.turbidity.value = 1; sky.material.uniforms.rayleigh.value = 0.01; sky.material.uniforms.luminance.value = 1; sky.material.uniforms.mieCoefficient.value = 0.0003; sky.material.uniforms.mieDirectionalG.value = 0.99995; scene.add( sky ); sunSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 20000, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ); sunSphere.visible = false; scene.add( sunSphere ); var theta = Math.PI * ( -0.03 ); var phi = 2 * Math.PI * ( -.25 ); sunSphere.position.x = 400000 * Math.cos( phi ); sunSphere.position.y = 400000 * Math.sin( phi ) * Math.sin( theta ); sunSphere.position.z = 400000 * Math.sin( phi ) * Math.cos( theta ); sky.material.uniforms.sunPosition.value.copy( sunSphere.position ); } function resize(){ width = window.innerWidth height = window.innerHeight camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize( width, height ); } function onInputMove(e){ e.preventDefault(); var x, y if(e.type == "mousemove"){ x = e.clientX; y = e.clientY; }else{ x = e.changedTouches[0].clientX y = e.changedTouches[0].clientY } mouse.x = x; mouse.y = y; } function render(){ requestAnimationFrame(render) // damping mouse for smoother interaction mouse.xDamped = lerp(mouse.xDamped, mouse.x, 0.1); mouse.yDamped = lerp(mouse.yDamped, mouse.y, 0.1); var time = performance.now() * 0.001 terrain.material.uniforms.time.value = time; terrain.material.uniforms.distortCenter.value = Math.sin(time) * 0.1; terrain.material.uniforms.maxHeight.value = map(mouse.yDamped, 0, height, 20, 5); renderer.render(scene, camera) } function map (value, start1, stop1, start2, stop2) { return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)) } function lerp (start, end, amt){ return (1 - amt) * start + amt * end } } const getRandomNumber = (min, max) => (Math.random() * (max - min) + min); animateTitles(); function animateTitles() { const overlay = document.querySelector('.overlay'); const title = document.querySelector('.content__title'); charming(title); const titleLetters = Array.from(title.querySelectorAll('span')); TweenMax.to(overlay, 2, { ease: Quad.easeOut, opacity: 0 }); TweenMax.set(titleLetters, {opacity: 0}); TweenMax.staggerTo(titleLetters, 1, { ease: Expo.easeOut, startAt: {rotationX: -100, z: -1000}, opacity: 1, rotationX: 0, z: 0 }, 0.1); const subtitle = document.querySelector('.content__subtitle'); TweenMax.set(subtitle, {opacity: 0}); TweenMax.to(subtitle, 1.5, { ease: Expo.easeOut, startAt: {y: 30}, opacity: 1, y: 0 }); const glitch = (el,cycles) => { if ( cycles === 0 || cycles > 3 ) return; TweenMax.set(el, { x: getRandomNumber(-20,20), y: getRandomNumber(-20,20), color: ['#7aaac3','#55276d','#111'][cycles-1] }); setTimeout(() => { TweenMax.set(el, {x: 0, y: 0, color: '#fff'}); glitch(el, cycles-1); }, getRandomNumber(20,100)); }; const loop = (startAt) => { this.timeout = setTimeout(() => { const titleLettersShuffled = titleLetters.sort((a,b) => 0.5 - Math.random()); const lettersSet = titleLettersShuffled.slice(0, getRandomNumber(1,titleLetters.length+1)); for (let i = 0, len = lettersSet.length; i < len-1; ++i) { glitch(lettersSet[i], 3); } loop(); }, startAt || getRandomNumber(500, 3000)); } loop(1500); } ================================================ FILE: js/demo3.js ================================================ createLandscape({ palleteImage:'img/pallete.png' }) function createLandscape(params){ var container = document.querySelector(".landscape") var width = window.innerWidth; var height = window.innerHeight; var scene, renderer, camera; var terrain; var mouse = { x:0, y:0, xDamped:0, yDamped:0 }; var isMobile = typeof window.orientation !== 'undefined' init(); function init(){ sceneSetup(); sceneElements(); sceneTextures(); render(); if(isMobile) window.addEventListener("touchmove", onInputMove, {passive:false}) else window.addEventListener("mousemove", onInputMove) window.addEventListener("resize", resize) resize() } function sceneSetup(){ scene = new THREE.Scene(); var fogColor = new THREE.Color( 0x333333 ) scene.background = fogColor; scene.fog = new THREE.Fog(fogColor, 0, 400); sky() camera = new THREE.PerspectiveCamera(60, width / height, .1, 10000); camera.position.y = 8; camera.position.z = 4; ambientLight = new THREE.AmbientLight(0xffffff, 1); scene.add(ambientLight) renderer = new THREE.WebGLRenderer( { canvas:container, antialias:true } ); renderer.setPixelRatio = devicePixelRatio; renderer.setSize(width, height); } function sceneElements(){ var geometry = new THREE.PlaneBufferGeometry(100, 400, 400, 400); var uniforms = { time: { type: "f", value: 0.0 }, scroll: { type: "f", value: 0.0 }, distortCenter: { type: "f", value: 0.1 }, roadWidth: { type: "f", value: 0.5 }, pallete:{ type: "t", value: null}, speed: { type: "f", value: 3 }, maxHeight: { type: "f", value: 10.0 }, color:new THREE.Color(1, 1, 1) } var material = new THREE.ShaderMaterial({ uniforms: THREE.UniformsUtils.merge([ THREE.ShaderLib.basic.uniforms, uniforms ]), vertexShader: document.getElementById( 'custom-vertex' ).textContent, fragmentShader: document.getElementById( 'custom-fragment' ).textContent, wireframe:false, fog:true }); terrain = new THREE.Mesh(geometry, material); terrain.position.z = -180; terrain.rotation.x = -Math.PI / 2 scene.add(terrain) } function sceneTextures(){ // pallete new THREE.TextureLoader().load( params.palleteImage, function(texture){ terrain.material.uniforms.pallete.value = texture; terrain.material.needsUpdate = true; }); } function sky(){ sky = new THREE.Sky(); sky.scale.setScalar( 450000 ); sky.material.uniforms.turbidity.value = 13; sky.material.uniforms.rayleigh.value = 1.2; sky.material.uniforms.luminance.value = 1; sky.material.uniforms.mieCoefficient.value = 0.1; sky.material.uniforms.mieDirectionalG.value = 0.58; scene.add( sky ); sunSphere = new THREE.Mesh( new THREE.SphereBufferGeometry( 20000, 16, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) ); sunSphere.visible = false; scene.add( sunSphere ); var theta = Math.PI * ( -0.002 ); var phi = 2 * Math.PI * ( -.25 ); sunSphere.position.x = 400000 * Math.cos( phi ); sunSphere.position.y = 400000 * Math.sin( phi ) * Math.sin( theta ); sunSphere.position.z = 400000 * Math.sin( phi ) * Math.cos( theta ); sky.material.uniforms.sunPosition.value.copy( sunSphere.position ); } function resize(){ width = window.innerWidth height = window.innerHeight camera.aspect = width / height; camera.updateProjectionMatrix(); renderer.setSize( width, height ); } function onInputMove(e){ e.preventDefault(); var x, y if(e.type == "mousemove"){ x = e.clientX; y = e.clientY; }else{ x = e.changedTouches[0].clientX y = e.changedTouches[0].clientY } mouse.x = x; mouse.y = y; } function render(){ requestAnimationFrame(render) // damping mouse for smoother interaction mouse.xDamped = lerp(mouse.xDamped, mouse.x, 0.1); mouse.yDamped = lerp(mouse.yDamped, mouse.y, 0.1); var time = performance.now() * 0.001 terrain.material.uniforms.time.value = time terrain.material.uniforms.scroll.value = time + map(mouse.yDamped, 0, height, 0, 4); terrain.material.uniforms.distortCenter.value = Math.sin(time) * 0.1; terrain.material.uniforms.roadWidth.value = map(mouse.xDamped, 0, width, 1, 4.5); camera.position.y = map(mouse.yDamped, 0, height, 4, 11); renderer.render(scene, camera) } function map (value, start1, stop1, start2, stop2) { return start2 + (stop2 - start2) * ((value - start1) / (stop1 - start1)) } function lerp (start, end, amt){ return (1 - amt) * start + amt * end } } const getRandomNumber = (min, max) => (Math.random() * (max - min) + min); animateTitles(); function animateTitles() { const overlay = document.querySelector('.overlay'); const title = document.querySelector('.content__title'); charming(title); const titleLetters = Array.from(title.querySelectorAll('span')); TweenMax.to(overlay, 2, { ease: Quad.easeOut, opacity: 0 }); TweenMax.set(titleLetters, {opacity: 0}); TweenMax.staggerTo(titleLetters, 1.5, { ease: Expo.easeOut, startAt: {rotationX: -100, z: -1000}, opacity: 1, rotationX: 0, z: 0 }, 0.1); const subtitle = document.querySelector('.content__subtitle'); TweenMax.set(subtitle, {opacity: 0}); TweenMax.to(subtitle, 1.5, { ease: Expo.easeOut, startAt: {y: 30}, opacity: 1, y: 0 }); const glitch = (el,cycles) => { if ( cycles === 0 || cycles > 3 ) return; TweenMax.set(el, { x: getRandomNumber(-20,20), y: getRandomNumber(-20,20), color: ['#f4d339','#df003f','#111111'][cycles-1] }); setTimeout(() => { TweenMax.set(el, {x: 0, y: 0, color: '#fff'}); glitch(el, cycles-1); }, getRandomNumber(20,100)); }; const loop = (startAt) => { this.timeout = setTimeout(() => { const titleLettersShuffled = titleLetters.sort((a,b) => 0.5 - Math.random()); const lettersSet = titleLettersShuffled.slice(0, getRandomNumber(1,titleLetters.length+1)); for (let i = 0, len = lettersSet.length; i < len-1; ++i) { glitch(lettersSet[i], 3); } loop(); }, startAt || getRandomNumber(500, 3000)); } loop(1500); } ================================================ FILE: js/vendor/Sky.js ================================================ /** * @author zz85 / https://github.com/zz85 * * Based on "A Practical Analytic Model for Daylight" * aka The Preetham Model, the de facto standard analytic skydome model * http://www.cs.utah.edu/~shirley/papers/sunsky/sunsky.pdf * * First implemented by Simon Wallner * http://www.simonwallner.at/projects/atmospheric-scattering * * Improved by Martin Upitis * http://blenderartists.org/forum/showthread.php?245954-preethams-sky-impementation-HDR * * Three.js integration by zz85 http://twitter.com/blurspline */ THREE.Sky = function () { var shader = THREE.Sky.SkyShader; var material = new THREE.ShaderMaterial( { fragmentShader: shader.fragmentShader, vertexShader: shader.vertexShader, uniforms: THREE.UniformsUtils.clone( shader.uniforms ), side: THREE.BackSide } ); THREE.Mesh.call( this, new THREE.BoxBufferGeometry( 1, 1, 1 ), material ); }; THREE.Sky.prototype = Object.create( THREE.Mesh.prototype ); THREE.Sky.SkyShader = { uniforms: { luminance: { value: 1 }, turbidity: { value: 2 }, rayleigh: { value: 1 }, mieCoefficient: { value: 0.005 }, mieDirectionalG: { value: 0.8 }, sunPosition: { value: new THREE.Vector3() } }, vertexShader: [ 'uniform vec3 sunPosition;', 'uniform float rayleigh;', 'uniform float turbidity;', 'uniform float mieCoefficient;', 'varying vec3 vWorldPosition;', 'varying vec3 vSunDirection;', 'varying float vSunfade;', 'varying vec3 vBetaR;', 'varying vec3 vBetaM;', 'varying float vSunE;', 'const vec3 up = vec3( 0.0, 1.0, 0.0 );', // constants for atmospheric scattering 'const float e = 2.71828182845904523536028747135266249775724709369995957;', 'const float pi = 3.141592653589793238462643383279502884197169;', // wavelength of used primaries, according to preetham 'const vec3 lambda = vec3( 680E-9, 550E-9, 450E-9 );', // this pre-calcuation replaces older TotalRayleigh(vec3 lambda) function: // (8.0 * pow(pi, 3.0) * pow(pow(n, 2.0) - 1.0, 2.0) * (6.0 + 3.0 * pn)) / (3.0 * N * pow(lambda, vec3(4.0)) * (6.0 - 7.0 * pn)) 'const vec3 totalRayleigh = vec3( 5.804542996261093E-6, 1.3562911419845635E-5, 3.0265902468824876E-5 );', // mie stuff // K coefficient for the primaries 'const float v = 4.0;', 'const vec3 K = vec3( 0.686, 0.678, 0.666 );', // MieConst = pi * pow( ( 2.0 * pi ) / lambda, vec3( v - 2.0 ) ) * K 'const vec3 MieConst = vec3( 1.8399918514433978E14, 2.7798023919660528E14, 4.0790479543861094E14 );', // earth shadow hack // cutoffAngle = pi / 1.95; 'const float cutoffAngle = 1.6110731556870734;', 'const float steepness = 1.5;', 'const float EE = 1000.0;', 'float sunIntensity( float zenithAngleCos ) {', ' zenithAngleCos = clamp( zenithAngleCos, -1.0, 1.0 );', ' return EE * max( 0.0, 1.0 - pow( e, -( ( cutoffAngle - acos( zenithAngleCos ) ) / steepness ) ) );', '}', 'vec3 totalMie( float T ) {', ' float c = ( 0.2 * T ) * 10E-18;', ' return 0.434 * c * MieConst;', '}', 'void main() {', ' vec4 worldPosition = modelMatrix * vec4( position, 1.0 );', ' vWorldPosition = worldPosition.xyz;', ' gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );', ' gl_Position.z = gl_Position.w;', // set z to camera.far ' vSunDirection = normalize( sunPosition );', ' vSunE = sunIntensity( dot( vSunDirection, up ) );', ' vSunfade = 1.0 - clamp( 1.0 - exp( ( sunPosition.y / 450000.0 ) ), 0.0, 1.0 );', ' float rayleighCoefficient = rayleigh - ( 1.0 * ( 1.0 - vSunfade ) );', // extinction (absorbtion + out scattering) // rayleigh coefficients ' vBetaR = totalRayleigh * rayleighCoefficient;', // mie coefficients ' vBetaM = totalMie( turbidity ) * mieCoefficient;', '}' ].join( '\n' ), fragmentShader: [ 'varying vec3 vWorldPosition;', 'varying vec3 vSunDirection;', 'varying float vSunfade;', 'varying vec3 vBetaR;', 'varying vec3 vBetaM;', 'varying float vSunE;', 'uniform float luminance;', 'uniform float mieDirectionalG;', 'const vec3 cameraPos = vec3( 0.0, 0.0, 0.0 );', // constants for atmospheric scattering 'const float pi = 3.141592653589793238462643383279502884197169;', 'const float n = 1.0003;', // refractive index of air 'const float N = 2.545E25;', // number of molecules per unit volume for air at // 288.15K and 1013mb (sea level -45 celsius) // optical length at zenith for molecules 'const float rayleighZenithLength = 8.4E3;', 'const float mieZenithLength = 1.25E3;', 'const vec3 up = vec3( 0.0, 1.0, 0.0 );', // 66 arc seconds -> degrees, and the cosine of that 'const float sunAngularDiameterCos = 0.999956676946448443553574619906976478926848692873900859324;', // 3.0 / ( 16.0 * pi ) 'const float THREE_OVER_SIXTEENPI = 0.05968310365946075;', // 1.0 / ( 4.0 * pi ) 'const float ONE_OVER_FOURPI = 0.07957747154594767;', 'float rayleighPhase( float cosTheta ) {', ' return THREE_OVER_SIXTEENPI * ( 1.0 + pow( cosTheta, 2.0 ) );', '}', 'float hgPhase( float cosTheta, float g ) {', ' float g2 = pow( g, 2.0 );', ' float inverse = 1.0 / pow( 1.0 - 2.0 * g * cosTheta + g2, 1.5 );', ' return ONE_OVER_FOURPI * ( ( 1.0 - g2 ) * inverse );', '}', // Filmic ToneMapping http://filmicgames.com/archives/75 'const float A = 0.15;', 'const float B = 0.50;', 'const float C = 0.10;', 'const float D = 0.20;', 'const float E = 0.02;', 'const float F = 0.30;', 'const float whiteScale = 1.0748724675633854;', // 1.0 / Uncharted2Tonemap(1000.0) 'vec3 Uncharted2Tonemap( vec3 x ) {', ' return ( ( x * ( A * x + C * B ) + D * E ) / ( x * ( A * x + B ) + D * F ) ) - E / F;', '}', 'void main() {', // optical length // cutoff angle at 90 to avoid singularity in next formula. ' float zenithAngle = acos( max( 0.0, dot( up, normalize( vWorldPosition - cameraPos ) ) ) );', ' float inverse = 1.0 / ( cos( zenithAngle ) + 0.15 * pow( 93.885 - ( ( zenithAngle * 180.0 ) / pi ), -1.253 ) );', ' float sR = rayleighZenithLength * inverse;', ' float sM = mieZenithLength * inverse;', // combined extinction factor ' vec3 Fex = exp( -( vBetaR * sR + vBetaM * sM ) );', // in scattering ' float cosTheta = dot( normalize( vWorldPosition - cameraPos ), vSunDirection );', ' float rPhase = rayleighPhase( cosTheta * 0.5 + 0.5 );', ' vec3 betaRTheta = vBetaR * rPhase;', ' float mPhase = hgPhase( cosTheta, mieDirectionalG );', ' vec3 betaMTheta = vBetaM * mPhase;', ' vec3 Lin = pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * ( 1.0 - Fex ), vec3( 1.5 ) );', ' Lin *= mix( vec3( 1.0 ), pow( vSunE * ( ( betaRTheta + betaMTheta ) / ( vBetaR + vBetaM ) ) * Fex, vec3( 1.0 / 2.0 ) ), clamp( pow( 1.0 - dot( up, vSunDirection ), 5.0 ), 0.0, 1.0 ) );', // nightsky ' vec3 direction = normalize( vWorldPosition - cameraPos );', ' float theta = acos( direction.y ); // elevation --> y-axis, [-pi/2, pi/2]', ' float phi = atan( direction.z, direction.x ); // azimuth --> x-axis [-pi/2, pi/2]', ' vec2 uv = vec2( phi, theta ) / vec2( 2.0 * pi, pi ) + vec2( 0.5, 0.0 );', ' vec3 L0 = vec3( 0.1 ) * Fex;', // composition + solar disc ' float sundisk = smoothstep( sunAngularDiameterCos, sunAngularDiameterCos + 0.00002, cosTheta );', ' L0 += ( vSunE * 19000.0 * Fex ) * sundisk;', ' vec3 texColor = ( Lin + L0 ) * 0.04 + vec3( 0.0, 0.0003, 0.00075 );', ' vec3 curr = Uncharted2Tonemap( ( log2( 2.0 / pow( luminance, 4.0 ) ) ) * texColor );', ' vec3 color = curr * whiteScale;', ' vec3 retColor = pow( color, vec3( 1.0 / ( 1.2 + ( 1.2 * vSunfade ) ) ) );', ' gl_FragColor = vec4( retColor, 1.0 );', '}' ].join( '\n' ) };