Add volumetric clouds3d renderer
Raymarches a horizontal cloud layer (28 steps, 4-octave 3D fBm).
Camera path is sinusoidal in altitude so the view dips into the
cloud deck at intervals for a 'fly through the edges' feel.
Density threshold from weather: sunny -> sparse, stormy -> overcast.
Renderer is selected via CONFIG.renderer ('clouds2d' or 'clouds3d').
This commit is contained in:
1 parent
3812f80f40
commit
0648676974
4 files changed
+289
-12
No files matched your search
+10
-6
@@ -83,7 +83,7 @@ window.CONFIG = {
|
|||||||
debug: 0,
|
debug: 0,
|
||||||
theme: 'ash',
|
theme: 'ash',
|
||||||
units: 'metric',
|
units: 'metric',
|
||||||
renderer: 'clouds2d', // 'vanta' or 'clouds2d'
|
renderer: 'clouds3d', // 'vanta', 'clouds2d', or 'clouds3d'
|
||||||
language: 'de',
|
language: 'de',
|
||||||
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
||||||
location: {
|
location: {
|
||||||
@@ -1087,6 +1087,7 @@ else window.SunCalc = SunCalc;
|
|||||||
<script>
|
<script>
|
||||||
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports._vantaEffect=t():e._vantaEffect=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function i(o){if(t[o])return t[o].exports;var s=t[o]={i:o,l:!1,exports:{}};return e[o].call(s.exports,s,s.exports,i),s.l=!0,s.exports}return i.m=e,i.c=t,i.d=function(e,t,o){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)i.d(o,s,function(t){return e[t]}.bind(null,s));return o},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=7)}([function(e,t,i){"use strict";function o(e,t){for(let i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e}function s(){return"undefined"!=typeof navigator?/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<600:null}i.d(t,"c",(function(){return o})),i.d(t,"e",(function(){return s})),i.d(t,"i",(function(){return n})),i.d(t,"h",(function(){return r})),i.d(t,"g",(function(){return h})),i.d(t,"f",(function(){return a})),i.d(t,"a",(function(){return u})),i.d(t,"b",(function(){return c})),i.d(t,"d",(function(){return l})),Number.prototype.clamp=function(e,t){return Math.min(Math.max(this,e),t)};const n=e=>e[Math.floor(Math.random()*e.length)];function r(e,t){return null==e&&(e=0),null==t&&(t=1),e+Math.random()*(t-e)}function h(e,t){return null==e&&(e=0),null==t&&(t=1),Math.floor(e+Math.random()*(t-e+1))}const a=e=>document.querySelector(e),u=e=>"number"==typeof e?"#"+("00000"+e.toString(16)).slice(-6):e,c=(e,t=1)=>{const i=u(e),o=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(i),s=o?{r:parseInt(o[1],16),g:parseInt(o[2],16),b:parseInt(o[3],16)}:null;return"rgba("+s.r+","+s.g+","+s.b+","+t+")"},l=e=>.299*e.r+.587*e.g+.114*e.b},function(e,t,i){"use strict";i.d(t,"a",(function(){return r}));var o=i(0);const s="object"==typeof window;let n=s&&window.THREE||{};s&&!window.VANTA&&(window.VANTA={});const r=s&&window.VANTA||{};r.register=(e,t)=>r[e]=e=>new t(e),r.version="0.5.21";const h=function(){return Array.prototype.unshift.call(arguments,"[VANTA]"),console.error.apply(this,arguments)};r.VantaBase=class{constructor(e={}){if(!s)return!1;r.current=this,this.windowMouseMoveWrapper=this.windowMouseMoveWrapper.bind(this),this.windowTouchWrapper=this.windowTouchWrapper.bind(this),this.windowGyroWrapper=this.windowGyroWrapper.bind(this),this.resize=this.resize.bind(this),this.animationLoop=this.animationLoop.bind(this),this.restart=this.restart.bind(this);const t="function"==typeof this.getDefaultOptions?this.getDefaultOptions():this.defaultOptions;if(this.options=Object(o.c)({mouseControls:!0,touchControls:!0,gyroControls:!1,minHeight:200,minWidth:200,scale:1,scaleMobile:1},t),(e instanceof HTMLElement||"string"==typeof e)&&(e={el:e}),Object(o.c)(this.options,e),this.options.THREE&&(n=this.options.THREE),this.el=this.options.el,null==this.el)h('Instance needs "el" param!');else if(!(this.options.el instanceof HTMLElement)){const e=this.el;if(this.el=Object(o.f)(e),!this.el)return void h("Cannot find element",e)}this.prepareEl(),this.initThree(),this.setSize();try{this.init()}catch(e){return h("Init error",e),this.renderer&&this.renderer.domElement&&this.el.removeChild(this.renderer.domElement),void(this.options.backgroundColor&&(console.log("[VANTA] Falling back to backgroundColor"),this.el.style.background=Object(o.a)(this.options.backgroundColor)))}this.initMouse(),this.resize(),this.animationLoop();const i=window.addEventListener;i("resize",this.resize),window.requestAnimationFrame(this.resize),this.options.mouseControls&&(i("scroll",this.windowMouseMoveWrapper),i("mousemove",this.windowMouseMoveWrapper)),this.options.touchControls&&(i("touchstart",this.windowTouchWrapper),i("touchmove",this.windowTouchWrapper)),this.options.gyroControls&&i("deviceorientation",this.windowGyroWrapper)}setOptions(e={}){Object(o.c)(this.options,e),this.triggerMouseMove()}prepareEl(){let e,t;if("undefined"!=typeof Node&&Node.TEXT_NODE)for(e=0;e<this.el.childNodes.length;e++){const t=this.el.childNodes[e];if(t.nodeType===Node.TEXT_NODE){const e=document.createElement("span");e.textContent=t.textContent,t.parentElement.insertBefore(e,t),t.remove()}}for(e=0;e<this.el.children.length;e++)t=this.el.children[e],"static"===getComputedStyle(t).position&&(t.style.position="relative"),"auto"===getComputedStyle(t).zIndex&&(t.style.zIndex=1);"Line truncated
|
!function(e,t){"object"==typeof exports&&"object"==typeof module?module.exports=t():"function"==typeof define&&define.amd?define([],t):"object"==typeof exports?exports._vantaEffect=t():e._vantaEffect=t()}("undefined"!=typeof self?self:this,(function(){return function(e){var t={};function i(o){if(t[o])return t[o].exports;var s=t[o]={i:o,l:!1,exports:{}};return e[o].call(s.exports,s,s.exports,i),s.l=!0,s.exports}return i.m=e,i.c=t,i.d=function(e,t,o){i.o(e,t)||Object.defineProperty(e,t,{enumerable:!0,get:o})},i.r=function(e){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},i.t=function(e,t){if(1&t&&(e=i(e)),8&t)return e;if(4&t&&"object"==typeof e&&e&&e.__esModule)return e;var o=Object.create(null);if(i.r(o),Object.defineProperty(o,"default",{enumerable:!0,value:e}),2&t&&"string"!=typeof e)for(var s in e)i.d(o,s,function(t){return e[t]}.bind(null,s));return o},i.n=function(e){var t=e&&e.__esModule?function(){return e.default}:function(){return e};return i.d(t,"a",t),t},i.o=function(e,t){return Object.prototype.hasOwnProperty.call(e,t)},i.p="",i(i.s=7)}([function(e,t,i){"use strict";function o(e,t){for(let i in t)t.hasOwnProperty(i)&&(e[i]=t[i]);return e}function s(){return"undefined"!=typeof navigator?/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent)||window.innerWidth<600:null}i.d(t,"c",(function(){return o})),i.d(t,"e",(function(){return s})),i.d(t,"i",(function(){return n})),i.d(t,"h",(function(){return r})),i.d(t,"g",(function(){return h})),i.d(t,"f",(function(){return a})),i.d(t,"a",(function(){return u})),i.d(t,"b",(function(){return c})),i.d(t,"d",(function(){return l})),Number.prototype.clamp=function(e,t){return Math.min(Math.max(this,e),t)};const n=e=>e[Math.floor(Math.random()*e.length)];function r(e,t){return null==e&&(e=0),null==t&&(t=1),e+Math.random()*(t-e)}function h(e,t){return null==e&&(e=0),null==t&&(t=1),Math.floor(e+Math.random()*(t-e+1))}const a=e=>document.querySelector(e),u=e=>"number"==typeof e?"#"+("00000"+e.toString(16)).slice(-6):e,c=(e,t=1)=>{const i=u(e),o=/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(i),s=o?{r:parseInt(o[1],16),g:parseInt(o[2],16),b:parseInt(o[3],16)}:null;return"rgba("+s.r+","+s.g+","+s.b+","+t+")"},l=e=>.299*e.r+.587*e.g+.114*e.b},function(e,t,i){"use strict";i.d(t,"a",(function(){return r}));var o=i(0);const s="object"==typeof window;let n=s&&window.THREE||{};s&&!window.VANTA&&(window.VANTA={});const r=s&&window.VANTA||{};r.register=(e,t)=>r[e]=e=>new t(e),r.version="0.5.21";const h=function(){return Array.prototype.unshift.call(arguments,"[VANTA]"),console.error.apply(this,arguments)};r.VantaBase=class{constructor(e={}){if(!s)return!1;r.current=this,this.windowMouseMoveWrapper=this.windowMouseMoveWrapper.bind(this),this.windowTouchWrapper=this.windowTouchWrapper.bind(this),this.windowGyroWrapper=this.windowGyroWrapper.bind(this),this.resize=this.resize.bind(this),this.animationLoop=this.animationLoop.bind(this),this.restart=this.restart.bind(this);const t="function"==typeof this.getDefaultOptions?this.getDefaultOptions():this.defaultOptions;if(this.options=Object(o.c)({mouseControls:!0,touchControls:!0,gyroControls:!1,minHeight:200,minWidth:200,scale:1,scaleMobile:1},t),(e instanceof HTMLElement||"string"==typeof e)&&(e={el:e}),Object(o.c)(this.options,e),this.options.THREE&&(n=this.options.THREE),this.el=this.options.el,null==this.el)h('Instance needs "el" param!');else if(!(this.options.el instanceof HTMLElement)){const e=this.el;if(this.el=Object(o.f)(e),!this.el)return void h("Cannot find element",e)}this.prepareEl(),this.initThree(),this.setSize();try{this.init()}catch(e){return h("Init error",e),this.renderer&&this.renderer.domElement&&this.el.removeChild(this.renderer.domElement),void(this.options.backgroundColor&&(console.log("[VANTA] Falling back to backgroundColor"),this.el.style.background=Object(o.a)(this.options.backgroundColor)))}this.initMouse(),this.resize(),this.animationLoop();const i=window.addEventListener;i("resize",this.resize),window.requestAnimationFrame(this.resize),this.options.mouseControls&&(i("scroll",this.windowMouseMoveWrapper),i("mousemove",this.windowMouseMoveWrapper)),this.options.touchControls&&(i("touchstart",this.windowTouchWrapper),i("touchmove",this.windowTouchWrapper)),this.options.gyroControls&&i("deviceorientation",this.windowGyroWrapper)}setOptions(e={}){Object(o.c)(this.options,e),this.triggerMouseMove()}prepareEl(){let e,t;if("undefined"!=typeof Node&&Node.TEXT_NODE)for(e=0;e<this.el.childNodes.length;e++){const t=this.el.childNodes[e];if(t.nodeType===Node.TEXT_NODE){const e=document.createElement("span");e.textContent=t.textContent,t.parentElement.insertBefore(e,t),t.remove()}}for(e=0;e<this.el.children.length;e++)t=this.el.children[e],"static"===getComputedStyle(t).position&&(t.style.position="relative"),"auto"===getComputedStyle(t).zIndex&&(t.style.zIndex=1);"Line truncated
|
||||||
</script>
|
</script>
|
||||||
|
<script src="vendor/clouds3d.js"></script>
|
||||||
<script>
|
<script>
|
||||||
/* Custom 2D WebGL cloud renderer.
|
/* Custom 2D WebGL cloud renderer.
|
||||||
* Much lighter than Vanta's volumetric raymarching while still
|
* Much lighter than Vanta's volumetric raymarching while still
|
||||||
@@ -1332,7 +1333,8 @@ window.clouds2dRender = function(target) {
|
|||||||
window.__clouds2dStarted = true;
|
window.__clouds2dStarted = true;
|
||||||
try {
|
try {
|
||||||
var noiseDataUri = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEACAYAAABccqhmAAAACXBIWXMAAAsTAAALEwEAmpwYAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAQHCElEQVR42gABBP77AcLEj6YeajfMaCYTB1essIWN/o5kF40gDvp1hY1/QSLfUJscBLiiMqHupXwD3rxHs9OQg7wVULiWQdBmquqFs1RIf4q6/0vckOrgFx+bwJYLQQGYPMqqdnNUniDOKHk6wElB4OscRKzO1tj29dU1s5PXCJXcGe58lXLj+gDD9kqmTlAlsJnLFRXEnaUh/hnkYuBbMp3z18ag9CuzrADPEBXxJxYKqvcPeXezs2V3bXSUpbhhAW7IbBa1VSVg0v9agEcKSIDhc/Ru6SyVzepRops5oGyQpYsovqNbFMNI4KYx9aCKBqmeQ4RZ/i7l08i3zLNF/HX/2KcIEWMhCOHT3EoK6i8UfffDVu+SXTNuhU85cLGkkzcvZxDPZt9+xvdKjmZruBWoyt+98MGpwJTrsF3VL3IJSQANMx2hGQ7L9P8/aAuPx2xRLaYS3Xtz4SCowhjGZjcyAV+hI1Xylr52X0z1ziqmRLQK/CIXGxD9tT7u3nJclhAUasXaAI1WJKnSTXARcujsaG7E4ifBlPH7176vXfJXKCZDmILRVFcOnq1WgHMtV2jiwQvz+vnTBI3Xv+yf9hOzPHXP28I9E68mGRPIlXTJlhyhYI+bPty2OBX51dciZcnGc/DDzynAA1p3bMxi224J9wdUTLYSta6WmQmO7vgq8078vLpfvsBqHrwRZB4BIUl6SEPWgsIpD6PSfhZ8SeT3kiAQfpyPCsMMaf3Ap6GDHagR8rd8ihv0oh65nrLfpVrtR+Qmm1zmG6C6JY9IwcbaGp1joDimH8PBKjP6ECpu9G/Rx7B64Ye+3ZoeyOX4LGrd0X64P7hESRNBePnerLNVORk8N2w5x/1JTzZIIq+9UAb5Zw9sae3ysn4n7qmT1giAT1LZR4DcNiG4bNlXnHYREx3S33rxVNQJGOZZqcC2orwWHe5QAKIfE2yeXugl8K5/n4vReP59e2RbrFTvhemzl/AeWqFj1vlwC40piODp3ca4WbAcS92cCzOScnSJDu6D9EmcTz7oc4p5zHzP60dFBRmgeHem4eeuYXK+3S23i2dswOSYh5Vfc8uFiL6DD8jAjC0jbc2lkIhcrRq3gVm7Ggsbvqas1FvHsRaBoDFVZeSbMZgDFMJUwNctPtrJzPyf3AXigV/vr+u0pJpJtcDZzN+hK+SThh1XYKHit3rFe0FLg5mC6dbnB+ihdkLM//V4Ob6xjzISGe63giYd80rYwTXsXhp6e9eewqCNSMkgJNNkx/8RUrpt3MDKCjHvyw3UQm0WQeRGSOmEBvrHHNOHRRi5UMe/Pdj1Wn95x1RKC2JUrgymQ4V3/35JIaWPQeLR6uOIDU2vC7SqPHOKWjAHBWdHNLGBRs4ID3cCAAEE/vsC59Vplei8dd1XMe8bXXEtu9nSseJpJgBUk6mzTfpyj4E0iHVC8pt7x6ypCMJOTk+yp5zrBZHFmMrhGyA6tquR8y8q8v2DQCNSXLawd8BlBMgLagUg+PODDK7/3IyeMCBW1wG3lZRiDr0Hft7vjL1XifGfSXZK9BeOc2DnBD1idW67HBVHqdI0eJvIvbyfdx7GROsQ9zIdHKNpinje909/H2kz02dh24p055haNrpC82f8Ia+qHQQZxm7mW+7MCco4fPBjLwZ+FaemJ03ZM9gM44ydVgWRDtlL3c6krTKxqG1FIdYcMwJI6c28Dn+pUan2hhq0ow01SDH32DIzV/ntDS4ymhjkFoNjMoadWHUbCWtqSon6ptO8TVA8sozQ6E28//rs/j8n+YNLngjJnW5vsEhwmrVwlyMj7KTmj9NXDfHV6/PRJF8+FTc1BbML9MEG2zW1vuNlMCS6EZE11ejuQhQAV2DZh5ugxXHqOf08DlAKRKUtrp9KhSIayRToVjtuPXgZlDoRuxEzRk4PTu80J6Y4VQMUutbrgi+BQutr1yBChF4h+24yN918OTw7AIXRGsT5DCCRVQthg3/QOP5QAfECSZLATLioePegWQgDOArYeApJnBfTY/YYm8FW9o8L561iUN08kLMhUcQ1MgQjMwBUI2+zvlPE2eGfRrSXMV67hIHUkookfMRZDQrebUbdjtxVbI2ygonRdb8ls7Zy1cEgATwWd7OFYLs9Od43N2/bQbUm5QTJiuzTAD3CCO1s/tfztYX9Q35Oh1zVFX4zR2qqvyDE6PD5dE5QyhLhU7bKhiew7JSR47qUx7g25zKxlBpJlBPHZZi6JA0fdW8NVWXDRexJSp49QNb81rW6XkAc7CRmfqbQXoLcL7etrdbzxuG8yfDFt3rm9EGaJ/dv93JhM/II4Fv0oHSi3gIL/tbVeZfi3cP8C5g4adYSjSSUNNDAQeQjhVT2c157+JB0BrRynr9KfKxttSLNpDnj5m04kDgl2LPVvfvimKyux98VWd2M9Ixd9InypXxKZloXmNT7WzYzBjfCEyVj6++Gxv99kNH/PuOLX4+NRcY46DqxnYNplV/B8eHhOv8kCRUiZUVpkt2opKS92eda1aQQwIZkEFQ/kIVWN7QqjcT/UymG5TO8VqCBJXgI5u9ZOL+GvUeKbBQ+pQJ3jKLXC19EcpoUcAcSF9xULDeeiBs2biDNPrYkZ68bhzkcJIOSBdDff/XJCiPyTTZrhr6HDj4zOdewgFp0XKtkors5QE0jnAJxUfzCLJTRgn4W3y0wGcO3q08dYCxO27uiXIvueR2rBNRvxB6i4RJLBtlfK/S6uMMF6I8Wmp1EQ9ZuQ4exGiAh3QIAAQT++wIE1paaeFjolY6SQvMD0Z8ZgnvjJICqGkwoLUWsnnxxiK1AafPWGKSotLxIHoMBHR9Kz1x+IyCG/hmN1FYau3wOzWfWwGeX467w2QY008m9l7VR/iy1GpknqhhTSgqRTdp5hw2mlRj7845t+u0E3mTXTlNDyihhbhaAgxnymzPfUsJBb+7yfaPU9Wl/sL2Yy6ng1Gt8U4iYHQ0UT55CEVm+WKZMy9M+AZnGNnTP5PNh22zmqe5aAE1gaH3u62Lr/stYzWluU7hlXOs4fDJ4XEVBqcjZW1Uz2kQWLsoWjOodWfi41gAka4Q5tV8Z/pELKf8qNh84vbpKtowT9OnQa557Ah3VieCCE30WgXCyXooB/ZaRceMATXsaglP44Xq5R2nkMPc2uPcM36Jta/qwvzpN4yCt/uB+yBMk23cOQW8VrRMsDzwtUtFXjErTU1Nx0d9mvsN6kfv+xcB3Yate/0qjN0LpgILeZ7498h3S2fxPcpiDuzZARFWN/4uUhnvKt8U+tRediiSJHPUs09TC5VpY8My/TbuBfCNuXx4E+3yMQ0lSAxOkvb1bmEdQwu3D66dV0sekOgv3KYwupK5RlWDr1ylLHLThMLGubtkbbF/i2pUmIGY+GNiwUcTmjWz7b2XWT4wIoYofdPjSMvhejBWknuVSXzpIRAesPPtyzDGhFXDRXLZmRx9VMiN6kCUjczMK29+49MRalVvIT7YJmSG8sYmDzMFDaF0bfthPL63r1IjNIbjKF/IEKVRuTGnfFGEw86KX/DtL2B388hJN9jrgCp3rvPruQhNO9yEo5aA6kzQhYQMrrNdi73bXAiVaxe3yEZMN3E/B2J8Na0uvfdBQwivNi5D8XRN+gdZ8jkvCkpEPz8jNHGPMEmh1lX8E/r94+RdHGtO2Hs+a4c8ScByIq0OKtBI1HtT0/IA/96s+XIpEdhItp/e71xgEgasZltgzMDwDPPZWJWbr2r+NU8qqEfn9VA/z0GobikPEAp0tVh+Zr9xQM3xuxW5ONl1DZH3tpouKHzPxJ8X47MfgzSGIjps4bmX+AZ5p9OepVaGrLmODgNr74920nGVL83SSpislkL+zEtHXf6jeKBnO6Hi2M9LeXO2tNy/OeDIEJe+CWfiIIN/zA/rfrReYPWVEkPMqM+frwsGYa5QLBM2TGYXiw4BmJ/jD4emMYDtsji1iTe5+273Hp0FdPppBUv8FDc/8CR5InpyznvPhoOrdjoTPmdbohzbUtF3uoCoVxR7IXVNJ9GrwE7SgmuNjK7m7mvQoc4/Xuf+g5Na03BigR65nCs4griMNeO5+UHSwIk96bPeLQS/w0cWtZbVlLnNTTiE4kc/63sGza7U+qsV3xNPNAgABBP77A+IN+6iK2Qcss8PwHYKj4P2pfSsPgURbctSlIe6BvxGXudtQLKGZqBrowi/vbx+iNQWp3gRoCiyCVoiV51Xpwcmd0qROuTNQAEdNnZ78mjHQ2eK1G1LZivLZc6s2PuKnK6R1CdiyjWN3KI66sQTyWWYPtzGFWJt6y39DN2waMqJ1AS1Rj4bGjFUqP2rmMslMjSAu9ohJUtM4KmTw6vEvL+Vmg2h/6ysF3UCy3UScv8SoDilUTCmZDHwZsaA2MU+VerKnLZZMLaf5qfXax0aULLXOpYvKPgGdJD1r/TKlog1kODivvTlRQCBF5ytnyK9kXtJpwn0cK4MXwxQpSwq175CpHq4HCIUzzm96Mv1pYHZUKJS87dtptw8fIx1tByzAO7QjOmxqukns7YIOu1eGAkMU5UrrYd891Qvu08S95NHFTgWwuZSOgQLbv84Y0CcgWy6ev5fgt7G4uiSDcPZHHHR4ycAEvKQYVWsJVk0YR0+lmo5/5mMPIOa/pb+cDSEo05AnCpTq2euCxaTJjZsJHSe4hq5h86EN8YiUfPF9EO45ID2VYH+OxnGHaflDuue57EjK2jMlsV2Ps3XScwXu5Eevp48z1rVPADNdAnOtUPlGcComQmgpxCsYLkpuVpoZawWI4//z2fzEo4e6fADQYLine truncated
|
var noiseDataUri = 'data:image/png;base64,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 truncated
|
||||||
var useVanta = !(window.CONFIG && window.CONFIG.renderer === 'clouds2d');
|
var renderer = (window.CONFIG && window.CONFIG.renderer) || 'vanta';
|
||||||
|
var useVanta = (renderer !== 'clouds2d' && renderer !== 'clouds3d');
|
||||||
var effect;
|
var effect;
|
||||||
if (useVanta && typeof VANTA !== 'undefined') {
|
if (useVanta && typeof VANTA !== 'undefined') {
|
||||||
effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
||||||
@@ -1353,14 +1355,16 @@ if (useVanta && typeof VANTA !== 'undefined') {
|
|||||||
texturePath: noiseDataUri
|
texturePath: noiseDataUri
|
||||||
}) || { setOptions: function() {} };
|
}) || { setOptions: function() {} };
|
||||||
} else {
|
} else {
|
||||||
|
// Pick the right renderer based on CONFIG.renderer
|
||||||
|
var _renderFn = (renderer === 'clouds3d' && window.clouds3dRender)
|
||||||
|
? window.clouds3dRender
|
||||||
|
: window.clouds2dRender;
|
||||||
effect = { setOptions: function() {} };
|
effect = { setOptions: function() {} };
|
||||||
function c2dRender() {
|
function c2dRender() {
|
||||||
var c2dCanvas = document.getElementById('vanta');
|
var c2dCanvas = document.getElementById('vanta');
|
||||||
if (!c2dCanvas) return;
|
if (!c2dCanvas) return;
|
||||||
c2dCanvas.width = window.innerWidth;
|
if (typeof _renderFn === 'function') {
|
||||||
c2dCanvas.height = window.innerHeight;
|
_renderFn(c2dCanvas);
|
||||||
if (typeof window.clouds2dRender === 'function') {
|
|
||||||
window.clouds2dRender(c2dCanvas);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c2dRender();
|
c2dRender();
|
||||||
|
|||||||
+9
-5
@@ -474,12 +474,14 @@
|
|||||||
<script src="vendor/three.min.js"></script>
|
<script src="vendor/three.min.js"></script>
|
||||||
<script src="vendor/vanta.clouds.min.js"></script>
|
<script src="vendor/vanta.clouds.min.js"></script>
|
||||||
<script src="vendor/vanta.clouds2.min.js"></script>
|
<script src="vendor/vanta.clouds2.min.js"></script>
|
||||||
|
<script src="vendor/clouds3d.js"></script>
|
||||||
<script src="vendor/clouds2d.js"></script>
|
<script src="vendor/clouds2d.js"></script>
|
||||||
<script>
|
<script>
|
||||||
window.__clouds2dStarted = true;
|
window.__clouds2dStarted = true;
|
||||||
try {
|
try {
|
||||||
var noiseDataUri = 'data:image/png;base64,PLACEHOLDER';
|
var noiseDataUri = 'data:image/png;base64,PLACEHOLDER';
|
||||||
var useVanta = !(window.CONFIG && window.CONFIG.renderer === 'clouds2d');
|
var renderer = (window.CONFIG && window.CONFIG.renderer) || 'vanta';
|
||||||
|
var useVanta = (renderer !== 'clouds2d' && renderer !== 'clouds3d');
|
||||||
var effect;
|
var effect;
|
||||||
if (useVanta && typeof VANTA !== 'undefined') {
|
if (useVanta && typeof VANTA !== 'undefined') {
|
||||||
effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
effect = (VANTA.CLOUDS2 || VANTA.CLOUDS)({
|
||||||
@@ -500,14 +502,16 @@ if (useVanta && typeof VANTA !== 'undefined') {
|
|||||||
texturePath: noiseDataUri
|
texturePath: noiseDataUri
|
||||||
}) || { setOptions: function() {} };
|
}) || { setOptions: function() {} };
|
||||||
} else {
|
} else {
|
||||||
|
// Pick the right renderer based on CONFIG.renderer
|
||||||
|
var _renderFn = (renderer === 'clouds3d' && window.clouds3dRender)
|
||||||
|
? window.clouds3dRender
|
||||||
|
: window.clouds2dRender;
|
||||||
effect = { setOptions: function() {} };
|
effect = { setOptions: function() {} };
|
||||||
function c2dRender() {
|
function c2dRender() {
|
||||||
var c2dCanvas = document.getElementById('vanta');
|
var c2dCanvas = document.getElementById('vanta');
|
||||||
if (!c2dCanvas) return;
|
if (!c2dCanvas) return;
|
||||||
c2dCanvas.width = window.innerWidth;
|
if (typeof _renderFn === 'function') {
|
||||||
c2dCanvas.height = window.innerHeight;
|
_renderFn(c2dCanvas);
|
||||||
if (typeof window.clouds2dRender === 'function') {
|
|
||||||
window.clouds2dRender(c2dCanvas);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
c2dRender();
|
c2dRender();
|
||||||
|
|||||||
@@ -76,7 +76,7 @@ window.CONFIG = {
|
|||||||
debug: 0,
|
debug: 0,
|
||||||
theme: 'ash',
|
theme: 'ash',
|
||||||
units: 'metric',
|
units: 'metric',
|
||||||
renderer: 'clouds2d', // 'vanta' or 'clouds2d'
|
renderer: 'clouds3d', // 'vanta', 'clouds2d', or 'clouds3d'
|
||||||
language: 'de',
|
language: 'de',
|
||||||
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
timeFormat: 'DD. MMMM, YYYY ‒ HH:mm:SS',
|
||||||
location: {
|
location: {
|
||||||
|
|||||||
Vendored
+269
@@ -0,0 +1,269 @@
|
|||||||
|
/* Volumetric 3D cloud renderer.
|
||||||
|
* Raymarches a horizontal cloud layer with the camera flying
|
||||||
|
* slowly above and through the cloud deck. Density threshold
|
||||||
|
* comes from the weather theme, so 'sunny' gives breaks in the
|
||||||
|
* deck and 'stormy' puts you inside overcast most of the time.
|
||||||
|
*
|
||||||
|
* Reads palette + weather + speed from window globals (same
|
||||||
|
* contract as clouds2d.js, theme-agnostic).
|
||||||
|
*/
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
function makeShader(gl, type, src) {
|
||||||
|
var s = gl.createShader(type);
|
||||||
|
gl.shaderSource(s, src);
|
||||||
|
gl.compileShader(s);
|
||||||
|
if (!gl.getShaderParameter(s, gl.COMPILE_STATUS)) {
|
||||||
|
console.error('clouds3d: Shader compile error:', gl.getShaderInfoLog(s));
|
||||||
|
gl.deleteShader(s);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
function makeProgram(gl, vsSrc, fsSrc) {
|
||||||
|
var vs = makeShader(gl, gl.VERTEX_SHADER, vsSrc);
|
||||||
|
var fs = makeShader(gl, gl.FRAGMENT_SHADER, fsSrc);
|
||||||
|
if (!vs || !fs) return null;
|
||||||
|
var p = gl.createProgram();
|
||||||
|
gl.attachShader(p, vs);
|
||||||
|
gl.attachShader(p, fs);
|
||||||
|
gl.linkProgram(p);
|
||||||
|
if (!gl.getProgramParameter(p, gl.LINK_STATUS)) {
|
||||||
|
console.error('clouds3d: Program link error:', gl.getProgramInfoLog(p));
|
||||||
|
gl.deleteProgram(p);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
var VERT_SRC = [
|
||||||
|
'attribute vec2 aPos;',
|
||||||
|
'void main() { gl_Position = vec4(aPos, 0.0, 1.0); }'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
// Volumetric raymarch through a horizontal cloud layer.
|
||||||
|
// Cheap 3D value noise + 4-octave fbm. 28 raymarch steps.
|
||||||
|
// Camera path is sinusoidal so the view occasionally dips
|
||||||
|
// into the cloud deck for the "fly through the edges" feel.
|
||||||
|
var FRAG_SRC = [
|
||||||
|
'precision highp float;',
|
||||||
|
'uniform vec2 uResolution;',
|
||||||
|
'uniform float uTime;',
|
||||||
|
'uniform vec3 uSky;',
|
||||||
|
'uniform vec3 uCloud;',
|
||||||
|
'uniform vec3 uCloudShadow;',
|
||||||
|
'uniform vec3 uLight;',
|
||||||
|
'uniform vec3 uBg;',
|
||||||
|
'uniform float uSpeed;',
|
||||||
|
'uniform float uDensity;',
|
||||||
|
'uniform float uDayProgress;',
|
||||||
|
// 3D hash and value noise
|
||||||
|
'float hash3(vec3 p) {',
|
||||||
|
'p = fract(p * vec3(0.1031, 0.1030, 0.0973));',
|
||||||
|
'p += dot(p, p.yxz + 33.33);',
|
||||||
|
'return fract((p.x + p.y) * p.z);',
|
||||||
|
'}',
|
||||||
|
'float noise3(vec3 p) {',
|
||||||
|
'vec3 i = floor(p);',
|
||||||
|
'vec3 f = fract(p);',
|
||||||
|
'f = f * f * (3.0 - 2.0 * f);',
|
||||||
|
'return mix(',
|
||||||
|
'mix(mix(hash3(i + vec3(0,0,0)), hash3(i + vec3(1,0,0)), f.x),',
|
||||||
|
'mix(hash3(i + vec3(0,1,0)), hash3(i + vec3(1,1,0)), f.x), f.y),',
|
||||||
|
'mix(mix(hash3(i + vec3(0,0,1)), hash3(i + vec3(1,0,1)), f.x),',
|
||||||
|
'mix(hash3(i + vec3(0,1,1)), hash3(i + vec3(1,1,1)), f.x), f.y),',
|
||||||
|
'f.z);',
|
||||||
|
'}',
|
||||||
|
'float fbm3(vec3 p) {',
|
||||||
|
'float v = 0.0;',
|
||||||
|
'float a = 0.5;',
|
||||||
|
'for (int i = 0; i < 4; i++) {',
|
||||||
|
'v += a * noise3(p);',
|
||||||
|
'p = p * 2.03 + vec3(13.7, 7.1, 19.3);',
|
||||||
|
'a *= 0.5;',
|
||||||
|
'}',
|
||||||
|
'return v;',
|
||||||
|
'}',
|
||||||
|
// Density at a point in the cloud layer.
|
||||||
|
'float cloudDensity(vec3 p, float baseThresh) {',
|
||||||
|
'float n = fbm3(p * 0.35 + vec3(0.0, 0.0, uTime * 0.06));',
|
||||||
|
// Cloud layer thickness: between y=-0.3 (deck bottom)
|
||||||
|
// and y=1.8 (top), densest in the middle.
|
||||||
|
'float h = clamp((p.y + 0.3) / 2.1, 0.0, 1.0);',
|
||||||
|
'float profile = 4.0 * h * (1.0 - h); // bell curve',
|
||||||
|
'profile = max(profile, 0.0);',
|
||||||
|
'return smoothstep(baseThresh, baseThresh + 0.18, n) * profile;',
|
||||||
|
'}',
|
||||||
|
'void main() {',
|
||||||
|
'vec2 uv = (gl_FragCoord.xy / uResolution.xy) * 2.0 - 1.0;',
|
||||||
|
'uv.x *= uResolution.x / uResolution.y;',
|
||||||
|
// Camera path: slow drift in X, sinusoidal altitude.
|
||||||
|
// The altitude dip drives the "fly through cloud edges" effect.
|
||||||
|
'float drift = uTime * uSpeed * 0.45;',
|
||||||
|
'vec3 ro = vec3(',
|
||||||
|
'drift,',
|
||||||
|
// Altitude oscillates: above (1.5), at cloud-top (0.6), inside (0.1)
|
||||||
|
'1.5 - 1.4 * (0.5 + 0.5 * sin(uTime * 0.07)),',
|
||||||
|
'drift * 0.7',
|
||||||
|
');',
|
||||||
|
// Ray direction: gentle downward tilt so we see the cloud deck
|
||||||
|
// and the sky horizon at once.
|
||||||
|
'vec3 rd = normalize(vec3(uv.x * 0.55, -0.18 + uv.y * 0.25, 1.0));',
|
||||||
|
// Sky gradient based on ray Y (background).
|
||||||
|
'float skyT = clamp(rd.y * 0.7 + 0.5, 0.0, 1.0);',
|
||||||
|
'vec3 col = mix(uBg, uSky, skyT);',
|
||||||
|
// Density threshold: weather 'sunny' -> high (sparse clouds),
|
||||||
|
// 'stormy' -> low (overcast deck).
|
||||||
|
'float baseThresh = mix(0.72, 0.40, uDensity);',
|
||||||
|
// March the cloud layer (bounded in Y).
|
||||||
|
'float t = 0.0;',
|
||||||
|
'float stepSize = 0.55;',
|
||||||
|
'float trans = 1.0;',
|
||||||
|
// Pre-compute ray entry/exit on cloud layer for early out
|
||||||
|
'for (int i = 0; i < 28; i++) {',
|
||||||
|
'vec3 p = ro + rd * t;',
|
||||||
|
'if (p.y > 2.0 || p.y < -0.4) break;',
|
||||||
|
'float d = cloudDensity(p, baseThresh);',
|
||||||
|
'if (d > 0.005) {',
|
||||||
|
// Cloud shading: bottom = shadow, top = lit
|
||||||
|
'float lit = clamp((p.y + 0.3) / 2.1, 0.0, 1.0);',
|
||||||
|
'vec3 cloudCol = mix(uCloudShadow, uCloud, lit);',
|
||||||
|
// Sun tint on the lit side, fades at night
|
||||||
|
'cloudCol = mix(cloudCol, uLight, uDayProgress * 0.35 * lit);',
|
||||||
|
'float alpha = clamp(d * stepSize * 0.9, 0.0, 1.0);',
|
||||||
|
'col = mix(col, cloudCol, alpha * trans);',
|
||||||
|
'trans *= (1.0 - alpha);',
|
||||||
|
'if (trans < 0.04) break;',
|
||||||
|
'}',
|
||||||
|
't += stepSize;',
|
||||||
|
'}',
|
||||||
|
// Night darkening
|
||||||
|
'col = mix(col, mix(uBg, col, 0.4), (1.0 - uDayProgress) * 0.55);',
|
||||||
|
'gl_FragColor = vec4(col, 1.0);',
|
||||||
|
'}'
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
function init(canvas) {
|
||||||
|
var gl = canvas.getContext('webgl', { antialias: false, alpha: false });
|
||||||
|
if (!gl) {
|
||||||
|
console.error('clouds3d: WebGL not available');
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
var prog = makeProgram(gl, VERT_SRC, FRAG_SRC);
|
||||||
|
if (!prog) return null;
|
||||||
|
|
||||||
|
var buf = gl.createBuffer();
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, buf);
|
||||||
|
gl.bufferData(gl.ARRAY_BUFFER,
|
||||||
|
new Float32Array([ -1,-1, 3,-1, -1,3 ]),
|
||||||
|
gl.STATIC_DRAW);
|
||||||
|
|
||||||
|
var aPos = gl.getAttribLocation(prog, 'aPos');
|
||||||
|
var uRes = gl.getUniformLocation(prog, 'uResolution');
|
||||||
|
var uTime = gl.getUniformLocation(prog, 'uTime');
|
||||||
|
var uSky = gl.getUniformLocation(prog, 'uSky');
|
||||||
|
var uCloud = gl.getUniformLocation(prog, 'uCloud');
|
||||||
|
var uCloudShadow = gl.getUniformLocation(prog, 'uCloudShadow');
|
||||||
|
var uLight = gl.getUniformLocation(prog, 'uLight');
|
||||||
|
var uBg = gl.getUniformLocation(prog, 'uBg');
|
||||||
|
var uSpeed = gl.getUniformLocation(prog, 'uSpeed');
|
||||||
|
var uDensity = gl.getUniformLocation(prog, 'uDensity');
|
||||||
|
var uDayProgress = gl.getUniformLocation(prog, 'uDayProgress');
|
||||||
|
|
||||||
|
return {
|
||||||
|
gl: gl, prog: prog, buf: buf, aPos: aPos,
|
||||||
|
uRes: uRes, uTime: uTime, uSky: uSky, uCloud: uCloud,
|
||||||
|
uCloudShadow: uCloudShadow, uLight: uLight, uBg: uBg,
|
||||||
|
uSpeed: uSpeed, uDensity: uDensity, uDayProgress: uDayProgress
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function hex(rgb) {
|
||||||
|
return [((rgb >> 16) & 255) / 255,
|
||||||
|
((rgb >> 8) & 255) / 255,
|
||||||
|
(rgb & 255) / 255];
|
||||||
|
}
|
||||||
|
|
||||||
|
// Same density mapping as clouds2d so swapping renderers doesn't
|
||||||
|
// change the visual weather.
|
||||||
|
var WEATHER_DENSITY = {
|
||||||
|
sunny: 0.20,
|
||||||
|
cloudy: 0.70,
|
||||||
|
stormy: 1.00,
|
||||||
|
night: 0.45
|
||||||
|
};
|
||||||
|
|
||||||
|
window.clouds3dRender = function(target) {
|
||||||
|
if (!target || typeof target !== 'object') {
|
||||||
|
console.error('clouds3d: invalid target');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var canvas = target.__clouds3dCanvas;
|
||||||
|
if (!canvas) {
|
||||||
|
canvas = document.createElement('canvas');
|
||||||
|
canvas.style.position = 'absolute';
|
||||||
|
canvas.style.top = '0';
|
||||||
|
canvas.style.left = '0';
|
||||||
|
canvas.style.width = '100%';
|
||||||
|
canvas.style.height = '100%';
|
||||||
|
while (target.firstChild) target.removeChild(target.firstChild);
|
||||||
|
target.appendChild(canvas);
|
||||||
|
target.style.position = 'relative';
|
||||||
|
target.__clouds3dCanvas = canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!canvas.__state) {
|
||||||
|
canvas.__state = init(canvas);
|
||||||
|
if (!canvas.__state) return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var s = canvas.__state;
|
||||||
|
var gl = s.gl;
|
||||||
|
canvas.width = target.clientWidth || window.innerWidth;
|
||||||
|
canvas.height = target.clientHeight || window.innerHeight;
|
||||||
|
gl.viewport(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
gl.useProgram(s.prog);
|
||||||
|
gl.bindBuffer(gl.ARRAY_BUFFER, s.buf);
|
||||||
|
gl.enableVertexAttribArray(s.aPos);
|
||||||
|
gl.vertexAttribPointer(s.aPos, 2, gl.FLOAT, false, 0, 0);
|
||||||
|
|
||||||
|
gl.uniform2f(s.uRes, canvas.width, canvas.height);
|
||||||
|
gl.uniform1f(s.uTime, performance.now() / 1000);
|
||||||
|
|
||||||
|
var pal = window.__currentPalette;
|
||||||
|
if (!pal) {
|
||||||
|
gl.clearColor(0, 0, 0, 1);
|
||||||
|
gl.clear(gl.COLOR_BUFFER_BIT);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var weather = window.__currentWeatherName;
|
||||||
|
var dayProgress = window.__currentDayProgress;
|
||||||
|
var themeSpeed = window.__currentThemeSpeed;
|
||||||
|
|
||||||
|
gl.uniform3fv(s.uSky, hex(pal.sky));
|
||||||
|
gl.uniform3fv(s.uCloud, hex(pal.cloud));
|
||||||
|
gl.uniform3fv(s.uCloudShadow, hex(
|
||||||
|
pal.cloudShadow !== undefined ? pal.cloudShadow : pal.sky));
|
||||||
|
gl.uniform3fv(s.uLight, hex(pal.light));
|
||||||
|
gl.uniform3fv(s.uBg, hex(pal.bg));
|
||||||
|
|
||||||
|
var density = (weather && WEATHER_DENSITY[weather] !== undefined)
|
||||||
|
? WEATHER_DENSITY[weather] : 0.5;
|
||||||
|
gl.uniform1f(s.uDensity, density);
|
||||||
|
|
||||||
|
var day = (typeof dayProgress === 'number')
|
||||||
|
? Math.max(0, Math.min(1, dayProgress)) : 0.5;
|
||||||
|
gl.uniform1f(s.uDayProgress, day);
|
||||||
|
|
||||||
|
var speed = (typeof themeSpeed === 'number')
|
||||||
|
? Math.max(0.1, Math.min(3, themeSpeed)) : 1.0;
|
||||||
|
gl.uniform1f(s.uSpeed, speed);
|
||||||
|
|
||||||
|
gl.drawArrays(gl.TRIANGLES, 0, 3);
|
||||||
|
};
|
||||||
|
|
||||||
|
})();
|
||||||
Reference in new issue
Block a user