├── .gitignore ├── LICENSE ├── README.md ├── images ├── LitSphere_test_02.jpg ├── ghost_strip.gif ├── matball01.jpg ├── poi_icons_32.png ├── skylight.jpg ├── sunset.jpg └── wheel.png ├── index.html ├── lib ├── dat.gui.min.js └── leaflet-hash.js ├── main.js ├── scene.yaml ├── shaders ├── color_bleed.glsl ├── dots.glsl ├── elevator.glsl ├── glsl-noise-periodic-3d.glsl ├── noise.glsl ├── spotlight.glsl └── wood.glsl └── styles ├── breathe.yaml ├── dots.yaml ├── halftone.yaml ├── popup.yaml └── windows.yaml /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/tangram/* 2 | !node_modules/tangram/dist/ 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Mapzen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # shaders-demo 2 | 3 | A demo showing some ways to use GLSL shaders to draw maps with the [Tangram](http://github.com/tangrams/tangram) library. 4 | 5 | Live demo: http://tangrams.github.io/shaders-demo 6 | 7 | ![shaders-demo screenshot](https://cloud.githubusercontent.com/assets/459970/6629447/7fa6bf9a-c8e4-11e4-86ba-90d108237e48.png) 8 | 9 | ### To run locally: 10 | 11 | Download this repo, then start a web server in its directory: 12 | 13 | python -m SimpleHTTPServer 8000 14 | 15 | If that doesn't work, try: 16 | 17 | python -m http.server 8000 18 | 19 | Then navigate to: [http://localhost:8000](http://localhost:8000) 20 | -------------------------------------------------------------------------------- /images/LitSphere_test_02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/LitSphere_test_02.jpg -------------------------------------------------------------------------------- /images/ghost_strip.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/ghost_strip.gif -------------------------------------------------------------------------------- /images/matball01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/matball01.jpg -------------------------------------------------------------------------------- /images/poi_icons_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/poi_icons_32.png -------------------------------------------------------------------------------- /images/skylight.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/skylight.jpg -------------------------------------------------------------------------------- /images/sunset.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/sunset.jpg -------------------------------------------------------------------------------- /images/wheel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tangrams/shaders-demo/6857e4671bbad391d2c059c1c26436ae01e7d780/images/wheel.png -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 11 | 12 | 13 | Shaders demo - Tangram 14 | 15 | 16 | 17 | 31 | 32 | 33 | 34 |
35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /lib/dat.gui.min.js: -------------------------------------------------------------------------------- 1 | var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){a=a||document;var b=a.createElement("link");b.type="text/css";b.rel="stylesheet";b.href=e;a.getElementsByTagName("head")[0].appendChild(b)},inject:function(e,a){a=a||document;var b=document.createElement("style");b.type="text/css";b.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(b)}}}(); 2 | dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(b){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(b[f]=a[f])},this);return b},defaults:function(b){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(b[f])&&(b[f]=a[f])},this);return b},compose:function(){var b=a.call(arguments);return function(){for(var d=a.call(arguments),f=b.length-1;0<=f;f--)d=[b[f].apply(this,d)];return d[0]}}, 3 | each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var c=0,p=a.length;cthis.__max&&(a=this.__max);void 0!==this.__step&&0!=a%this.__step&&(a=Math.round(a/this.__step)*this.__step);return b.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return b}(dat.controllers.Controller,dat.utils.common); 17 | dat.controllers.NumberControllerBox=function(e,a,b){var d=function(f,c,e){function k(){var a=parseFloat(n.__input.value);b.isNaN(a)||n.setValue(a)}function l(a){var c=r-a.clientY;n.setValue(n.getValue()+c*n.__impliedStep);r=a.clientY}function q(){a.unbind(window,"mousemove",l);a.unbind(window,"mouseup",q)}this.__truncationSuspended=!1;d.superclass.call(this,f,c,e);var n=this,r;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",k);a.bind(this.__input, 18 | "blur",function(){k();n.__onFinishChange&&n.__onFinishChange.call(n,n.getValue())});a.bind(this.__input,"mousedown",function(c){a.bind(window,"mousemove",l);a.bind(window,"mouseup",q);r=c.clientY});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&(n.__truncationSuspended=!0,this.blur(),n.__truncationSuspended=!1)});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;b.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input,c;if(this.__truncationSuspended)c= 19 | this.getValue();else{c=this.getValue();var b=Math.pow(10,this.__precision);c=Math.round(c*b)/b}a.value=c;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); 20 | dat.controllers.NumberControllerSlider=function(e,a,b,d,f){function c(a,c,d,b,f){return b+(a-c)/(d-c)*(f-b)}var p=function(d,b,f,e,r){function y(d){d.preventDefault();var b=a.getOffset(h.__background),f=a.getWidth(h.__background);h.setValue(c(d.clientX,b.left,b.left+f,h.__min,h.__max));return!1}function g(){a.unbind(window,"mousemove",y);a.unbind(window,"mouseup",g);h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())}p.superclass.call(this,d,b,{min:f,max:e,step:r});var h=this;this.__background= 21 | document.createElement("div");this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(c){a.bind(window,"mousemove",y);a.bind(window,"mouseup",g);y(c)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};p.superclass=e;p.useDefaultStyles=function(){b.inject(f)};d.extend(p.prototype,e.prototype,{updateDisplay:function(){var a= 22 | (this.getValue()-this.__min)/(this.__max-this.__min);this.__foreground.style.width=100*a+"%";return p.superclass.prototype.updateDisplay.call(this)}});return p}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,"/**\n * dat-gui JavaScript Controller Library\n * http://code.google.com/p/dat-gui\n *\n * Copyright 2011 Data Arts Team, Google Creative Lab\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n */\n\n.slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); 23 | dat.controllers.FunctionController=function(e,a,b){var d=function(b,c,e){d.superclass.call(this,b,c);var k=this;this.__button=document.createElement("div");this.__button.innerHTML=void 0===e?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();k.fire();return!1});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;b.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, 24 | this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 25 | dat.controllers.BooleanController=function(e,a,b){var d=function(b,c){d.superclass.call(this,b,c);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},!1);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;b.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& 26 | this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){!0===this.getValue()?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=!0):this.__checkbox.checked=!1;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 27 | dat.color.toString=function(e){return function(a){if(1==a.a||e.isUndefined(a.a)){for(a=a.hex.toString(16);6>a.length;)a="0"+a;return"#"+a}return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); 28 | dat.color.interpret=function(e,a){var b,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return null===a?!1:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); 29 | return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return null===a?!1:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return 3!= 30 | a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return 4!=a.length?!1:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(c){return a.isNumber(c.r)&&a.isNumber(c.g)&&a.isNumber(c.b)&&a.isNumber(c.a)?{space:"RGB",r:c.r,g:c.g,b:c.b,a:c.a}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(c){return a.isNumber(c.r)&& 31 | a.isNumber(c.g)&&a.isNumber(c.b)?{space:"RGB",r:c.r,g:c.g,b:c.b}:!1},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(c){return a.isNumber(c.h)&&a.isNumber(c.s)&&a.isNumber(c.v)&&a.isNumber(c.a)?{space:"HSV",h:c.h,s:c.s,v:c.v,a:c.a}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(d){return a.isNumber(d.h)&&a.isNumber(d.s)&&a.isNumber(d.v)?{space:"HSV",h:d.h,s:d.s,v:d.v}:!1},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d=!1; 32 | var c=1\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n', 59 | ".dg {\n /** Clear list styles */\n /* Auto-place container */\n /* Auto-placed GUI's */\n /* Line items that don't contain folders. */\n /** Folder names */\n /** Hides closed items */\n /** Controller row */\n /** Name-half (left) */\n /** Controller-half (right) */\n /** Controller placement */\n /** Shorter number boxes when slider is present. */\n /** Ensure the entire boolean and function row shows a hand */ }\n .dg ul {\n list-style: none;\n margin: 0;\n padding: 0;\n width: 100%;\n clear: both; }\n .dg.ac {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n height: 0;\n z-index: 0; }\n .dg:not(.ac) .main {\n /** Exclude mains in ac so that we don't hide close button */\n overflow: hidden; }\n .dg.main {\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear; }\n .dg.main.taller-than-window {\n overflow-y: auto; }\n .dg.main.taller-than-window .close-button {\n opacity: 1;\n /* TODO, these are style notes */\n margin-top: -1px;\n border-top: 1px solid #2c2c2c; }\n .dg.main ul.closed .close-button {\n opacity: 1 !important; }\n .dg.main:hover .close-button,\n .dg.main .close-button.drag {\n opacity: 1; }\n .dg.main .close-button {\n /*opacity: 0;*/\n -webkit-transition: opacity 0.1s linear;\n -o-transition: opacity 0.1s linear;\n -moz-transition: opacity 0.1s linear;\n transition: opacity 0.1s linear;\n border: 0;\n position: absolute;\n line-height: 19px;\n height: 20px;\n /* TODO, these are style notes */\n cursor: pointer;\n text-align: center;\n background-color: #000; }\n .dg.main .close-button:hover {\n background-color: #111; }\n .dg.a {\n float: right;\n margin-right: 15px;\n overflow-x: hidden; }\n .dg.a.has-save > ul {\n margin-top: 27px; }\n .dg.a.has-save > ul.closed {\n margin-top: 0; }\n .dg.a .save-row {\n position: fixed;\n top: 0;\n z-index: 1002; }\n .dg li {\n -webkit-transition: height 0.1s ease-out;\n -o-transition: height 0.1s ease-out;\n -moz-transition: height 0.1s ease-out;\n transition: height 0.1s ease-out; }\n .dg li:not(.folder) {\n cursor: auto;\n height: 27px;\n line-height: 27px;\n overflow: hidden;\n padding: 0 4px 0 5px; }\n .dg li.folder {\n padding: 0;\n border-left: 4px solid rgba(0, 0, 0, 0); }\n .dg li.title {\n cursor: pointer;\n margin-left: -4px; }\n .dg .closed li:not(.title),\n .dg .closed ul li,\n .dg .closed ul li > * {\n height: 0;\n overflow: hidden;\n border: 0; }\n .dg .cr {\n clear: both;\n padding-left: 3px;\n height: 27px; }\n .dg .property-name {\n cursor: default;\n float: left;\n clear: left;\n width: 40%;\n overflow: hidden;\n text-overflow: ellipsis; }\n .dg .c {\n float: left;\n width: 60%; }\n .dg .c input[type=text] {\n border: 0;\n margin-top: 4px;\n padding: 3px;\n width: 100%;\n float: right; }\n .dg .has-slider input[type=text] {\n width: 30%;\n /*display: none;*/\n margin-left: 0; }\n .dg .slider {\n float: left;\n width: 66%;\n margin-left: -5px;\n margin-right: 0;\n height: 19px;\n margin-top: 4px; }\n .dg .slider-fg {\n height: 100%; }\n .dg .c input[type=checkbox] {\n margin-top: 9px; }\n .dg .c select {\n margin-top: 5px; }\n .dg .cr.function,\n .dg .cr.function .property-name,\n .dg .cr.function *,\n .dg .cr.boolean,\n .dg .cr.boolean * {\n cursor: pointer; }\n .dg .selector {\n display: none;\n position: absolute;\n margin-left: -9px;\n margin-top: 23px;\n z-index: 10; }\n .dg .c:hover .selector,\n .dg .selector.drag {\n display: block; }\n .dg li.save-row {\n padding: 0; }\n .dg li.save-row .button {\n display: inline-block;\n padding: 0px 6px; }\n .dg.dialogue {\n background-color: #222;\n width: 460px;\n padding: 15px;\n font-size: 13px;\n line-height: 15px; }\n\n/* TODO Separate style and structure */\n#dg-new-constructor {\n padding: 10px;\n color: #222;\n font-family: Monaco, monospace;\n font-size: 10px;\n border: 0;\n resize: none;\n box-shadow: inset 1px 1px 1px #888;\n word-wrap: break-word;\n margin: 12px 0;\n display: block;\n width: 440px;\n overflow-y: scroll;\n height: 100px;\n position: relative; }\n\n#dg-local-explain {\n display: none;\n font-size: 11px;\n line-height: 17px;\n border-radius: 3px;\n background-color: #333;\n padding: 8px;\n margin-top: 10px; }\n #dg-local-explain code {\n font-size: 10px; }\n\n#dat-gui-save-locally {\n display: none; }\n\n/** Main type */\n.dg {\n color: #eee;\n font: 11px 'Lucida Grande', sans-serif;\n text-shadow: 0 -1px 0 #111;\n /** Auto place */\n /* Controller row,
  • */\n /** Controllers */ }\n .dg.main {\n /** Scrollbar */ }\n .dg.main::-webkit-scrollbar {\n width: 5px;\n background: #1a1a1a; }\n .dg.main::-webkit-scrollbar-corner {\n height: 0;\n display: none; }\n .dg.main::-webkit-scrollbar-thumb {\n border-radius: 5px;\n background: #676767; }\n .dg li:not(.folder) {\n background: #1a1a1a;\n border-bottom: 1px solid #2c2c2c; }\n .dg li.save-row {\n line-height: 25px;\n background: #dad5cb;\n border: 0; }\n .dg li.save-row select {\n margin-left: 5px;\n width: 108px; }\n .dg li.save-row .button {\n margin-left: 5px;\n margin-top: 1px;\n border-radius: 2px;\n font-size: 9px;\n line-height: 7px;\n padding: 4px 4px 5px 4px;\n background: #c5bdad;\n color: #fff;\n text-shadow: 0 1px 0 #b0a58f;\n box-shadow: 0 -1px 0 #b0a58f;\n cursor: pointer; }\n .dg li.save-row .button.gears {\n background: #c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;\n height: 7px;\n width: 8px; }\n .dg li.save-row .button:hover {\n background-color: #bab19e;\n box-shadow: 0 -1px 0 #b0a58f; }\n .dg li.folder {\n border-bottom: 0; }\n .dg li.title {\n padding-left: 16px;\n background: black url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;\n cursor: pointer;\n border-bottom: 1px solid rgba(255, 255, 255, 0.2); }\n .dg .closed li.title {\n background-image: url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==); }\n .dg .cr.boolean {\n border-left: 3px solid #806787; }\n .dg .cr.function {\n border-left: 3px solid #e61d5f; }\n .dg .cr.number {\n border-left: 3px solid #2fa1d6; }\n .dg .cr.number input[type=text] {\n color: #2fa1d6; }\n .dg .cr.string {\n border-left: 3px solid #1ed36f; }\n .dg .cr.string input[type=text] {\n color: #1ed36f; }\n .dg .cr.function:hover, .dg .cr.boolean:hover {\n background: #111; }\n .dg .c input[type=text] {\n background: #303030;\n outline: none; }\n .dg .c input[type=text]:hover {\n background: #3c3c3c; }\n .dg .c input[type=text]:focus {\n background: #494949;\n color: #fff; }\n .dg .c .slider {\n background: #303030;\n cursor: ew-resize; }\n .dg .c .slider-fg {\n background: #2fa1d6; }\n .dg .c .slider:hover {\n background: #3c3c3c; }\n .dg .c .slider:hover .slider-fg {\n background: #44abda; }\n", 60 | dat.controllers.factory=function(e,a,b,d,f,c,p){return function(k,l,q,n){var r=k[l];if(p.isArray(q)||p.isObject(q))return new e(k,l,q);if(p.isNumber(r))return p.isNumber(q)&&p.isNumber(n)?new b(k,l,q,n):new a(k,l,{min:q,max:n});if(p.isString(r))return new d(k,l);if(p.isFunction(r))return new f(k,l,"");if(p.isBoolean(r))return new c(k,l)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,b){var d= 61 | function(b,c){function e(){k.setValue(k.__input.value)}d.superclass.call(this,b,c);var k=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){k.__onFinishChange&&k.__onFinishChange.call(k,k.getValue())});a.bind(this.__input,"keydown",function(a){13===a.keyCode&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;b.extend(d.prototype, 62 | e.prototype,{updateDisplay:function(){a.isActive(this.__input)||(this.__input.value=this.getValue());return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, 63 | dat.controllers.ColorController=function(e,a,b,d,f){function c(a,b,d,c){a.style.background="";f.each(l,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+d+" 0%, "+c+" 100%); "})}function p(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; 64 | a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var k=function(e,n){function r(b){t(b);a.bind(window,"mousemove",t);a.bind(window, 65 | "mouseup",l)}function l(){a.unbind(window,"mousemove",t);a.unbind(window,"mouseup",l)}function g(){var a=d(this.value);!1!==a?(s.__color.__state=a,s.setValue(s.__color.toOriginal())):this.value=s.__color.toString()}function h(){a.unbind(window,"mousemove",u);a.unbind(window,"mouseup",h)}function t(b){b.preventDefault();var d=a.getWidth(s.__saturation_field),c=a.getOffset(s.__saturation_field),e=(b.clientX-c.left+document.body.scrollLeft)/d;b=1-(b.clientY-c.top+document.body.scrollTop)/d;1 66 | b&&(b=0);1e&&(e=0);s.__color.v=b;s.__color.s=e;s.setValue(s.__color.toOriginal());return!1}function u(b){b.preventDefault();var d=a.getHeight(s.__hue_field),c=a.getOffset(s.__hue_field);b=1-(b.clientY-c.top+document.body.scrollTop)/d;1b&&(b=0);s.__color.h=360*b;s.setValue(s.__color.toOriginal());return!1}k.superclass.call(this,e,n);this.__color=new b(this.getValue());this.__temp=new b(0);var s=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement,!1); 67 | this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input=document.createElement("input"); 68 | this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){13===a.keyCode&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(b){a.addClass(this,"drag").bind(window,"mouseup",function(b){a.removeClass(s.__selector,"drag")})});var v=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"});f.extend(this.__field_knob.style, 69 | {position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(0.5>this.__color.v?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(v.style,{width:"100%",height:"100%", 70 | background:"none"});c(v,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});p(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",r);a.bind(this.__field_knob,"mousedown",r);a.bind(this.__hue_field,"mousedown",function(b){u(b);a.bind(window, 71 | "mousemove",u);a.bind(window,"mouseup",h)});this.__saturation_field.appendChild(v);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};k.superclass=e;f.extend(k.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue());if(!1!==a){var e=!1; 72 | f.each(b.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=!0,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var k=0.5>this.__color.v||0.5a&&(a+=1);return{h:360*a,s:e/c,v:c/255}},rgb_to_hex:function(a,b,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,b);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,b){return a>>8*b&255},hex_with_component:function(a,b,d){return d<<(e=8*b)|a&~(255<Tangram | © OSM contributors | Mapzen' 30 | }); 31 | 32 | window.layer = layer; 33 | var scene = layer.scene; 34 | window.scene = scene; 35 | // setView expects format ([lat, long], zoom) 36 | map.setView(map_start_location.slice(0, 3), map_start_location[2]); 37 | 38 | var hash = new L.Hash(map); 39 | 40 | 41 | // GUI options for rendering modes/effects 42 | var style_controls = { 43 | 'Default' : function() { 44 | this.setstyle(Object.keys(scene.config.layers), undefined); 45 | }, 46 | 'Elevator' : function() { 47 | this.setstyle(['buildings'], 'elevator'); 48 | }, 49 | 'Popup' : function() { 50 | this.setstyle(['buildings'], 'popup'); 51 | }, 52 | 'radius': 300, 53 | 'height': 3, 54 | 'Rainbow' : function() { 55 | this.setstyle(['buildings','landuse'], 'rainbow'); 56 | }, 57 | 'Dots' : function() { 58 | this.setstyle(['buildings','landuse'], 'dots'); 59 | }, 60 | 'Halftone' : function() { 61 | this.setstyle(Object.keys(scene.config.layers), 'halftone'); 62 | }, 63 | 'dot_frequency' : 100, 64 | 'dot_scale' : 1.5, 65 | 'Colorhalftone' : function() { 66 | this.setstyle(Object.keys(scene.config.layers), 'colorhalftone'); 67 | }, 68 | 'color_dot_frequency' : 50, 69 | 'color_dot_scale' : 1.5, 70 | 'Windows' : function() { 71 | this.setstyle(['buildings'], 'windows', 'isometric'); 72 | }, 73 | setstyle: function (layers, newstyle, camera) { 74 | // Restore initial state 75 | for (var i in scene.config.layers) { 76 | try { 77 | scene.config.layers[i].draw.polygons.style = undefined; 78 | } 79 | catch(e) { 80 | scene.config.layers[i].draw.lines.style = undefined; 81 | } 82 | }; 83 | // Apply new style 84 | for (var j=0; j < layers.length; j++) { 85 | try { 86 | scene.config.layers[layers[j]].draw.polygons.style = newstyle; 87 | } 88 | catch(e) { 89 | scene.config.layers[layers[j]].draw.lines.style = newstyle; 90 | } 91 | } 92 | // Set camera 93 | if (camera) { 94 | scene.setActiveCamera(camera); 95 | } else { 96 | scene.setActiveCamera("perspective"); 97 | } 98 | // Recompile/rebuild 99 | scene.rebuild(); 100 | } 101 | }; 102 | 103 | // Create dat GUI 104 | var gui = new dat.GUI({ autoPlace: true }); 105 | function addGUI () { 106 | gui.domElement.parentNode.style.zIndex = 500; 107 | window.gui = gui; 108 | var folder = gui.addFolder("Click a style:"); 109 | folder.open(); // this just points the arrow downward 110 | // Styles 111 | gui.add(style_controls, 'Default'); 112 | gui.add(style_controls, 'Elevator'); 113 | gui.add(style_controls, 'Popup'); 114 | gui.add(style_controls, 'radius', 0, 500).name("  radius").onChange(function(value) { 115 | scene.styles.popup.shaders.uniforms.u_popup_radius = value; 116 | scene.requestRedraw(); 117 | }); 118 | gui.add(style_controls, 'height', 0, 5).name("  amount").onChange(function(value) { 119 | scene.styles.popup.shaders.uniforms.u_popup_height = value; 120 | scene.requestRedraw(); 121 | }); 122 | gui.add(style_controls, 'Rainbow'); 123 | 124 | gui.add(style_controls, 'Halftone'); 125 | gui.add(style_controls, 'dot_frequency', 1, 200).name("  frequency").onChange(function(value) { 126 | scene.styles.halftone.shaders.uniforms.dot_frequency = value; 127 | scene.requestRedraw(); 128 | }); 129 | gui.add(style_controls, 'dot_scale', 0, 10).name("  scale").onChange(function(value) { 130 | scene.styles.halftone.shaders.uniforms.dot_scale = value; 131 | scene.requestRedraw(); 132 | }); 133 | 134 | gui.add(style_controls, 'Colorhalftone'); 135 | gui.add(style_controls, 'color_dot_frequency', 0, 100).name("  frequency").onChange(function(value) { 136 | scene.styles.colorhalftone.shaders.uniforms.dot_frequency = value; 137 | scene.requestRedraw(); 138 | }); 139 | gui.add(style_controls, 'color_dot_scale', 0, 3).name("  scale").onChange(function(value) { 140 | scene.styles.colorhalftone.shaders.uniforms.dot_scale = value; 141 | scene.requestRedraw(); 142 | }); 143 | 144 | gui.add(style_controls, 'Windows'); 145 | } 146 | 147 | 148 | 149 | /***** Render loop *****/ 150 | window.addEventListener('load', function () { 151 | // Scene initialized 152 | layer.on('init', function() { 153 | addGUI(); 154 | }); 155 | layer.addTo(map); 156 | 157 | }); 158 | 159 | return map; 160 | 161 | 162 | }()); 163 | -------------------------------------------------------------------------------- /scene.yaml: -------------------------------------------------------------------------------- 1 | import: 2 | - styles/popup.yaml 3 | - styles/breathe.yaml 4 | - styles/dots.yaml 5 | - styles/halftone.yaml 6 | - styles/halftone.yaml 7 | - styles/windows.yaml 8 | 9 | sources: 10 | mapzen: 11 | type: MVT 12 | url: https://tile.nextzen.org/tilezen/vector/v1/512/all/{z}/{x}/{y}.mvt 13 | url_params: 14 | api_key: NaqqS33fTUmyQcvbuIUCKA 15 | tile_size: 512 16 | max_zoom: 16 17 | 18 | cameras: 19 | perspective: 20 | type: perspective 21 | # focal_length: 1 22 | focal_length: [[16, 2], [17, 2.5], [18, 3], [19, 4], [20, 6]] # pairs of [zoom, focal len] 23 | vanishing_point: [-250, -250] # relative to center of screen, in pixels 24 | isometric: 25 | type: isometric 26 | axis: [0,1] 27 | 28 | lights: 29 | key: 30 | type: directional 31 | direction: [-.2, 1, -1] 32 | diffuse: 1 33 | ambient: .6 34 | 35 | styles: 36 | rainbow: 37 | base: polygons 38 | animated: true 39 | shaders: 40 | blocks: 41 | global: | 42 | // hue-saturation-value to RGB color space converter, to allow hue cycling 43 | vec3 hsv2rgb(vec3 c) { 44 | vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0); 45 | vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www); 46 | return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y); 47 | } 48 | color: | 49 | // set an hsv value with hue varied by z-position (aka height) and by time 50 | // then convert to rgb and set pixel color 51 | vec3 c = vec3(worldPosition().z * .003 + u_time / 10., 1.0, 1.0); 52 | color.rgb = hsv2rgb(c); 53 | 54 | elevator: 55 | base: polygons 56 | animated: true 57 | shaders: 58 | blocks: 59 | position: | 60 | // Elevator buildings 61 | if (position.z > 0.01) { 62 | position.z *= (sin(position.z + u_time) + 1.0); 63 | } 64 | 65 | layers: 66 | earth: 67 | data: 68 | source: mapzen 69 | layer: earth 70 | draw: 71 | polygons: 72 | order: function() { return feature.sort_rank; } 73 | color: [0.175, 0.175, 0.175] 74 | 75 | landuse: 76 | data: 77 | source: mapzen 78 | layer: landuse 79 | draw: 80 | polygons: 81 | order: function() { return feature.sort_rank; } 82 | color: [0.5, 0.875, 0.5] 83 | pitch: 84 | filter: 85 | kind: ['pitch', 'garden', 'playground'] 86 | draw: 87 | polygons: 88 | color: [0.3, 0.675, 0.3] 89 | 90 | water: 91 | data: 92 | source: mapzen 93 | layer: water 94 | draw: 95 | polygons: 96 | order: function() { return feature.sort_rank; } 97 | color: [0.5, 0.5, 0.875] 98 | outline: 99 | # don't draw outlines on ocean or riverbank features 100 | filter: {not: {kind: [ocean, river, riverbank]}} 101 | draw: 102 | lines: 103 | color: [0.6, 0.6, 0.975] 104 | order: function() { return feature.sort_rank; } 105 | width: function () { 106 | return ( 107 | $zoom >= 16 && (2.5 * Math.log($zoom)) 108 | ); 109 | } 110 | 111 | roads: 112 | data: 113 | source: mapzen 114 | layer: roads 115 | draw: 116 | lines: 117 | order: function () { return feature.sort_rank; } 118 | outline: 119 | color: [0.7, 0.7, 0.7] 120 | width: 1 121 | highway: 122 | filter: { kind: highway } 123 | draw: 124 | lines: 125 | color: [1.0, 1.0, 1.0] 126 | width: 9 127 | tunnel: 128 | filter: { is_tunnel: yes } 129 | draw: 130 | lines: 131 | color: '#333' 132 | width: 6 133 | outline: 134 | order: 352 # magic number 135 | color: white 136 | width: 1 137 | major_road: 138 | filter: { kind: major_road } 139 | draw: 140 | lines: 141 | color: [0.5, 0.5, 0.5] 142 | width: 5 143 | minor_road: 144 | filter: { kind: minor_road } 145 | draw: 146 | lines: 147 | color: [0.65, 0.65, 0.65] 148 | width: 4 149 | path: 150 | filter: 151 | kind: path 152 | draw: 153 | lines: 154 | color: [0.8, 0.8, 0.8] 155 | width: 3 156 | rail: 157 | filter: 158 | kind: rail 159 | not: { is_tunnel: yes } 160 | draw: 161 | lines: 162 | color: [0.5, 0.0, 0.0] 163 | width: 3 164 | outline: 165 | order: 352 # magic number 166 | width: .5 167 | 168 | buildings: 169 | data: 170 | source: mapzen 171 | layer: buildings 172 | filter: { $geometry: polygon } # filter out address points in buildings layer 173 | draw: 174 | polygons: 175 | order: function() { return feature.sort_rank; } 176 | color: [.6, .6, .6] 177 | # at z15, only extrude buildings over 20 m; at z16 and higher, extrude all buildings 178 | extrude: function () { return (($zoom >= 15 && feature.height > 20) || $zoom >= 16) } 179 | # buildings with a name only 180 | namedBuildings: 181 | filter: { name: true } 182 | draw: 183 | polygons: 184 | color: [.6, .2, .2] 185 | # buildings with a name and a kind 186 | namedKindBuildings: 187 | filter: { kind: true } 188 | draw: 189 | polygons: 190 | color: [.6, .2, .6] 191 | # buildings with a kind only 192 | kindBuildings: 193 | filter: { name: false, kind: true } 194 | draw: 195 | polygons: 196 | color: [.2, .2, .6] 197 | -------------------------------------------------------------------------------- /shaders/color_bleed.glsl: -------------------------------------------------------------------------------- 1 | color += lighting * vec3(gl_FragCoord.x / u_resolution.x, 0.0, gl_FragCoord.y / u_resolution.y); 2 | #if defined(EFFECT_COLOR_BLEED_ANIMATED) 3 | color.r += lighting.r * sin(u_time / 3.0); 4 | #endif 5 | -------------------------------------------------------------------------------- /shaders/dots.glsl: -------------------------------------------------------------------------------- 1 | // 3d dots 2 | 3 | uniform float u_dot_grid_scale; // = .1; 4 | uniform float u_dot_scale; // = .07; 5 | uniform vec3 u_dot_background_color; // vec3(.5, .2, .2) 6 | uniform vec3 u_dot_color; // vec3(1.) 7 | 8 | vec3 dots (vec3 pos) { 9 | vec3 color = u_dot_background_color; 10 | 11 | const float dot_fuzz = .15; // antialias range as % of dot radius 12 | 13 | // Rotate to get better dot coverage across 3d geometry 14 | float dot_angle = radians(45.); 15 | // float dot_angle = u_time / 100000.; 16 | mat4 dot_rotate; 17 | 18 | dot_rotate = mat4( 19 | // rotate z 20 | vec4(cos(dot_angle), sin(dot_angle), 0., 0.), 21 | vec4(-sin(dot_angle), cos(dot_angle), 0., 0.), 22 | vec4(0., 0., 1., 0.), 23 | vec4(0., 0., 0., 1.) 24 | ); 25 | 26 | dot_rotate *= mat4( 27 | // rotate x 28 | vec4(1., 0., 0., 0.), 29 | vec4(0., cos(dot_angle), sin(dot_angle), 0.), 30 | vec4(0., -sin(dot_angle), cos(dot_angle), 0.), 31 | vec4(0., 0., 0., 1.) 32 | ); 33 | 34 | // dot_rotate *= mat4( 35 | // // rotate y 36 | // vec4(cos(dot_angle), 0., sin(dot_angle), 0.), 37 | // vec4(0., 1., 0., 0.), 38 | // vec4(-sin(dot_angle), 0., cos(dot_angle), 0.), 39 | // vec4(0., 0., 0., 1.) 40 | // ); 41 | 42 | pos = (dot_rotate * vec4(pos, 1.)).xyz; 43 | 44 | // offset experiments 45 | // pos += vec3(u_time * 10., u_time * 10., 0.); 46 | // pos += vec3(0., 0., u_time * 10.); 47 | // pos += vec3(u_time * 10., u_time * 10., u_time * 10.); 48 | 49 | vec3 scaledXYZ = pos * u_dot_grid_scale; 50 | vec3 cell = floor(scaledXYZ); 51 | vec3 offset = scaledXYZ - cell; 52 | vec3 currentOffset; 53 | 54 | float priority = -1.0; 55 | for (float i = -1.0; i <= 0.0; i++) { 56 | for (float j = -1.0; j <= 0.0; j++) { 57 | for (float k = -1.0; k <= 0.0; k++) { 58 | vec3 currentCell = cell + vec3(i, j, k); 59 | vec3 cellOffset = offset - vec3(i, j, k); 60 | vec2 randomXY = currentCell.xy + currentCell.z * 0.003; 61 | currentOffset = cellOffset - (vec3(0.5, 0.5, 0.5)); 62 | 63 | float radius = dot(currentOffset, currentOffset); 64 | if (radius < u_dot_scale) { 65 | color = u_dot_color; 66 | } 67 | else if (radius < u_dot_scale * (1. + dot_fuzz)) { 68 | color = mix(u_dot_background_color, u_dot_color, ((u_dot_scale * (1. + dot_fuzz)) - radius) / (u_dot_scale * dot_fuzz)); 69 | } 70 | } 71 | } 72 | } 73 | 74 | return color; 75 | } 76 | 77 | #pragma glslify: export(dots) 78 | -------------------------------------------------------------------------------- /shaders/elevator.glsl: -------------------------------------------------------------------------------- 1 | if (position.z > 0.) { 2 | position.z *= max((sin(position.z + u_time) + 1.0) / 2.0, 0.05); // elevator buildings 3 | } 4 | -------------------------------------------------------------------------------- /shaders/glsl-noise-periodic-3d.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // GLSL textureless classic 3D noise "cnoise", 3 | // with an RSL-style periodic variant "pnoise". 4 | // Author: Stefan Gustavson (stefan.gustavson@liu.se) 5 | // Version: 2011-10-11 6 | // 7 | // Many thanks to Ian McEwan of Ashima Arts for the 8 | // ideas for permutation and gradient selection. 9 | // 10 | // Copyright (c) 2011 Stefan Gustavson. All rights reserved. 11 | // Distributed under the MIT license. See LICENSE file. 12 | // https://github.com/ashima/webgl-noise 13 | // 14 | 15 | vec3 mod289(vec3 x) 16 | { 17 | return x - floor(x * (1.0 / 289.0)) * 289.0; 18 | } 19 | 20 | vec4 mod289(vec4 x) 21 | { 22 | return x - floor(x * (1.0 / 289.0)) * 289.0; 23 | } 24 | 25 | vec4 permute(vec4 x) 26 | { 27 | return mod289(((x*34.0)+1.0)*x); 28 | } 29 | 30 | vec4 taylorInvSqrt(vec4 r) 31 | { 32 | return 1.79284291400159 - 0.85373472095314 * r; 33 | } 34 | 35 | vec3 fade(vec3 t) { 36 | return t*t*t*(t*(t*6.0-15.0)+10.0); 37 | } 38 | 39 | // Classic Perlin noise, periodic variant 40 | float pnoise(vec3 P, vec3 rep) 41 | { 42 | vec3 Pi0 = mod(floor(P), rep); // Integer part, modulo period 43 | vec3 Pi1 = mod(Pi0 + vec3(1.0), rep); // Integer part + 1, mod period 44 | Pi0 = mod289(Pi0); 45 | Pi1 = mod289(Pi1); 46 | vec3 Pf0 = fract(P); // Fractional part for interpolation 47 | vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0 48 | vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x); 49 | vec4 iy = vec4(Pi0.yy, Pi1.yy); 50 | vec4 iz0 = Pi0.zzzz; 51 | vec4 iz1 = Pi1.zzzz; 52 | 53 | vec4 ixy = permute(permute(ix) + iy); 54 | vec4 ixy0 = permute(ixy + iz0); 55 | vec4 ixy1 = permute(ixy + iz1); 56 | 57 | vec4 gx0 = ixy0 * (1.0 / 7.0); 58 | vec4 gy0 = fract(floor(gx0) * (1.0 / 7.0)) - 0.5; 59 | gx0 = fract(gx0); 60 | vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0); 61 | vec4 sz0 = step(gz0, vec4(0.0)); 62 | gx0 -= sz0 * (step(0.0, gx0) - 0.5); 63 | gy0 -= sz0 * (step(0.0, gy0) - 0.5); 64 | 65 | vec4 gx1 = ixy1 * (1.0 / 7.0); 66 | vec4 gy1 = fract(floor(gx1) * (1.0 / 7.0)) - 0.5; 67 | gx1 = fract(gx1); 68 | vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1); 69 | vec4 sz1 = step(gz1, vec4(0.0)); 70 | gx1 -= sz1 * (step(0.0, gx1) - 0.5); 71 | gy1 -= sz1 * (step(0.0, gy1) - 0.5); 72 | 73 | vec3 g000 = vec3(gx0.x,gy0.x,gz0.x); 74 | vec3 g100 = vec3(gx0.y,gy0.y,gz0.y); 75 | vec3 g010 = vec3(gx0.z,gy0.z,gz0.z); 76 | vec3 g110 = vec3(gx0.w,gy0.w,gz0.w); 77 | vec3 g001 = vec3(gx1.x,gy1.x,gz1.x); 78 | vec3 g101 = vec3(gx1.y,gy1.y,gz1.y); 79 | vec3 g011 = vec3(gx1.z,gy1.z,gz1.z); 80 | vec3 g111 = vec3(gx1.w,gy1.w,gz1.w); 81 | 82 | vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110))); 83 | g000 *= norm0.x; 84 | g010 *= norm0.y; 85 | g100 *= norm0.z; 86 | g110 *= norm0.w; 87 | vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111))); 88 | g001 *= norm1.x; 89 | g011 *= norm1.y; 90 | g101 *= norm1.z; 91 | g111 *= norm1.w; 92 | 93 | float n000 = dot(g000, Pf0); 94 | float n100 = dot(g100, vec3(Pf1.x, Pf0.yz)); 95 | float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z)); 96 | float n110 = dot(g110, vec3(Pf1.xy, Pf0.z)); 97 | float n001 = dot(g001, vec3(Pf0.xy, Pf1.z)); 98 | float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z)); 99 | float n011 = dot(g011, vec3(Pf0.x, Pf1.yz)); 100 | float n111 = dot(g111, Pf1); 101 | 102 | vec3 fade_xyz = fade(Pf0); 103 | vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z); 104 | vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y); 105 | float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 106 | return 2.2 * n_xyz; 107 | } 108 | 109 | #pragma glslify: export(pnoise) 110 | -------------------------------------------------------------------------------- /shaders/noise.glsl: -------------------------------------------------------------------------------- 1 | // Default wrap space for noise if not specified 2 | #if defined(WORLD_POSITION_WRAP) 3 | #define NOISE_WRAP WORLD_POSITION_WRAP 4 | #else 5 | #define NOISE_WRAP 100000. 6 | #endif 7 | 8 | #if defined(EFFECT_NOISE_ANIMATED) 9 | color *= (abs(pnoise((v_world_position.xyz + vec3(u_time * 5., u_time * 7.5, u_time * 10.)) / 10.0, vec3(NOISE_WRAP / 10.0))) / 4.0) + 0.75; 10 | #else 11 | color *= (abs(pnoise(v_world_position.xyz / 10.0, vec3(NOISE_WRAP / 10.0))) / 4.0) + 0.75; 12 | #endif 13 | -------------------------------------------------------------------------------- /shaders/spotlight.glsl: -------------------------------------------------------------------------------- 1 | // Spotlight effect 2 | vec2 pixel_position = gl_FragCoord.xy / u_resolution.xy; // scale coords to [0.0, 1.0] 3 | pixel_position = pixel_position * 2.0 - 1.0; // scale coords to [-1.0, 1.0] 4 | pixel_position *= u_aspect; // correct aspect ratio 5 | 6 | color *= max(1.0 - distance(pixel_position, vec2(0.0, 0.0)), 0.2); 7 | -------------------------------------------------------------------------------- /shaders/wood.glsl: -------------------------------------------------------------------------------- 1 | uniform vec3 u_wood_color1; // = .1; 2 | uniform vec3 u_wood_color2; // = .1; 3 | uniform float u_wood_eccentricity; // = .07; 4 | uniform float u_wood_twist; // = .07; 5 | uniform float u_wood_scale; // = .07; 6 | uniform float u_wood_spacing; // = .07; 7 | 8 | vec3 wood( vec3 pos ) { 9 | 10 | float r = length(pos.xy); 11 | float phi = atan(pos.y, pos.x); 12 | 13 | phi += u_wood_twist * pos.z; 14 | // phi += u_test * pos.z; 15 | 16 | r *= 1.0 + u_wood_eccentricity * cos(phi); 17 | // r *= 1.0 + u_test3 * cos(phi); 18 | 19 | float k = u_wood_spacing / u_wood_scale; 20 | // float k = TAU / u_test2; 21 | 22 | float density = 1. + sin(k * r); 23 | 24 | return vec3(mix( 25 | u_wood_color1, 26 | u_wood_color2, 27 | density) 28 | ); 29 | } 30 | 31 | #pragma glslify: export(wood) -------------------------------------------------------------------------------- /styles/breathe.yaml: -------------------------------------------------------------------------------- 1 | styles: 2 | breathe: 3 | base: polygons 4 | animated: true 5 | shaders: 6 | uniforms: 7 | u_breathe_scale: 10 8 | u_breathe_speed: 1 9 | blocks: 10 | position: | 11 | position.xy += a_normal.xy * u_breathe_scale * smoothstep(0.25, 1., abs(sin(u_time * u_breathe_speed))); 12 | 13 | -------------------------------------------------------------------------------- /styles/dots.yaml: -------------------------------------------------------------------------------- 1 | styles: 2 | # 3d dots 3 | dots: 4 | base: polygons 5 | shaders: 6 | uniforms: 7 | u_dot_grid_scale: 0.02 8 | u_dot_scale: 0.07 9 | u_dot_background_color: [.5, .2, .2] 10 | u_dot_color: [1, 1, 1] 11 | blocks: 12 | global: | 13 | 14 | vec3 dots (vec3 pos) { 15 | vec3 color = u_dot_background_color; 16 | 17 | const float dot_fuzz = .15; // antialias range as % of dot radius 18 | 19 | // Rotate to get better dot coverage across 3d geometry 20 | float dot_angle = radians(45.); 21 | // float dot_angle = u_time / 100000.; 22 | mat4 dot_rotate; 23 | 24 | dot_rotate = mat4( 25 | // rotate z 26 | vec4(cos(dot_angle), sin(dot_angle), 0., 0.), 27 | vec4(-sin(dot_angle), cos(dot_angle), 0., 0.), 28 | vec4(0., 0., 1., 0.), 29 | vec4(0., 0., 0., 1.) 30 | ); 31 | 32 | dot_rotate *= mat4( 33 | // rotate x 34 | vec4(1., 0., 0., 0.), 35 | vec4(0., cos(dot_angle), sin(dot_angle), 0.), 36 | vec4(0., -sin(dot_angle), cos(dot_angle), 0.), 37 | vec4(0., 0., 0., 1.) 38 | ); 39 | 40 | // dot_rotate *= mat4( 41 | // // rotate y 42 | // vec4(cos(dot_angle), 0., sin(dot_angle), 0.), 43 | // vec4(0., 1., 0., 0.), 44 | // vec4(-sin(dot_angle), 0., cos(dot_angle), 0.), 45 | // vec4(0., 0., 0., 1.) 46 | // ); 47 | 48 | pos = (dot_rotate * vec4(pos, 1.)).xyz; 49 | 50 | // offset experiments 51 | // pos += vec3(u_time * 10., u_time * 10., 0.); 52 | // pos += vec3(0., 0., u_time * 10.); 53 | // pos += vec3(u_time * 10., u_time * 10., u_time * 10.); 54 | 55 | vec3 scaledXYZ = pos * u_dot_grid_scale; 56 | vec3 cell = floor(scaledXYZ); 57 | vec3 offset = scaledXYZ - cell; 58 | vec3 currentOffset; 59 | 60 | float priority = -1.0; 61 | for (float i = -1.0; i <= 0.0; i++) { 62 | for (float j = -1.0; j <= 0.0; j++) { 63 | for (float k = -1.0; k <= 0.0; k++) { 64 | vec3 currentCell = cell + vec3(i, j, k); 65 | vec3 cellOffset = offset - vec3(i, j, k); 66 | vec2 randomXY = currentCell.xy + currentCell.z * 0.003; 67 | currentOffset = cellOffset - (vec3(0.5, 0.5, 0.5)); 68 | 69 | float radius = dot(currentOffset, currentOffset); 70 | if (radius < u_dot_scale) { 71 | color = u_dot_color; 72 | } 73 | else if (radius < u_dot_scale * (1. + dot_fuzz)) { 74 | color = mix(u_dot_background_color, u_dot_color, ((u_dot_scale * (1. + dot_fuzz)) - radius) / (u_dot_scale * dot_fuzz)); 75 | } 76 | } 77 | } 78 | } 79 | 80 | return color; 81 | } 82 | 83 | color: color.rgb *= dots(worldPosition().xyz); 84 | # fragment: color *= dots(worldPosition().xyz + vec3(0., 0., u_time * 25.)); // animated dots 85 | # fragment: vec3 n = abs(v_normal); if (n.z > n.x && n.z > n.y) { color *= dots(worldPosition().xyz); } // apply only to up-facing surfaces 86 | -------------------------------------------------------------------------------- /styles/halftone.yaml: -------------------------------------------------------------------------------- 1 | styles: 2 | # Halftone shaders adapted from: http://webstaff.itn.liu.se/~stegu/webglshadertutorial/shadertutorial.html 3 | halftone: 4 | base: polygons 5 | shaders: 6 | uniforms: 7 | dot_frequency: 100.4748637184972 8 | dot_scale: 2 9 | blocks: 10 | global: | 11 | // Antialiasing 12 | float aastep(float threshold, const float freq, float value) { 13 | const float scale = 2.; 14 | const float y_rot = 0.; 15 | float afwidth = freq * (1.0/200.0) / scale / cos(y_rot); 16 | return smoothstep(threshold-afwidth, threshold+afwidth, value); 17 | } 18 | 19 | filter: | 20 | // Distance to nearest point in a grid of 21 | // (frequency x frequency) points over the unit square 22 | // Scale dot size for a subset of zoom 23 | const float max_scale_zoom = 19.; 24 | const float min_scale_zoom = 17.; 25 | const float scale_zoom_factor = .25; 26 | float zoom_frequency = dot_frequency / u_device_pixel_ratio; 27 | zoom_frequency *= 1. + ((max_scale_zoom - clamp(u_map_position.z, min_scale_zoom, max_scale_zoom)) * scale_zoom_factor); 28 | 29 | float meter_pixels = u_meters_per_pixel / u_device_pixel_ratio; 30 | 31 | // Sample based on screenspace 32 | const float pixel_scale = 695.; // arbitrary pixel_scale based on playing w/values 33 | vec2 st = gl_FragCoord.xy / pixel_scale; 34 | 35 | // But peg to map center so dots on ground plane stay in place as we move 36 | // (there's what looks like some floating point precision crawl, but it's not too bad) 37 | const float dot_wrap = 1000.; 38 | st += mod(u_map_position.xy / meter_pixels, dot_wrap) / pixel_scale; 39 | 40 | // Rotate dot & find nearest dot distance 41 | vec2 st2 = mat2(0.707, -0.707, 0.707, 0.707) * st; 42 | vec2 nearest = dot_scale * fract(zoom_frequency * st2) - 1.0; 43 | float dist = length(nearest); 44 | 45 | // Modulate the size of the dots 46 | float radius = clamp(sqrt(1.0 - color.g), 0.001, 1.); // use green channel, clamp to avoid giant dot 47 | 48 | // Color mix 49 | const vec3 white = vec3(1.0, 1.0, 1.0); 50 | const vec3 black = vec3(0.0, 0.0, 0.0); 51 | // color = mix(black + (color * float(halftone_colored)), white, aastep(radius, zoom_frequency, dist)); 52 | color.rgb = mix(black, white, aastep(radius, zoom_frequency, dist)); 53 | 54 | # Color version simulating CMYK printing process 55 | colorhalftone: 56 | base: polygons 57 | shaders: 58 | uniforms: 59 | dot_frequency: 100. 60 | dot_scale: 1.5 61 | true_color: false 62 | 63 | blocks: 64 | global: | 65 | 66 | // Antialiasing 67 | float aastep(float threshold, const float freq, float value) { 68 | const float scale = 2.; 69 | const float y_rot = 0.; 70 | float afwidth = freq * (1.0/200.0) / scale / cos(y_rot); 71 | return smoothstep(threshold-afwidth, threshold+afwidth, value); 72 | } 73 | 74 | filter: | 75 | // Distance to nearest point in a grid of 76 | // (frequency x frequency) points over the unit square 77 | // Scale dot size for a subset of zoom 78 | const float max_scale_zoom = 19.; 79 | const float min_scale_zoom = 17.; 80 | const float scale_zoom_factor = .25; 81 | float zoom_frequency = dot_frequency / u_device_pixel_ratio; 82 | zoom_frequency *= 1. + ((max_scale_zoom - clamp(u_map_position.z, min_scale_zoom, max_scale_zoom)) * scale_zoom_factor); 83 | 84 | float meter_pixels = u_meters_per_pixel / u_device_pixel_ratio; 85 | 86 | // Sample based on screenspace 87 | const float pixel_scale = 695.; // arbitrary pixel_scale based on playing w/values 88 | vec2 st = gl_FragCoord.xy / pixel_scale; 89 | 90 | // But peg to map center so dots on ground plane stay in place as we move 91 | // (there's what looks like some floating point precision crawl, but it's not too bad) 92 | const float dot_wrap = 1000.; 93 | st += mod(u_map_position.xy / meter_pixels, dot_wrap) / pixel_scale; 94 | 95 | vec3 white = vec3(0.97); 96 | vec3 black = vec3(0.1); 97 | 98 | // Perform a rough RGB-to-CMYK conversion 99 | vec4 cmyk; 100 | cmyk.xyz = 1.0 - color.rgb; 101 | cmyk.w = min(cmyk.x, min(cmyk.y, cmyk.z)); // Create K 102 | if (!true_color) {cmyk.xyz -= cmyk.w;} // Subtract K equivalent from CMY 103 | 104 | // Distance to nearest point in a grid of 105 | // (frequency x frequency) points over the unit square 106 | vec2 Kst = zoom_frequency*mat2(0.707, -0.707, 0.707, 0.707)*st; 107 | vec2 Kuv = dot_scale*fract(Kst)-(dot_scale/2.); 108 | float k = aastep(0.0, zoom_frequency, sqrt(cmyk.w)-length(Kuv)); 109 | vec2 Cst = zoom_frequency*mat2(0.966, -0.259, 0.259, 0.966)*st; 110 | vec2 Cuv = dot_scale*fract(Cst)-(dot_scale/2.); 111 | float c = aastep(0.0, zoom_frequency, sqrt(cmyk.x)-length(Cuv)); 112 | vec2 Mst = zoom_frequency*mat2(0.966, 0.259, -0.259, 0.966)*st; 113 | vec2 Muv = dot_scale*fract(Mst)-(dot_scale/2.); 114 | float m = aastep(0.0, zoom_frequency, sqrt(cmyk.y)-length(Muv)); 115 | vec2 Yst = zoom_frequency*st; // 0 deg 116 | vec2 Yuv = dot_scale*fract(Yst)-(dot_scale/2.); 117 | float y = aastep(0.0, zoom_frequency, sqrt(cmyk.z)-length(Yuv)); 118 | 119 | vec3 rgbscreen = 1.0 - 0.9*vec3(c,m,y); // most saturated color = .9 120 | rgbscreen = mix(rgbscreen, black, 0.85*k); // darkest black = .85 121 | 122 | // Modulate the size of the dots 123 | //float luminance = (0.299 * color.r + 0.587 * color.g + 0.114 * color.b); // HSP color model 124 | //float radius = clamp(sqrt(1.0 - luminance), 0.001, 1.); // use luminance, clamp to avoid giant dot 125 | 126 | color.rgb = rgbscreen; 127 | 128 | -------------------------------------------------------------------------------- /styles/popup.yaml: -------------------------------------------------------------------------------- 1 | styles: 2 | popup: 3 | base: polygons 4 | shaders: 5 | uniforms: 6 | u_popup_radius: 330 7 | u_popup_height: 5 8 | blocks: 9 | global: | 10 | // Pop-up effect - 3d in center of viewport, fading to 2d at edges 11 | vec4 popup (vec4 position, const vec2 center, const float radius) { 12 | if (position.z > 0.) { 13 | float cd = distance(position.xy, center); 14 | float popup_fade_inner = radius * 2. / 3.; // 0.5 15 | float popup_fade_outer = radius; // 0.75 16 | if (cd > popup_fade_inner) { 17 | position.z *= 1.0 - smoothstep(popup_fade_inner, popup_fade_outer, cd); 18 | } 19 | } 20 | return position; 21 | } 22 | 23 | position: | 24 | position.z *= u_popup_height; // boost height for exaggerated visual effect 25 | position = popup(position, vec2(0., 0.), u_popup_radius * u_meters_per_pixel); 26 | -------------------------------------------------------------------------------- /styles/windows.yaml: -------------------------------------------------------------------------------- 1 | styles: 2 | windows: 3 | base: polygons 4 | animated: true 5 | 6 | shaders: 7 | uniforms: 8 | u_frequency: 10. 9 | u_windowColor: [1., 1., .9] 10 | u_buildingColor: [.8, .8, .7] 11 | u_roofColor: [.5, .4, .5] 12 | blocks: 13 | global: | 14 | 15 | // 3d noise 16 | vec4 v_mod289(vec4 x){return x - floor(x * (1.0 / 289.0)) * 289.0;} 17 | vec4 perm(vec4 x){return v_mod289(((x * 34.0) + 1.0) * x);} 18 | float noise(vec3 p){ 19 | vec3 a = floor(p); 20 | vec3 d = p - a; 21 | d = d * d * (3.0 - 2.0 * d); 22 | 23 | vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); 24 | vec4 k1 = perm(b.xyxy); 25 | vec4 k2 = perm(k1.xyxy + b.zzww); 26 | 27 | vec4 c = k2 + a.zzzz; 28 | vec4 k3 = perm(c); 29 | vec4 k4 = perm(c + 1.0); 30 | 31 | vec4 o1 = fract(k3 * (1.0 / 41.0)); 32 | vec4 o2 = fract(k4 * (1.0 / 41.0)); 33 | 34 | vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); 35 | vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); 36 | 37 | return o4.y * d.y + o4.x * (1.0 - d.y); 38 | } 39 | color: | 40 | vec3 vPos = worldPosition().xyz / u_frequency; 41 | vec3 mask = mix(vec3(0.0), vec3(1.0), step(fract(mod(vPos, .9)), vec3(.4, .4, .6))); 42 | 43 | if (mask.x + mask.y + mask.z > .5) { 44 | color.rgb = u_buildingColor; 45 | color.rgb -= vec3(vPos.z * .1); // height factor 46 | } else { 47 | float noiseColor = 2. * noise(worldPosition().xyz * 0.1 + (floor(u_time * 5.) / 10.)); 48 | color.rgb = u_windowColor * noiseColor; 49 | } 50 | 51 | if (v_normal.z > .6 || v_normal.z < -.6) { 52 | color.rgb = u_roofColor; 53 | color.rgb -= vec3(vPos.z * .01); // height factor 54 | } 55 | 56 | --------------------------------------------------------------------------------