├── .gitignore ├── fixtures ├── no-args.glsl ├── readme.glsl ├── clouds.glsl ├── seascape.glsl ├── volcanic.glsl └── gross.glsl ├── package.json ├── LICENSE.md ├── test.js ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /fixtures/no-args.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | vec4 test() { 4 | return vec4(1, 0, 1, 1); 5 | } 6 | 7 | void main() { 8 | gl_FragColor = test(); 9 | } 10 | -------------------------------------------------------------------------------- /fixtures/readme.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | float noise(float x, float y) { 4 | return 0.0; // K.I.S.S. 5 | } 6 | 7 | float noise(float x, float y, float z) { 8 | return 0.0; 9 | } 10 | 11 | float noise(float x, float y, float z, float w) { 12 | return 0.0; 13 | } 14 | 15 | void main() { 16 | gl_FragColor = vec4(noise(gl_FragCoord.x, gl_FragCoord.y), 0, 0, 1); 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "glsl-token-function-shaker", 3 | "version": "1.0.0", 4 | "license": "MIT", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/stackgl/glsl-token-function-shaker.git" 8 | }, 9 | "scripts": { 10 | "test": "node test | tspec" 11 | }, 12 | "dependencies": { 13 | "glsl-token-functions": "^1.0.0" 14 | }, 15 | "devDependencies": { 16 | "glsl-token-string": "^1.0.1", 17 | "glsl-tokenizer": "^2.1.2", 18 | "tap-spec": "^4.1.1", 19 | "tape": "^4.6.0" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | ===================== 3 | 4 | * Copyright © 2016 [Hugh Kennedy](https://github.com/hughsk/) 5 | * Copyright © 2016 [stackgl](https://github.com/stackgl/) contributors 6 | 7 | *stackgl contributors listed at * 8 | 9 | Permission is hereby granted, free of charge, to any person obtaining a copy of 10 | this software and associated documentation files (the "Software"), to deal 11 | in the Software without restriction, including without limitation the rights 12 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | copies of the Software, and to permit persons to whom the Software is 14 | furnished to do so, subject to the following conditions: 15 | 16 | The above copyright notice and this permission notice shall be included in 17 | all copies or substantial portions of the Software. 18 | 19 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS 25 | IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const tokenize = require('glsl-tokenizer') 2 | const str = require('glsl-token-string') 3 | const test = require('tape') 4 | const path = require('path') 5 | const shake = require('./') 6 | const fs = require('fs') 7 | 8 | test('glsl-token-function-shaker: gross.glsl', createTest('gross.glsl', true)) 9 | test('glsl-token-function-shaker: volcanic.glsl', createTest('volcanic.glsl', false)) 10 | test('glsl-token-function-shaker: seascape.glsl', createTest('seascape.glsl', false)) 11 | test('glsl-token-function-shaker: readme.glsl', createTest('readme.glsl', true)) 12 | test('glsl-token-function-shaker: no-args.glsl', createTest('no-args.glsl', false)) 13 | 14 | // NOTE: currently removes too much due to the presence of functions in #defines 15 | // Probably needs to resolve all the preprocessors beforehand in a separate package? 16 | test('glsl-token-function-shaker: clouds.glsl', createTest('clouds.glsl', true)) 17 | 18 | function createTest (file, shouldModify, shouldLog) { 19 | return function (t) { 20 | const fixture = fs.readFileSync(path.join(__dirname, 'fixtures', file), 'utf8') 21 | const tokens = tokenize(fixture) 22 | const stats = shake(tokens) 23 | 24 | const orig = fixture 25 | const next = str(tokens) 26 | if (shouldLog) console.error(next) 27 | 28 | t.ok(stats.iterations, 'ran at least one iteration') 29 | 30 | if (shouldModify) { 31 | t.ok(stats.functionsRemoved, 'removed at least one function') 32 | t.ok(stats.tokensRemoved, 'removed at least one token') 33 | t.ok(next.length < orig.length, 'reduced final size (' + (100 * next.length / orig.length).toFixed(3) + '% of original)') 34 | } else { 35 | t.ok(!stats.functionsRemoved, 'did not need to remove any functions') 36 | t.ok(!stats.functionsRemoved, 'did not need to remove any tokens') 37 | t.ok(next === orig, 'shader went completely untouched') 38 | } 39 | 40 | t.end() 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # glsl-token-function-shaker 2 | 3 | Shakes out any unused functions from your GLSL shaders. Especially useful alongside tools such as [glslify](https://github.com/stackgl/glslify). 4 | 5 | This is done by running through your shader and checking that each function is actually called. This even works with functions that share the same name but use a different number of arguments! For example, take the following shader: 6 | 7 | ``` glsl 8 | precision mediump float; 9 | 10 | float noise(float x, float y) { 11 | return 0.0; // K.I.S.S. 12 | } 13 | 14 | float noise(float x, float y, float z) { 15 | return 0.0; 16 | } 17 | 18 | float noise(float x, float y, float z, float w) { 19 | return 0.0; 20 | } 21 | 22 | void main() { 23 | gl_FragColor = vec4(noise(gl_FragCoord.x, gl_FragCoord.y), 0, 0, 1); 24 | } 25 | ``` 26 | 27 | After running it through `glsl-token-function-shaker`, you should get something like this in return: 28 | 29 | ``` glsl 30 | precision mediump float; 31 | 32 | float noise(float x, float y) { 33 | return 0.0; // K.I.S.S. 34 | } 35 | 36 | void main() { 37 | gl_FragColor = vec4(noise(gl_FragCoord.x, gl_FragCoord.y), 0, 0, 1); 38 | } 39 | ``` 40 | 41 | This keeps the size of your shaders down, but can also be used to cut out errors in unused functions. 42 | 43 | ## Usage 44 | 45 | Install `glsl-token-function-shaker` using [npm](https://npmjs.com/): 46 | 47 | ``` bash 48 | npm install --save glsl-token-function-shaker 49 | ``` 50 | 51 | ### `shake(tokens[, options])` 52 | 53 | Takes an array of `tokens` from [glsl-tokenizer](https://github.com/stackgl/glsl-tokenizer) and removes unused functions. Accepts the following options: 54 | 55 | * `ignore`: an array of strings containing the names of functions you'd like to keep in the shader, even if they're not being used. `void main();` is always preserved, as is the last function in the shader. 56 | 57 | Modifies the `tokens` array in place, and returns some simple stats: 58 | 59 | * `functionsRemoved`: the number of function declarations removed. 60 | * `tokensRemoved`: the number of GLSL tokens removed. 61 | * `iterations`: the number of function shaking iterations made. 62 | 63 | ``` javascript 64 | const shake = require('glsl-token-function-shaker') 65 | const stringify = require('glsl-token-string') 66 | const tokenize = require('glsl-tokenizer') 67 | const fs = require('fs') 68 | 69 | const src = fs.readFileSync('shader.glsl', 'utf8') 70 | const tokens = tokenize(src) 71 | 72 | shake(tokens, { ignore: ['vert'] }) 73 | 74 | const shaken = stringify(tokens) 75 | ``` 76 | 77 | ## License 78 | 79 | MIT, see [LICENSE.md](LICENSE.md) for details. 80 | -------------------------------------------------------------------------------- /fixtures/clouds.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // Created by inigo quilez - iq/2013 4 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 5 | 6 | // Volumetric clouds. It performs level of detail (LOD) for faster rendering 7 | 8 | float noise( in vec3 x ) 9 | { 10 | vec3 p = floor(x); 11 | vec3 f = fract(x); 12 | f = f*f*(3.0-2.0*f); 13 | vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy; 14 | vec2 rg = texture2D( iChannel0, (uv+ 0.5)/256.0, -100.0 ).yx; 15 | return -1.0+2.0*mix( rg.x, rg.y, f.z ); 16 | } 17 | 18 | float map5( in vec3 p ) 19 | { 20 | vec3 q = p - vec3(0.0,0.1,1.0)*iGlobalTime; 21 | float f; 22 | f = 0.50000*noise( q ); q = q*2.02; 23 | f += 0.25000*noise( q ); q = q*2.03; 24 | f += 0.12500*noise( q ); q = q*2.01; 25 | f += 0.06250*noise( q ); q = q*2.02; 26 | f += 0.03125*noise( q ); 27 | return clamp( 1.5 - p.y - 2.0 + 1.75*f, 0.0, 1.0 ); 28 | } 29 | 30 | float map4( in vec3 p ) 31 | { 32 | vec3 q = p - vec3(0.0,0.1,1.0)*iGlobalTime; 33 | float f; 34 | f = 0.50000*noise( q ); q = q*2.02; 35 | f += 0.25000*noise( q ); q = q*2.03; 36 | f += 0.12500*noise( q ); q = q*2.01; 37 | f += 0.06250*noise( q ); 38 | return clamp( 1.5 - p.y - 2.0 + 1.75*f, 0.0, 1.0 ); 39 | } 40 | float map3( in vec3 p ) 41 | { 42 | vec3 q = p - vec3(0.0,0.1,1.0)*iGlobalTime; 43 | float f; 44 | f = 0.50000*noise( q ); q = q*2.02; 45 | f += 0.25000*noise( q ); q = q*2.03; 46 | f += 0.12500*noise( q ); 47 | return clamp( 1.5 - p.y - 2.0 + 1.75*f, 0.0, 1.0 ); 48 | } 49 | float map2( in vec3 p ) 50 | { 51 | vec3 q = p - vec3(0.0,0.1,1.0)*iGlobalTime; 52 | float f; 53 | f = 0.50000*noise( q ); q = q*2.02; 54 | f += 0.25000*noise( q );; 55 | return clamp( 1.5 - p.y - 2.0 + 1.75*f, 0.0, 1.0 ); 56 | } 57 | 58 | vec3 sundir = normalize( vec3(-1.0,0.0,-1.0) ); 59 | 60 | vec4 integrate( in vec4 sum, in float dif, in float den, in vec3 bgcol, in float t ) 61 | { 62 | // lighting 63 | vec3 lin = vec3(0.65,0.7,0.75)*1.4 + vec3(1.0, 0.6, 0.3)*dif; 64 | vec4 col = vec4( mix( vec3(1.0,0.95,0.8), vec3(0.25,0.3,0.35), den ), den ); 65 | col.xyz *= lin; 66 | col.xyz = mix( col.xyz, bgcol, 1.0-exp(-0.003*t*t) ); 67 | // front to back blending 68 | col.a *= 0.4; 69 | col.rgb *= col.a; 70 | return sum + col*(1.0-sum.a); 71 | } 72 | 73 | #define MARCH(STEPS,MAPLOD) for(int i=0; i2.0 || sum.a > 0.99 ) break; float den = MAPLOD( pos ); if( den>0.01 ) { float dif = clamp((den - MAPLOD(pos+0.3*sundir))/0.6, 0.0, 1.0 ); sum = integrate( sum, dif, den, bgcol, t ); } t += max(0.05,0.02*t); } 74 | 75 | vec4 raymarch( in vec3 ro, in vec3 rd, in vec3 bgcol ) 76 | { 77 | vec4 sum = vec4(0.0); 78 | 79 | float t = 0.0; 80 | 81 | MARCH(30,map5); 82 | MARCH(30,map4); 83 | MARCH(30,map3); 84 | MARCH(30,map2); 85 | 86 | return clamp( sum, 0.0, 1.0 ); 87 | } 88 | 89 | mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) 90 | { 91 | vec3 cw = normalize(ta-ro); 92 | vec3 cp = vec3(sin(cr), cos(cr),0.0); 93 | vec3 cu = normalize( cross(cw,cp) ); 94 | vec3 cv = normalize( cross(cu,cw) ); 95 | return mat3( cu, cv, cw ); 96 | } 97 | 98 | vec4 render( in vec3 ro, in vec3 rd ) 99 | { 100 | // background sky 101 | float sun = clamp( dot(sundir,rd), 0.0, 1.0 ); 102 | vec3 col = vec3(0.6,0.71,0.75) - rd.y*0.2*vec3(1.0,0.5,1.0) + 0.15*0.5; 103 | col += 0.2*vec3(1.0,.6,0.1)*pow( sun, 8.0 ); 104 | 105 | // clouds 106 | vec4 res = raymarch( ro, rd, col ); 107 | col = col*(1.0-res.w) + res.xyz; 108 | 109 | // sun glare 110 | col += 0.2*vec3(1.0,0.4,0.2)*pow( sun, 3.0 ); 111 | 112 | return vec4( col, 1.0 ); 113 | } 114 | 115 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 116 | { 117 | vec2 p = (-iResolution.xy + 2.0*fragCoord.xy)/ iResolution.y; 118 | 119 | vec2 m = iMouse.xy/iResolution.xy; 120 | 121 | // camera 122 | vec3 ro = 4.0*normalize(vec3(sin(3.0*m.x), 0.4*m.y, cos(3.0*m.x))); 123 | vec3 ta = vec3(0.0, -1.0, 0.0); 124 | mat3 ca = setCamera( ro, ta, 0.0 ); 125 | // ray 126 | vec3 rd = ca * normalize( vec3(p.xy,1.5)); 127 | 128 | fragColor = render( ro, rd ); 129 | } 130 | -------------------------------------------------------------------------------- /fixtures/seascape.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | /* 4 | "Seascape" by Alexander Alekseev aka TDM - 2014 5 | License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 6 | Contact: tdmaav@gmail.com 7 | */ 8 | 9 | const int NUM_STEPS = 8; 10 | const float PI = 3.1415; 11 | const float EPSILON = 1e-3; 12 | float EPSILON_NRM = 0.1 / iResolution.x; 13 | 14 | // sea 15 | const int ITER_GEOMETRY = 3; 16 | const int ITER_FRAGMENT = 5; 17 | const float SEA_HEIGHT = 0.6; 18 | const float SEA_CHOPPY = 4.0; 19 | const float SEA_SPEED = 0.8; 20 | const float SEA_FREQ = 0.16; 21 | const vec3 SEA_BASE = vec3(0.1,0.19,0.22); 22 | const vec3 SEA_WATER_COLOR = vec3(0.8,0.9,0.6); 23 | float SEA_TIME = iGlobalTime * SEA_SPEED; 24 | mat2 octave_m = mat2(1.6,1.2,-1.2,1.6); 25 | 26 | // math 27 | mat3 fromEuler(vec3 ang) { 28 | vec2 a1 = vec2(sin(ang.x),cos(ang.x)); 29 | vec2 a2 = vec2(sin(ang.y),cos(ang.y)); 30 | vec2 a3 = vec2(sin(ang.z),cos(ang.z)); 31 | mat3 m; 32 | m[0] = vec3(a1.y*a3.y+a1.x*a2.x*a3.x,a1.y*a2.x*a3.x+a3.y*a1.x,-a2.y*a3.x); 33 | m[1] = vec3(-a2.y*a1.x,a1.y*a2.y,a2.x); 34 | m[2] = vec3(a3.y*a1.x*a2.x+a1.y*a3.x,a1.x*a3.x-a1.y*a3.y*a2.x,a2.y*a3.y); 35 | return m; 36 | } 37 | float hash( vec2 p ) { 38 | float h = dot(p,vec2(127.1,311.7)); 39 | return fract(sin(h)*43758.5453123); 40 | } 41 | float noise( in vec2 p ) { 42 | vec2 i = floor( p ); 43 | vec2 f = fract( p ); 44 | vec2 u = f*f*(3.0-2.0*f); 45 | return -1.0+2.0*mix( mix( hash( i + vec2(0.0,0.0) ), 46 | hash( i + vec2(1.0,0.0) ), u.x), 47 | mix( hash( i + vec2(0.0,1.0) ), 48 | hash( i + vec2(1.0,1.0) ), u.x), u.y); 49 | } 50 | 51 | // lighting 52 | float diffuse(vec3 n,vec3 l,float p) { 53 | return pow(dot(n,l) * 0.4 + 0.6,p); 54 | } 55 | float specular(vec3 n,vec3 l,vec3 e,float s) { 56 | float nrm = (s + 8.0) / (3.1415 * 8.0); 57 | return pow(max(dot(reflect(e,n),l),0.0),s) * nrm; 58 | } 59 | 60 | // sky 61 | vec3 getSkyColor(vec3 e) { 62 | e.y = max(e.y,0.0); 63 | vec3 ret; 64 | ret.x = pow(1.0-e.y,2.0); 65 | ret.y = 1.0-e.y; 66 | ret.z = 0.6+(1.0-e.y)*0.4; 67 | return ret; 68 | } 69 | 70 | // sea 71 | float sea_octave(vec2 uv, float choppy) { 72 | uv += noise(uv); 73 | vec2 wv = 1.0-abs(sin(uv)); 74 | vec2 swv = abs(cos(uv)); 75 | wv = mix(wv,swv,wv); 76 | return pow(1.0-pow(wv.x * wv.y,0.65),choppy); 77 | } 78 | 79 | float map(vec3 p) { 80 | float freq = SEA_FREQ; 81 | float amp = SEA_HEIGHT; 82 | float choppy = SEA_CHOPPY; 83 | vec2 uv = p.xz; uv.x *= 0.75; 84 | 85 | float d, h = 0.0; 86 | for(int i = 0; i < ITER_GEOMETRY; i++) { 87 | d = sea_octave((uv+SEA_TIME)*freq,choppy); 88 | d += sea_octave((uv-SEA_TIME)*freq,choppy); 89 | h += d * amp; 90 | uv *= octave_m; freq *= 1.9; amp *= 0.22; 91 | choppy = mix(choppy,1.0,0.2); 92 | } 93 | return p.y - h; 94 | } 95 | 96 | float map_detailed(vec3 p) { 97 | float freq = SEA_FREQ; 98 | float amp = SEA_HEIGHT; 99 | float choppy = SEA_CHOPPY; 100 | vec2 uv = p.xz; uv.x *= 0.75; 101 | 102 | float d, h = 0.0; 103 | for(int i = 0; i < ITER_FRAGMENT; i++) { 104 | d = sea_octave((uv+SEA_TIME)*freq,choppy); 105 | d += sea_octave((uv-SEA_TIME)*freq,choppy); 106 | h += d * amp; 107 | uv *= octave_m; freq *= 1.9; amp *= 0.22; 108 | choppy = mix(choppy,1.0,0.2); 109 | } 110 | return p.y - h; 111 | } 112 | 113 | vec3 getSeaColor(vec3 p, vec3 n, vec3 l, vec3 eye, vec3 dist) { 114 | float fresnel = 1.0 - max(dot(n,-eye),0.0); 115 | fresnel = pow(fresnel,3.0) * 0.65; 116 | 117 | vec3 reflected = getSkyColor(reflect(eye,n)); 118 | vec3 refracted = SEA_BASE + diffuse(n,l,80.0) * SEA_WATER_COLOR * 0.12; 119 | 120 | vec3 color = mix(refracted,reflected,fresnel); 121 | 122 | float atten = max(1.0 - dot(dist,dist) * 0.001, 0.0); 123 | color += SEA_WATER_COLOR * (p.y - SEA_HEIGHT) * 0.18 * atten; 124 | 125 | color += vec3(specular(n,l,eye,60.0)); 126 | 127 | return color; 128 | } 129 | 130 | // tracing 131 | vec3 getNormal(vec3 p, float eps) { 132 | vec3 n; 133 | n.y = map_detailed(p); 134 | n.x = map_detailed(vec3(p.x+eps,p.y,p.z)) - n.y; 135 | n.z = map_detailed(vec3(p.x,p.y,p.z+eps)) - n.y; 136 | n.y = eps; 137 | return normalize(n); 138 | } 139 | 140 | float heightMapTracing(vec3 ori, vec3 dir, out vec3 p) { 141 | float tm = 0.0; 142 | float tx = 1000.0; 143 | float hx = map(ori + dir * tx); 144 | if(hx > 0.0) return tx; 145 | float hm = map(ori + dir * tm); 146 | float tmid = 0.0; 147 | for(int i = 0; i < NUM_STEPS; i++) { 148 | tmid = mix(tm,tx, hm/(hm-hx)); 149 | p = ori + dir * tmid; 150 | float hmid = map(p); 151 | if(hmid < 0.0) { 152 | tx = tmid; 153 | hx = hmid; 154 | } else { 155 | tm = tmid; 156 | hm = hmid; 157 | } 158 | } 159 | return tmid; 160 | } 161 | 162 | // main 163 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) { 164 | vec2 uv = fragCoord.xy / iResolution.xy; 165 | uv = uv * 2.0 - 1.0; 166 | uv.x *= iResolution.x / iResolution.y; 167 | float time = iGlobalTime * 0.3 + iMouse.x*0.01; 168 | 169 | // ray 170 | vec3 ang = vec3(sin(time*3.0)*0.1,sin(time)*0.2+0.3,time); 171 | vec3 ori = vec3(0.0,3.5,time*5.0); 172 | vec3 dir = normalize(vec3(uv.xy,-2.0)); dir.z += length(uv) * 0.15; 173 | dir = normalize(dir) * fromEuler(ang); 174 | 175 | // tracing 176 | vec3 p; 177 | heightMapTracing(ori,dir,p); 178 | vec3 dist = p - ori; 179 | vec3 n = getNormal(p, dot(dist,dist) * EPSILON_NRM); 180 | vec3 light = normalize(vec3(0.0,1.0,0.8)); 181 | 182 | // color 183 | vec3 color = mix( 184 | getSkyColor(dir), 185 | getSeaColor(p,n,light,dir,dist), 186 | pow(smoothstep(0.0,-0.05,dir.y),0.3)); 187 | 188 | // post 189 | fragColor = vec4(pow(color,vec3(0.75)), 1.0); 190 | } 191 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var tokenFunctions = require('glsl-token-functions') 2 | 3 | var FUNCTION_DEFINITION = '@@_GLSL_TOKEN_FUNCTION_DEFINITION' 4 | var SHOULD_REMOVE = '@@_GLSL_TOKEN_SHOULD_REMOVE' 5 | 6 | module.exports = shake 7 | 8 | function shake (tokens, options) { 9 | options = options || {} 10 | 11 | var functionsRemoved = 0 12 | var tokensRemoved = 0 13 | var iterations = 0 14 | 15 | var ignore = [].concat(options.ignore || []) 16 | var ignoreList = {} 17 | for (var i = 0; i < ignore.length; i++) { 18 | ignoreList[ignore[i]] = true 19 | } 20 | 21 | while (++iterations) { 22 | var results = shakeStep(tokens, ignoreList) 23 | if (!results) break 24 | functionsRemoved += results.functionsRemoved 25 | tokensRemoved += results.tokensRemoved 26 | if (!results.tokensRemoved) break 27 | } 28 | 29 | return { 30 | functionsRemoved: functionsRemoved, 31 | tokensRemoved: tokensRemoved, 32 | iterations: iterations 33 | } 34 | } 35 | 36 | function shakeStep (tokens, ignoreList) { 37 | var fns = tokenFunctions(tokens) 38 | if (!fns.length) return 39 | 40 | var fnIndex = {} 41 | var lastFunction = fns[fns.length - 1].name 42 | 43 | // Build up a list of functions to check 44 | for (var f = 0; f < fns.length; f++) { 45 | var fn = fns[f] 46 | 47 | fn.argCount = fnDefnArgCount(tokens, fn.args) 48 | fnIndex[fn.name] = fnIndex[fn.name] || [] 49 | fnIndex[fn.name].push(fn) 50 | 51 | // Mark the function's name token as such — this way 52 | // we know not to mistakenly delete it later. 53 | var next = nextGlyph(tokens, fn.outer[0]) 54 | if (next !== -1) tokens[next][FUNCTION_DEFINITION] = true 55 | } 56 | 57 | // Run through our tokens and cull any functions from `fnIndex` 58 | // that aren't being used. 59 | for (var i = 0; i < tokens.length; i++) { 60 | // Check if this is a function call. 61 | var token = tokens[i] 62 | if (token.type !== 'ident') continue 63 | if (token[FUNCTION_DEFINITION]) continue 64 | 65 | var index = fnIndex[token.data] 66 | if (!index) continue 67 | 68 | var next = nextGlyph(tokens, i) 69 | if (next === -1) continue 70 | if (tokens[next].data !== '(') continue 71 | 72 | // Remove any function calls that are being used. This is 73 | // grouped by argument count, so functions with the same 74 | // name but different arities will still get removed 75 | // in most cases where appropriate. 76 | var count = countArguments(tokens, next) 77 | var fnArgCount = count[0] 78 | var fnCallEnds = count[1] 79 | 80 | for (var j = 0; j < index.length; j++) { 81 | if (index[j].argCount === fnArgCount) { 82 | index.splice(j--, 1) 83 | } 84 | } 85 | 86 | if (!index.length) delete fnIndex[token.data] 87 | } 88 | 89 | var functionsRemoved = 0 90 | var tokensRemoved = 0 91 | 92 | for (var name in fnIndex) { 93 | // Don't delete `main`, the last function in the shader, 94 | // any functions passed through `opts.ignore`, or any functions 95 | // that are being used in the shader. 96 | if (name === 'main') continue 97 | if (name === lastFunction) continue 98 | if (ignoreList[name]) continue 99 | if (!fnIndex.hasOwnProperty(name)) continue 100 | 101 | // Otherwise run through the remaining missing functions 102 | // and mark their tokens for deletion. 103 | var index = fnIndex[name] 104 | for (var k = 0; k < index.length; k++) { 105 | var start = index[k].outer[0] 106 | var finish = Math.min(tokens.length, index[k].outer[1]) 107 | functionsRemoved++ 108 | 109 | for (var b = start; b < finish; b++) { 110 | tokensRemoved++ 111 | tokens[b][SHOULD_REMOVE] = true 112 | } 113 | 114 | if (tokens[b] && tokens[b].type === 'whitespace') { 115 | tokensRemoved++ 116 | tokens[b][SHOULD_REMOVE] = true 117 | } 118 | } 119 | } 120 | 121 | // Finally, remove all of the remaining tokens! 122 | for (var t = 0; t < tokens.length; t++) { 123 | if (!tokens[t]) break 124 | if (!tokens[t][SHOULD_REMOVE]) continue 125 | for (var u = t; u < tokens.length; u++) { 126 | if (tokens[u][SHOULD_REMOVE]) continue 127 | var diff = u - t 128 | tokens.splice(t, diff) 129 | t -= diff 130 | break 131 | } 132 | } 133 | 134 | return { 135 | functionsRemoved: functionsRemoved, 136 | tokensRemoved: tokensRemoved 137 | } 138 | } 139 | 140 | // Returns the index of the next non-whitespace token after `idx` 141 | function nextGlyph (tokens, idx) { 142 | for (var i = idx + 1; i < tokens.length; i++) { 143 | if (tokens[i].type !== 'whitespace') return i 144 | } 145 | return -1 146 | } 147 | 148 | // Returns a tuple containing the number of arguments for the function call 149 | // whose opening parenthesis starts at `idx`, and the token index where the 150 | // function call finishes. 151 | function countArguments (tokens, idx) { 152 | var hitGlyph = false 153 | var parDepth = 1 154 | var count = 0 155 | for (var i = idx + 1; i < tokens.length; i++) { 156 | var data = tokens[i].data 157 | if (data === ',') { 158 | if (parDepth === 1) count++ 159 | } else 160 | if (data === ')') { 161 | if (!--parDepth) return [count + hitGlyph, i] 162 | } else 163 | if (data === '(') { 164 | parDepth++ 165 | } else 166 | if (data === 'void') { 167 | continue 168 | } else 169 | if (tokens[i].type !== 'whitespace') { 170 | hitGlyph = true 171 | } 172 | } 173 | 174 | return [count, -1] 175 | } 176 | 177 | function fnDefnArgCount (tokens, args) { 178 | var hitGlyph = false 179 | var count = 0 180 | 181 | for (var i = args[0] + 1; i < args[1]; i++) { 182 | if (tokens[i].data === ')') break 183 | if (tokens[i].type !== 'whitespace') hitGlyph = true 184 | if (tokens[i].data === ',') count++ 185 | } 186 | 187 | return count + hitGlyph 188 | } 189 | -------------------------------------------------------------------------------- /fixtures/volcanic.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | // Created by inigo quilez - iq/2013 4 | // License Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Unported License. 5 | 6 | 7 | //#define HIGH_QUALITY_NOISE 8 | 9 | float noise( in vec3 x ) 10 | { 11 | vec3 p = floor(x); 12 | vec3 f = fract(x); 13 | f = f*f*(3.0-2.0*f); 14 | #ifndef HIGH_QUALITY_NOISE 15 | vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy; 16 | vec2 rg = texture2D( iChannel0, (uv+ 0.5)/256.0, -100.0 ).yx; 17 | #else 18 | vec2 uv = (p.xy+vec2(37.0,17.0)*p.z); 19 | vec2 rg1 = texture2D( iChannel0, (uv+ vec2(0.5,0.5))/256.0, -100.0 ).yx; 20 | vec2 rg2 = texture2D( iChannel0, (uv+ vec2(1.5,0.5))/256.0, -100.0 ).yx; 21 | vec2 rg3 = texture2D( iChannel0, (uv+ vec2(0.5,1.5))/256.0, -100.0 ).yx; 22 | vec2 rg4 = texture2D( iChannel0, (uv+ vec2(1.5,1.5))/256.0, -100.0 ).yx; 23 | vec2 rg = mix( mix(rg1,rg2,f.x), mix(rg3,rg4,f.x), f.y ); 24 | #endif 25 | return mix( rg.x, rg.y, f.z ); 26 | } 27 | 28 | 29 | 30 | float noise( in vec2 x ) 31 | { 32 | vec2 p = floor(x); 33 | vec2 f = fract(x); 34 | vec2 uv = p.xy + f.xy*f.xy*(3.0-2.0*f.xy); 35 | return texture2D( iChannel0, (uv+118.4)/256.0, -100.0 ).x; 36 | } 37 | 38 | vec4 texcube( sampler2D sam, in vec3 p, in vec3 n ) 39 | { 40 | vec4 x = texture2D( sam, p.yz ); 41 | vec4 y = texture2D( sam, p.zx ); 42 | vec4 z = texture2D( sam, p.xy ); 43 | return x*abs(n.x) + y*abs(n.y) + z*abs(n.z); 44 | } 45 | 46 | //===================================================================== 47 | 48 | float lava( vec2 p ) 49 | { 50 | p += vec2(2.0,4.0); 51 | float f; 52 | f = 0.5000*noise( p ); p = p*2.02; 53 | f += 0.2500*noise( p ); p = p*2.03; 54 | f += 0.1250*noise( p ); p = p*2.01; 55 | f += 0.0625*noise( p ); 56 | return f; 57 | } 58 | 59 | const mat3 m = mat3( 0.00, 0.80, 0.60, 60 | -0.80, 0.36, -0.48, 61 | -0.60, -0.48, 0.64 ); 62 | 63 | float displacement( vec3 p ) 64 | { 65 | p += vec3(1.0,0.0,0.8); 66 | 67 | float f; 68 | f = 0.5000*noise( p ); p = m*p*2.02; 69 | f += 0.2500*noise( p ); p = m*p*2.03; 70 | f += 0.1250*noise( p ); p = m*p*2.01; 71 | f += 0.0625*noise( p ); 72 | 73 | float n = noise( p*3.5 ); 74 | f += 0.03*n*n; 75 | 76 | return f; 77 | } 78 | 79 | float mapTerrain( in vec3 pos ) 80 | { 81 | return pos.y*0.1 + (displacement(pos*vec3(0.8,1.0,0.8)) - 0.4)*(1.0-smoothstep(1.0,3.0,pos.y)); 82 | } 83 | 84 | float raymarchTerrain( in vec3 ro, in vec3 rd ) 85 | { 86 | float maxd = 30.0; 87 | float t = 0.1; 88 | for( int i=0; i<160; i++ ) 89 | { 90 | float h = mapTerrain( ro+rd*t ); 91 | if( h<(0.001*t) || t>maxd ) break; 92 | t += h; 93 | } 94 | 95 | if( t>maxd ) t=-1.0; 96 | return t; 97 | } 98 | 99 | vec3 calcNormal( in vec3 pos, in float t ) 100 | { 101 | vec3 eps = vec3( max(0.02,0.001*t),0.0,0.0); 102 | return normalize( vec3( 103 | mapTerrain(pos+eps.xyy) - mapTerrain(pos-eps.xyy), 104 | mapTerrain(pos+eps.yxy) - mapTerrain(pos-eps.yxy), 105 | mapTerrain(pos+eps.yyx) - mapTerrain(pos-eps.yyx) ) ); 106 | 107 | } 108 | 109 | vec3 lig = normalize( vec3(-0.3,0.4,0.7) ); 110 | 111 | vec4 mapClouds( in vec3 pos ) 112 | { 113 | vec3 q = pos*0.5 + vec3(0.0,-iGlobalTime,0.0); 114 | 115 | float d; 116 | d = 0.5000*noise( q ); q = q*2.02; 117 | d += 0.2500*noise( q ); q = q*2.03; 118 | d += 0.1250*noise( q ); q = q*2.01; 119 | d += 0.0625*noise( q ); 120 | 121 | d = d - 0.55; 122 | d *= smoothstep( 0.5, 0.55, lava(0.1*pos.xz)+0.01 ); 123 | 124 | d = clamp( d, 0.0, 1.0 ); 125 | 126 | vec4 res = vec4( d ); 127 | 128 | res.xyz = mix( vec3(1.0,0.8,0.7), 0.2*vec3(0.4,0.4,0.4), res.x ); 129 | res.xyz *= 0.25; 130 | res.xyz *= 0.5 + 0.5*smoothstep( -2.0, 1.0, pos.y ); 131 | 132 | return res; 133 | } 134 | 135 | vec4 raymarchClouds( in vec3 ro, in vec3 rd, in vec3 bcol, float tmax ) 136 | { 137 | vec4 sum = vec4( 0.0 ); 138 | 139 | float sun = pow( clamp( dot(rd,lig), 0.0, 1.0 ),6.0 ); 140 | float t = 0.0; 141 | for( int i=0; i<60; i++ ) 142 | { 143 | if( t>tmax || sum.w>0.95 ) break;//continue; 144 | vec3 pos = ro + t*rd; 145 | vec4 col = mapClouds( pos ); 146 | 147 | col.xyz += vec3(1.0,0.7,0.4)*0.4*sun*(1.0-col.w); 148 | col.xyz = mix( col.xyz, bcol, 1.0-exp(-0.00006*t*t*t) ); 149 | 150 | col.rgb *= col.a; 151 | 152 | sum = sum + col*(1.0 - sum.a); 153 | 154 | t += max(0.1,0.05*t); 155 | } 156 | 157 | sum.xyz /= (0.001+sum.w); 158 | 159 | return clamp( sum, 0.0, 1.0 ); 160 | } 161 | 162 | float softshadow( in vec3 ro, in vec3 rd, float mint, float k ) 163 | { 164 | float res = 1.0; 165 | float t = mint; 166 | for( int i=0; i<48; i++ ) 167 | { 168 | float h = mapTerrain(ro + rd*t); 169 | h = max( h, 0.0 ); 170 | res = min( res, k*h/t ); 171 | t += clamp( h, 0.02, 0.5 ); 172 | if( h<0.0001 ) break; 173 | } 174 | return clamp(res,0.0,1.0); 175 | } 176 | 177 | vec3 path( float time ) 178 | { 179 | return vec3( 16.0*cos(0.2+0.5*.1*time*1.5), 1.5, 16.0*sin(0.1+0.5*0.11*time*1.5) ); 180 | 181 | } 182 | 183 | mat3 setCamera( in vec3 ro, in vec3 ta, float cr ) 184 | { 185 | vec3 cw = normalize(ta-ro); 186 | vec3 cp = vec3(sin(cr), cos(cr),0.0); 187 | vec3 cu = normalize( cross(cw,cp) ); 188 | vec3 cv = normalize( cross(cu,cw) ); 189 | return mat3( cu, cv, cw ); 190 | } 191 | 192 | void mainImage( out vec4 fragColor, in vec2 fragCoord ) 193 | { 194 | vec2 q = fragCoord.xy / iResolution.xy; 195 | vec2 p = -1.0 + 2.0*q; 196 | p.x *= iResolution.x / iResolution.y; 197 | 198 | 199 | // camera 200 | float off = step( 0.001, iMouse.z )*6.0*iMouse.x/iResolution.x; 201 | float time = 2.7+iGlobalTime + off; 202 | //time =35.0; 203 | vec3 ro = path( time+0.0 ); 204 | vec3 ta = path( time+1.6 ); 205 | //ta.y *= 0.3 + 0.25*cos(0.11*time); 206 | ta.y *= 0.35 + 0.25*sin(0.09*time); 207 | float roll = 0.3*sin(1.0+0.07*time); 208 | // camera2world transform 209 | mat3 cam = setCamera( ro, ta, roll ); 210 | 211 | // ray 212 | float r2 = p.x*p.x*0.32 + p.y*p.y; 213 | p *= (7.0-sqrt(37.5-11.5*r2))/(r2+1.0); 214 | vec3 rd = cam * normalize(vec3(p.xy,2.1)); 215 | 216 | // sky 217 | vec3 col = vec3(0.32,0.36,0.4) - rd.y*0.4; 218 | float sun = clamp( dot(rd,lig), 0.0, 1.0 ); 219 | col += vec3(1.0,0.8,0.4)*0.2*pow( sun, 6.0 ); 220 | col *= 0.9; 221 | 222 | vec3 bcol = col; 223 | 224 | // terrain 225 | float t = raymarchTerrain(ro, rd); 226 | if( t>0.0 ) 227 | { 228 | vec3 pos = ro + t*rd; 229 | vec3 nor = calcNormal( pos, t ); 230 | vec3 ref = reflect( rd, nor ); 231 | 232 | vec3 bn = -1.0 + 2.0*texcube( iChannel0, 3.0*pos/4.0, nor ).xyz; 233 | nor = normalize( nor + 0.6*bn ); 234 | 235 | float hh = 1.0 - smoothstep( -2.0, 1.0, pos.y ); 236 | 237 | // lighting 238 | float sun = clamp( dot( nor, lig ), 0.0, 1.0 ); 239 | float sha = 0.0; if( sun>0.01) sha=softshadow(pos,lig,0.01,32.0); 240 | float bac = clamp( dot( nor, normalize(lig*vec3(-1.0,0.0,-1.0)) ), 0.0, 1.0 ); 241 | float sky = 0.5 + 0.5*nor.y; 242 | float lav = smoothstep( 0.5, 0.55, lava(0.1*pos.xz) )*hh*clamp(0.5-0.5*nor.y,0.0,1.0); 243 | float occ = pow( (1.0-displacement(pos*vec3(0.8,1.0,0.8)))*1.6-0.5, 2.0 ); 244 | 245 | float amb = 1.0; 246 | 247 | col = vec3(0.8); 248 | 249 | vec3 lin = vec3(0.0); 250 | lin += sun*vec3(1.80,1.27,0.99)*pow(vec3(sha),vec3(1.0,1.2,1.5)); 251 | lin += sky*vec3(0.16,0.20,0.40)*occ; 252 | lin += bac*vec3(0.40,0.28,0.20)*occ; 253 | lin += amb*vec3(0.15,0.17,0.20)*occ; 254 | lin += lav*vec3(3.00,0.61,0.00); 255 | 256 | 257 | // surface shading/material 258 | col = texcube( iChannel1, 0.5*pos, nor ).xyz; 259 | col = col*(0.2+0.8*texcube( iChannel2, 4.0*vec3(2.0,8.0,2.0)*pos, nor ).x); 260 | vec3 verde = vec3(1.0,0.9,0.2); 261 | verde *= texture2D( iChannel2, pos.xz ).xyz; 262 | col = mix( col, 0.8*verde, hh ); 263 | 264 | float vv = smoothstep( 0.0, 0.8, nor.y )*smoothstep(0.0, 0.1, pos.y-0.8 ); 265 | verde = vec3(0.2,0.45,0.1); 266 | verde *= texture2D( iChannel2, 30.0*pos.xz ).xyz; 267 | verde += 0.2*texture2D( iChannel2, 1.0*pos.xz ).xyz; 268 | vv *= smoothstep( 0.0, 0.5, texture2D( iChannel2, 0.1*pos.xz + 0.01*nor.x ).x ); 269 | col = mix( col, verde*1.1, vv ); 270 | 271 | // light/surface interaction 272 | col = lin * col; 273 | 274 | // atmospheric 275 | col = mix( col, (1.0-0.7*hh)*bcol, 1.0-exp(-0.00006*t*t*t) ); 276 | } 277 | 278 | // sun glow 279 | col += vec3(1.0,0.6,0.2)*0.2*pow( sun, 2.0 )*clamp( (rd.y+0.4)/(0.0+0.4),0.0,1.0); 280 | 281 | // smoke 282 | { 283 | if( t<0.0 ) t=600.0; 284 | vec4 res = raymarchClouds( ro, rd, bcol, t ); 285 | col = mix( col, res.xyz, res.w ); 286 | } 287 | 288 | // gamma 289 | col = pow( clamp( col, 0.0, 1.0 ), vec3(0.45) ); 290 | 291 | // contrast, desat, tint and vignetting 292 | col = col*0.3 + 0.7*col*col*(3.0-2.0*col); 293 | col = mix( col, vec3(col.x+col.y+col.z)*0.33, 0.2 ); 294 | col *= 1.3*vec3(1.06,1.1,1.0); 295 | col *= 0.5 + 0.5*pow( 16.0*q.x*q.y*(1.0-q.x)*(1.0-q.y), 0.1 ); 296 | 297 | fragColor = vec4( col, 1.0 ); 298 | } 299 | -------------------------------------------------------------------------------- /fixtures/gross.glsl: -------------------------------------------------------------------------------- 1 | precision mediump float; 2 | 3 | #define beat1 (texture2D(iChannel0, vec2(0.1)).r * 2.) 4 | #define beat2 (texture2D(iChannel0, vec2(0.8)).r * 2.) 5 | 6 | vec2 doModel(vec3 p, vec2 beats); 7 | 8 | vec2 calcRayIntersection_2_0(vec3 rayOrigin, vec3 rayDir, float maxd, float precis, vec2 beats) { 9 | float latest = precis * 2.0; 10 | float dist = +0.0; 11 | float type = -1.0; 12 | vec2 res = vec2(-1.0, -1.0); 13 | 14 | for (int i = 0; i < 90; i++) { 15 | if (latest < precis || dist > maxd) break; 16 | 17 | vec2 result = doModel(rayOrigin + rayDir * dist, beats); 18 | 19 | latest = result.x; 20 | type = result.y; 21 | dist += latest; 22 | } 23 | 24 | if (dist < maxd) { 25 | res = vec2(dist, type); 26 | } 27 | 28 | return res; 29 | } 30 | 31 | vec2 calcRayIntersection_2_0(vec3 rayOrigin, vec3 rayDir, vec2 beats) { 32 | return calcRayIntersection_2_0(rayOrigin, rayDir, 20.0, 0.001, beats); 33 | } 34 | 35 | 36 | 37 | vec3 calcNormal_4_1(vec3 pos, float eps, vec2 beats) { 38 | const vec3 v1 = vec3( 1.0,-1.0,-1.0); 39 | const vec3 v2 = vec3(-1.0,-1.0, 1.0); 40 | const vec3 v3 = vec3(-1.0, 1.0,-1.0); 41 | const vec3 v4 = vec3( 1.0, 1.0, 1.0); 42 | 43 | return normalize( v1 * doModel( pos + v1*eps, beats ).x + 44 | v2 * doModel( pos + v2*eps, beats ).x + 45 | v3 * doModel( pos + v3*eps, beats ).x + 46 | v4 * doModel( pos + v4*eps, beats ).x ); 47 | } 48 | 49 | vec3 calcNormal_4_1(vec3 pos, vec2 beats) { 50 | return calcNormal_4_1(pos, 0.002, beats); 51 | } 52 | 53 | vec4 texcube( sampler2D sam, in vec3 p, in vec3 n ) 54 | { 55 | vec4 x = texture2D( sam, p.yz ); 56 | vec4 y = texture2D( sam, p.zx ); 57 | vec4 z = texture2D( sam, p.xy ); 58 | return x*abs(n.x) + y*abs(n.y) + z*abs(n.z); 59 | } 60 | 61 | 62 | 63 | float orenNayarDiffuse_3_2( 64 | vec3 lightDirection, 65 | vec3 viewDirection, 66 | vec3 surfaceNormal, 67 | float roughness, 68 | float albedo) { 69 | 70 | float LdotV = dot(lightDirection, viewDirection); 71 | float NdotL = dot(lightDirection, surfaceNormal); 72 | float NdotV = dot(surfaceNormal, viewDirection); 73 | 74 | float s = LdotV - NdotL * NdotV; 75 | float t = mix(1.0, max(NdotL, NdotV), step(0.0, s)); 76 | 77 | float sigma2 = roughness * roughness; 78 | float A = 1.0 + sigma2 * (albedo / (sigma2 + 0.13) + 0.5 / (sigma2 + 0.33)); 79 | float B = 0.45 * sigma2 / (sigma2 + 0.09); 80 | 81 | return albedo * max(0.0, NdotL) * (A + B * s / t) / 3.14159265; 82 | } 83 | 84 | 85 | float gaussianSpecular_5_3( 86 | vec3 lightDirection, 87 | vec3 viewDirection, 88 | vec3 surfaceNormal, 89 | float shininess) { 90 | vec3 H = normalize(lightDirection + viewDirection); 91 | float theta = acos(dot(H, surfaceNormal)); 92 | float w = theta / shininess; 93 | return exp(-w*w); 94 | } 95 | 96 | 97 | // 98 | // Description : Array and textureless GLSL 2D/3D/4D simplex 99 | // noise functions. 100 | // Author : Ian McEwan, Ashima Arts. 101 | // Maintainer : ijm 102 | // Lastmod : 20110822 (ijm) 103 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 104 | // Distributed under the MIT License. See LICENSE file. 105 | // https://github.com/ashima/webgl-noise 106 | // 107 | 108 | vec4 mod289_1_4(vec4 x) { 109 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 110 | 111 | float mod289_1_4(float x) { 112 | return x - floor(x * (1.0 / 289.0)) * 289.0; } 113 | 114 | vec4 permute_1_5(vec4 x) { 115 | return mod289_1_4(((x*34.0)+1.0)*x); 116 | } 117 | 118 | float permute_1_5(float x) { 119 | return mod289_1_4(((x*34.0)+1.0)*x); 120 | } 121 | 122 | vec4 taylorInvSqrt_1_6(vec4 r) 123 | { 124 | return 1.79284291400159 - 0.85373472095314 * r; 125 | } 126 | 127 | float taylorInvSqrt_1_6(float r) 128 | { 129 | return 1.79284291400159 - 0.85373472095314 * r; 130 | } 131 | 132 | vec4 grad4_1_7(float j, vec4 ip) 133 | { 134 | const vec4 ones = vec4(1.0, 1.0, 1.0, -1.0); 135 | vec4 p,s; 136 | 137 | p.xyz = floor( fract (vec3(j) * ip.xyz) * 7.0) * ip.z - 1.0; 138 | p.w = 1.5 - dot(abs(p.xyz), ones.xyz); 139 | s = vec4(lessThan(p, vec4(0.0))); 140 | p.xyz = p.xyz + (s.xyz*2.0 - 1.0) * s.www; 141 | 142 | return p; 143 | } 144 | 145 | // (sqrt(5) - 1)/4 = F4, used once below 146 | #define F4 0.309016994374947451 147 | 148 | float snoise_1_8(vec4 v) 149 | { 150 | const vec4 C = vec4( 0.138196601125011, // (5 - sqrt(5))/20 G4 151 | 0.276393202250021, // 2 * G4 152 | 0.414589803375032, // 3 * G4 153 | -0.447213595499958); // -1 + 4 * G4 154 | 155 | // First corner 156 | vec4 i = floor(v + dot(v, vec4(F4)) ); 157 | vec4 x0 = v - i + dot(i, C.xxxx); 158 | 159 | // Other corners 160 | 161 | // Rank sorting originally contributed by Bill Licea-Kane, AMD (formerly ATI) 162 | vec4 i0; 163 | vec3 isX = step( x0.yzw, x0.xxx ); 164 | vec3 isYZ = step( x0.zww, x0.yyz ); 165 | // i0.x = dot( isX, vec3( 1.0 ) ); 166 | i0.x = isX.x + isX.y + isX.z; 167 | i0.yzw = 1.0 - isX; 168 | // i0.y += dot( isYZ.xy, vec2( 1.0 ) ); 169 | i0.y += isYZ.x + isYZ.y; 170 | i0.zw += 1.0 - isYZ.xy; 171 | i0.z += isYZ.z; 172 | i0.w += 1.0 - isYZ.z; 173 | 174 | // i0 now contains the unique values 0,1,2,3 in each channel 175 | vec4 i3 = clamp( i0, 0.0, 1.0 ); 176 | vec4 i2 = clamp( i0-1.0, 0.0, 1.0 ); 177 | vec4 i1 = clamp( i0-2.0, 0.0, 1.0 ); 178 | 179 | // x0 = x0 - 0.0 + 0.0 * C.xxxx 180 | // x1 = x0 - i1 + 1.0 * C.xxxx 181 | // x2 = x0 - i2 + 2.0 * C.xxxx 182 | // x3 = x0 - i3 + 3.0 * C.xxxx 183 | // x4 = x0 - 1.0 + 4.0 * C.xxxx 184 | vec4 x1 = x0 - i1 + C.xxxx; 185 | vec4 x2 = x0 - i2 + C.yyyy; 186 | vec4 x3 = x0 - i3 + C.zzzz; 187 | vec4 x4 = x0 + C.wwww; 188 | 189 | // Permutations 190 | i = mod289_1_4(i); 191 | float j0 = permute_1_5( permute_1_5( permute_1_5( permute_1_5(i.w) + i.z) + i.y) + i.x); 192 | vec4 j1 = permute_1_5( permute_1_5( permute_1_5( permute_1_5 ( 193 | i.w + vec4(i1.w, i2.w, i3.w, 1.0 )) 194 | + i.z + vec4(i1.z, i2.z, i3.z, 1.0 )) 195 | + i.y + vec4(i1.y, i2.y, i3.y, 1.0 )) 196 | + i.x + vec4(i1.x, i2.x, i3.x, 1.0 )); 197 | 198 | // Gradients: 7x7x6 points over a cube, mapped onto a 4-cross polytope 199 | // 7*7*6 = 294, which is close to the ring size 17*17 = 289. 200 | vec4 ip = vec4(1.0/294.0, 1.0/49.0, 1.0/7.0, 0.0) ; 201 | 202 | vec4 p0_1_9 = grad4_1_7(j0, ip); 203 | vec4 p1 = grad4_1_7(j1.x, ip); 204 | vec4 p2 = grad4_1_7(j1.y, ip); 205 | vec4 p3 = grad4_1_7(j1.z, ip); 206 | vec4 p4 = grad4_1_7(j1.w, ip); 207 | 208 | // Normalise gradients 209 | vec4 norm = taylorInvSqrt_1_6(vec4(dot(p0_1_9,p0_1_9), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 210 | p0_1_9 *= norm.x; 211 | p1 *= norm.y; 212 | p2 *= norm.z; 213 | p3 *= norm.w; 214 | p4 *= taylorInvSqrt_1_6(dot(p4,p4)); 215 | 216 | // Mix contributions from the five corners 217 | vec3 m0 = max(0.6 - vec3(dot(x0,x0), dot(x1,x1), dot(x2,x2)), 0.0); 218 | vec2 m1 = max(0.6 - vec2(dot(x3,x3), dot(x4,x4) ), 0.0); 219 | m0 = m0 * m0; 220 | m1 = m1 * m1; 221 | return 49.0 * ( dot(m0*m0, vec3( dot( p0_1_9, x0 ), dot( p1, x1 ), dot( p2, x2 ))) 222 | + dot(m1*m1, vec2( dot( p3, x3 ), dot( p4, x4 ) ) ) ) ; 223 | 224 | } 225 | 226 | 227 | 228 | float fogFactorExp2_6_10( 229 | const float dist, 230 | const float density 231 | ) { 232 | const float LOG2 = -1.442695; 233 | float d = density * dist; 234 | return 1.0 - clamp(exp2(d * d * LOG2), 0.0, 1.0); 235 | } 236 | 237 | // 238 | // Description : Array and textureless GLSL 2D/3D/4D simplex 239 | // noise functions. 240 | // Author : Ian McEwan, Ashima Arts. 241 | // Maintainer : ijm 242 | // Lastmod : 20110822 (ijm) 243 | // License : Copyright (C) 2011 Ashima Arts. All rights reserved. 244 | // Distributed under the MIT License. See LICENSE file. 245 | // https://github.com/ashima/webgl-noise 246 | // 247 | 248 | vec3 mod289(vec3 x) { 249 | return x - floor(x * (1.0 / 289.0)) * 289.0; 250 | } 251 | 252 | vec4 mod289(vec4 x) { 253 | return x - floor(x * (1.0 / 289.0)) * 289.0; 254 | } 255 | 256 | vec4 permute(vec4 x) { 257 | return mod289(((x*34.0)+1.0)*x); 258 | } 259 | 260 | vec4 taylorInvSqrt(vec4 r) 261 | { 262 | return 1.79284291400159 - 0.85373472095314 * r; 263 | } 264 | 265 | float snoise3(vec3 v) 266 | { 267 | const vec2 C = vec2(1.0/6.0, 1.0/3.0) ; 268 | const vec4 D = vec4(0.0, 0.5, 1.0, 2.0); 269 | 270 | // First corner 271 | vec3 i = floor(v + dot(v, C.yyy) ); 272 | vec3 x0 = v - i + dot(i, C.xxx) ; 273 | 274 | // Other corners 275 | vec3 g = step(x0.yzx, x0.xyz); 276 | vec3 l = 1.0 - g; 277 | vec3 i1 = min( g.xyz, l.zxy ); 278 | vec3 i2 = max( g.xyz, l.zxy ); 279 | 280 | // x0 = x0 - 0.0 + 0.0 * C.xxx; 281 | // x1 = x0 - i1 + 1.0 * C.xxx; 282 | // x2 = x0 - i2 + 2.0 * C.xxx; 283 | // x3 = x0 - 1.0 + 3.0 * C.xxx; 284 | vec3 x1 = x0 - i1 + C.xxx; 285 | vec3 x2 = x0 - i2 + C.yyy; // 2.0*C.x = 1/3 = C.y 286 | vec3 x3 = x0 - D.yyy; // -1.0+3.0*C.x = -0.5 = -D.y 287 | 288 | // Permutations 289 | i = mod289(i); 290 | vec4 p = permute( permute( permute( 291 | i.z + vec4(0.0, i1.z, i2.z, 1.0 )) 292 | + i.y + vec4(0.0, i1.y, i2.y, 1.0 )) 293 | + i.x + vec4(0.0, i1.x, i2.x, 1.0 )); 294 | 295 | // Gradients: 7x7 points over a square, mapped onto an octahedron. 296 | // The ring size 17*17 = 289 is close to a multiple of 49 (49*6 = 294) 297 | float n_ = 0.142857142857; // 1.0/7.0 298 | vec3 ns = n_ * D.wyz - D.xzx; 299 | 300 | vec4 j = p - 49.0 * floor(p * ns.z * ns.z); // mod(p,7*7) 301 | 302 | vec4 x_ = floor(j * ns.z); 303 | vec4 y_ = floor(j - 7.0 * x_ ); // mod(j,N) 304 | 305 | vec4 x = x_ *ns.x + ns.yyyy; 306 | vec4 y = y_ *ns.x + ns.yyyy; 307 | vec4 h = 1.0 - abs(x) - abs(y); 308 | 309 | vec4 b0 = vec4( x.xy, y.xy ); 310 | vec4 b1 = vec4( x.zw, y.zw ); 311 | 312 | //vec4 s0 = vec4(lessThan(b0,0.0))*2.0 - 1.0; 313 | //vec4 s1 = vec4(lessThan(b1,0.0))*2.0 - 1.0; 314 | vec4 s0 = floor(b0)*2.0 + 1.0; 315 | vec4 s1 = floor(b1)*2.0 + 1.0; 316 | vec4 sh = -step(h, vec4(0.0)); 317 | 318 | vec4 a0 = b0.xzyw + s0.xzyw*sh.xxyy ; 319 | vec4 a1 = b1.xzyw + s1.xzyw*sh.zzww ; 320 | 321 | vec3 p0 = vec3(a0.xy,h.x); 322 | vec3 p1 = vec3(a0.zw,h.y); 323 | vec3 p2 = vec3(a1.xy,h.z); 324 | vec3 p3 = vec3(a1.zw,h.w); 325 | 326 | //Normalise gradients 327 | vec4 norm = taylorInvSqrt(vec4(dot(p0,p0), dot(p1,p1), dot(p2, p2), dot(p3,p3))); 328 | p0 *= norm.x; 329 | p1 *= norm.y; 330 | p2 *= norm.z; 331 | p3 *= norm.w; 332 | 333 | // Mix final noise value 334 | vec4 m = max(0.6 - vec4(dot(x0,x0), dot(x1,x1), dot(x2,x2), dot(x3,x3)), 0.0); 335 | m = m * m; 336 | return 42.0 * dot( m*m, vec4( dot(p0,x0), dot(p1,x1), 337 | dot(p2,x2), dot(p3,x3) ) ); 338 | } 339 | 340 | 341 | 342 | const float PI_7_11 = 3.14159265359; 343 | 344 | 345 | 346 | 347 | vec3 ta = vec3(0, 0, iChannelTime[0] * 2.75); 348 | vec3 ro = ta - vec3(0, 0, 2.5); 349 | vec3 l1 = ro + vec3(0, sin(iChannelTime[0] * 5.) * 0.5, 11.25 + cos(iChannelTime[0] * 0.97) * 9.); 350 | vec3 l2 = ta + vec3(0, 0, 5.25 + sin(iChannelTime[0] * 0.85) * 7.); 351 | vec3 c1 = vec3(0.1, 0.3, 0.9) * 1.; 352 | vec3 c2 = vec3(0.4, 0.1, 0.2) * 1.; 353 | vec3 c3 = vec3(0.3, 0.08, 0.05) * 0.25; 354 | vec3 bg = vec3(0.2, 0.5, 0.925); 355 | 356 | vec2 path(float progress) { 357 | return 1.2 * vec2(cos(progress * 0.59), 1.5 * sin(progress * 0.5)); 358 | } 359 | 360 | vec2 sU(vec2 p1, vec2 p2) { 361 | return p1.x > p2.x ? p2 : p1; 362 | } 363 | 364 | float smin(float a, float b, float k) { 365 | float h = clamp(0.5 + 0.5 * (b - a) / k, 0.0, 1.0); 366 | return mix(b, a, h) - k * h * (1.0 - h); 367 | } 368 | 369 | vec2 doModel(vec3 p, vec2 beats) { 370 | float m = snoise3(p.xyz); 371 | float n = beats.x * m * 0.2 + beats.x; 372 | 373 | float r = n + 2.0 + sin(p.z + p.y * 2. + iChannelTime[0] * 2.) * 0.35; 374 | float d1 = r - length(p.xy - path(p.z)); 375 | float d2 = length(p - l2) - 0.125 * beats.y; 376 | float d3 = length(p - l1) - 0.125 * beats.x; 377 | 378 | d1 = -d1; 379 | d1 = smin(d1, length(p - l2) - 3.0, 4.05); 380 | d1 = smin(d1, length(p - l1) - 3.0, 4.05); 381 | d1 = -d1; 382 | 383 | return sU( 384 | vec2(d1, clamp(n, 0., 0.99)), 385 | sU( 386 | vec2(d2, 1.0), 387 | vec2(d3, 2.0) 388 | ) 389 | ); 390 | } 391 | 392 | float attenuate(float d) { 393 | return pow(clamp(1.0 - d / 20.0, 0.0, 1.0), 2.95); 394 | } 395 | 396 | void mainImage(out vec4 fragColor, in vec2 fragCoord) { 397 | vec3 color = bg; 398 | 399 | float rotation = 0.0; 400 | float height = 0.0; 401 | float dist = 5.9; 402 | 403 | vec2 uv = (fragCoord.xy - iResolution.xy * 0.5) / iResolution.y; 404 | 405 | ta.xy += path(ta.z); 406 | ro.xy += path(ro.z); 407 | l1.xy += path(l1.z); 408 | l2.xy += path(l2.z); 409 | 410 | float fov = PI_7_11 / 1.5; 411 | vec3 forward = normalize(ta - ro); 412 | vec3 right = normalize(vec3(forward.z, 0, -forward.x)); 413 | vec3 up = cross(forward, right); 414 | vec3 rd = normalize(forward + fov * uv.x * right + fov * uv.y * up); 415 | 416 | vec2 beats = vec2(beat1, beat2); 417 | 418 | vec2 t = calcRayIntersection_2_0(ro, rd, 40., 0.001, beats); 419 | if (t.x > -0.5) { 420 | vec3 surface; 421 | 422 | if (t.y == 1.0) { 423 | surface = c2 * 5. * beat2; 424 | } else 425 | if (t.y == 2.0) { 426 | surface = c1 * 4. * beat1; 427 | } else { 428 | vec3 pos = ro + rd * t.x; 429 | vec3 nor = calcNormal_4_1(pos, beats); 430 | vec3 mat = vec3(1.0, 0.8, 0.7); 431 | 432 | vec3 d1 = normalize(l1 - pos); 433 | vec3 d2 = normalize(l2 - pos); 434 | 435 | float attn1 = attenuate(length(l1 - pos)); 436 | float attn2 = attenuate(length(l2 - pos)); 437 | float diff1 = orenNayarDiffuse_3_2(d1, -rd, nor, 0.29, 2.5 * beat1); 438 | float diff2 = orenNayarDiffuse_3_2(d2, -rd, nor, 0.29, 3.5 * beat2); 439 | float spec1 = gaussianSpecular_5_3(d1, -rd, nor, 0.08) * beat1; 440 | float spec2 = gaussianSpecular_5_3(d2, -rd, nor, 0.08) * beat2; 441 | 442 | float glow = max(0., pow(clamp(2. * (t.y - 0.8), 0., 1.), 1.5) * 3.); 443 | 444 | 445 | surface = ( 446 | c3 * glow * max(0., dot(nor, normalize(ro - pos))) + 447 | (c1 * spec1 * attn1 + c2 * spec2 * attn2) + 448 | (c1 * diff1 * attn1 + c2 * diff2 * attn2) * mat 449 | ); 450 | } 451 | 452 | color = mix(surface, color, fogFactorExp2_6_10(t.x, 0.055)); 453 | } 454 | 455 | color = pow(color, vec3(0.75)); 456 | color.r = smoothstep(-0.1, 0.975, color.r); 457 | color *= vec3(1.0) - dot(uv, uv) * 0.65; 458 | 459 | fragColor.rgb = color; 460 | fragColor.a = 1.0; 461 | } 462 | --------------------------------------------------------------------------------