├── README.md ├── build ├── build.sh ├── compile ├── extra ├── Stats.js ├── dat.gui.min.js ├── jquery.js ├── loader.coffee ├── loader.js ├── matrix.coffee ├── matrix.js ├── shims.coffee ├── shims.js ├── vector.coffee └── vector.js ├── lib ├── audio.coffee ├── audio.js ├── camera.coffee ├── camera.js ├── events.coffee ├── events.js ├── geometry.js ├── hdr_clear.shader ├── keys.coffee ├── keys.js ├── loading.coffee ├── loading.js ├── rendernode.coffee ├── rendernode.js ├── schedule.coffee ├── schedule.js ├── webgl-nuke-vendor-prefix.coffee ├── webgl-nuke-vendor-prefix.js ├── webgl-texture-float-extension-shims.coffee ├── webgl-texture-float-extension-shims.js └── webgl │ ├── cube.coffee │ ├── cube.js │ ├── drawable.coffee │ ├── drawable.js │ ├── framebuffer.coffee │ ├── framebuffer.js │ ├── hexgrid.coffee │ ├── hexgrid.js │ ├── model.coffee │ ├── model.js │ ├── plane.coffee │ ├── plane.js │ ├── quad.coffee │ ├── quad.js │ ├── shader.coffee │ ├── shader.js │ ├── sphere.coffee │ ├── sphere.js │ ├── texture.coffee │ └── texture.js ├── license ├── bsd-license ├── gplv2-license ├── gplv3-license ├── mit-license └── readme ├── pack ├── src ├── albedo.shader ├── antialias │ ├── fxaa2_0.shader │ ├── fxaa3_11.shader │ ├── fxaa3_11.shaderlib │ ├── fxaa3_11_preprocessed.shaderlib │ ├── fxaa3_11_stripped.shaderlib │ ├── module.coffee │ └── module.js ├── application.coffee ├── application.js ├── blur │ ├── blur.shader │ ├── module.coffee │ └── module.js ├── composit.shader ├── deferred_model.coffee ├── deferred_model.js ├── depth │ ├── deferred_shadow_map.shader │ ├── depth.shader │ ├── lightmap_shadow_map.shader │ ├── module.coffee │ ├── module.js │ └── variance.shaderlib ├── direct_illumination.shader ├── dist3d.coffee ├── dist3d.js ├── global_illumination.shader ├── harmonics.shaderlib ├── illumination │ ├── bounce.shader │ ├── bounce_model.coffee │ ├── bounce_model.js │ ├── cube_diffuse.shader │ ├── cubeprobe.shader │ ├── debug.shader │ ├── diffusemap.jpg │ ├── harmonics.shader │ ├── module.coffee │ ├── module.js │ ├── shadow.shader │ ├── texmap.png │ └── transfer.shader ├── main.coffee ├── main.js ├── model │ ├── bump │ │ ├── 01_S_kap-bump.png │ │ ├── 01_s_kap-bump.png │ │ ├── 01_st_kp-bump.png │ │ ├── 01_stub-bump.png │ │ ├── flat.png │ │ ├── kamen-bump.png │ │ ├── level.png │ │ ├── reljef-bump.png │ │ ├── sp_luk-bump.png │ │ └── x01_st-bump.png │ ├── diffuse │ │ ├── 00_skap.jpg │ │ ├── 01_s_ba.jpg │ │ ├── 01_s_kap.jpg │ │ ├── 01_st_kp.jpg │ │ ├── 01_stub.jpg │ │ ├── kamen-stup.jpg │ │ ├── kamen.jpg │ │ ├── prozor1.jpg │ │ ├── reljef.jpg │ │ ├── sp_luk.jpg │ │ ├── vrata_ko.jpg │ │ ├── vrata_kr.jpg │ │ ├── white.png │ │ └── x01_st.jpg │ ├── lowres.vertices │ ├── materials.json │ ├── module.coffee │ ├── module.js │ ├── sponza.indices │ └── sponza.vertices ├── normaldepth.shader ├── presets │ ├── default.json │ └── new.json ├── ssao │ ├── module.coffee │ ├── module.js │ ├── moments.shader │ └── ssao.shader ├── sun.coffee ├── sun.js └── windows │ ├── module.coffee │ ├── module.js │ └── window.shader └── www ├── assets.pack ├── code.js └── index.html /README.md: -------------------------------------------------------------------------------- 1 | WebGL Deferred Irradiance Volumes 2 | ================================= 3 | 4 | An implementation of global illumination using deferred application of irradiance probes. 5 | 6 | Documentation 7 | ------------- 8 | 9 | Blog entry outlining the basic principle: http://codeflow.org/entries/2012/aug/25/webgl-deferred-irradiance-volumes/ 10 | 11 | Demo 12 | ---- 13 | 14 | Live Demo at: http://codeflow.org/webgl/deferred-irradiance-volumes/www/ 15 | 16 | License 17 | ------- 18 | 19 | Copyright (c) 2012, Florian Boesch http://codeflow.org/ 20 | 21 | WebGL Deferred Irradiance Volumes is licensed under any of the following licenses at your choosing: 22 | 23 | * MIT: see mit-license 24 | * GPL: see gplv*-license 25 | * BSD: see bsd-license 26 | -------------------------------------------------------------------------------- /build: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | ''' 4 | Copy this into your bin directory for convenient building 5 | ''' 6 | 7 | import os, subprocess 8 | 9 | here = os.getcwd() 10 | folders = here.split('/') 11 | 12 | while folders: 13 | path = '/'.join(folders) 14 | scriptpath = os.path.join(path, 'build.sh') 15 | if os.path.exists(scriptpath): 16 | os.chdir(path) 17 | subprocess.call(['bash', 'build.sh']) 18 | break 19 | folders.pop() 20 | -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | ./compile 2 | ./pack 3 | -------------------------------------------------------------------------------- /compile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, stat, subprocess 4 | from datetime import datetime 5 | 6 | here = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | message_count = 0 9 | def message(text): 10 | global message_count 11 | now = datetime.now().strftime('%H:%M:%S') 12 | print '[%04i %s] %s' % (message_count, now, text) 13 | message_count+=1 14 | 15 | def error(text): 16 | sys.stdout.write('\x1b[31m%s\x1b[39m' % text) 17 | sys.stdout.flush() 18 | 19 | def coffee_compile(path, bare): 20 | message('compiling: %s' % path) 21 | if bare: 22 | command = ['coffee', '--compile', '--bare', path] 23 | else: 24 | command = ['coffee', '--compile', path] 25 | 26 | process = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) 27 | out, err = process.communicate() 28 | if process.returncode: 29 | error(err) 30 | 31 | def get_mtime(name): 32 | return os.stat(name)[stat.ST_MTIME] 33 | 34 | def listfiles(folder, *exts): 35 | for path, dirs, names in os.walk(os.path.join(here, folder)): 36 | for name in names: 37 | ext = os.path.splitext(name)[-1].lstrip('.') 38 | if ext in exts: 39 | yield path, name, os.path.join(path, name) 40 | 41 | def check_files(folder, bare): 42 | for path, name, loc in listfiles(folder, 'coffee'): 43 | js_path = os.path.join(path, os.path.splitext(name)[0] + '.js') 44 | coffee_mtime = get_mtime(loc) 45 | if os.path.exists(js_path): 46 | js_mtime = get_mtime(js_path) 47 | if coffee_mtime > js_mtime: 48 | coffee_compile(loc, bare=bare) 49 | else: 50 | coffee_compile(loc, bare=bare) 51 | 52 | if __name__ == '__main__': 53 | check_files('src', bare=True) 54 | check_files('lib', bare=True) 55 | check_files('extra', bare=False) 56 | -------------------------------------------------------------------------------- /extra/Stats.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author mrdoob / http://mrdoob.com/ 3 | */ 4 | 5 | var Stats = function () { 6 | 7 | var startTime = Date.now(), prevTime = startTime; 8 | var ms = 0, msMin = 1000, msMax = 0; 9 | var fps = 0, fpsMin = 1000, fpsMax = 0; 10 | var frames = 0, mode = 0; 11 | 12 | var container = document.createElement( 'div' ); 13 | container.id = 'stats'; 14 | container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); 15 | container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; 16 | 17 | var fpsDiv = document.createElement( 'div' ); 18 | fpsDiv.id = 'fps'; 19 | fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; 20 | container.appendChild( fpsDiv ); 21 | 22 | var fpsText = document.createElement( 'div' ); 23 | fpsText.id = 'fpsText'; 24 | fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 25 | fpsText.innerHTML = 'FPS'; 26 | fpsDiv.appendChild( fpsText ); 27 | 28 | var fpsGraph = document.createElement( 'div' ); 29 | fpsGraph.id = 'fpsGraph'; 30 | fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; 31 | fpsDiv.appendChild( fpsGraph ); 32 | 33 | while ( fpsGraph.children.length < 74 ) { 34 | 35 | var bar = document.createElement( 'span' ); 36 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; 37 | fpsGraph.appendChild( bar ); 38 | 39 | } 40 | 41 | var msDiv = document.createElement( 'div' ); 42 | msDiv.id = 'ms'; 43 | msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; 44 | container.appendChild( msDiv ); 45 | 46 | var msText = document.createElement( 'div' ); 47 | msText.id = 'msText'; 48 | msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; 49 | msText.innerHTML = 'MS'; 50 | msDiv.appendChild( msText ); 51 | 52 | var msGraph = document.createElement( 'div' ); 53 | msGraph.id = 'msGraph'; 54 | msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; 55 | msDiv.appendChild( msGraph ); 56 | 57 | while ( msGraph.children.length < 74 ) { 58 | 59 | var bar = document.createElement( 'span' ); 60 | bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; 61 | msGraph.appendChild( bar ); 62 | 63 | } 64 | 65 | var setMode = function ( value ) { 66 | 67 | mode = value; 68 | 69 | switch ( mode ) { 70 | 71 | case 0: 72 | fpsDiv.style.display = 'block'; 73 | msDiv.style.display = 'none'; 74 | break; 75 | case 1: 76 | fpsDiv.style.display = 'none'; 77 | msDiv.style.display = 'block'; 78 | break; 79 | } 80 | 81 | } 82 | 83 | var updateGraph = function ( dom, value ) { 84 | 85 | var child = dom.appendChild( dom.firstChild ); 86 | child.style.height = value + 'px'; 87 | 88 | } 89 | 90 | return { 91 | 92 | domElement: container, 93 | 94 | setMode: setMode, 95 | 96 | begin: function () { 97 | 98 | startTime = Date.now(); 99 | 100 | }, 101 | 102 | end: function () { 103 | 104 | var time = Date.now(); 105 | 106 | ms = time - startTime; 107 | msMin = Math.min( msMin, ms ); 108 | msMax = Math.max( msMax, ms ); 109 | 110 | msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; 111 | updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); 112 | 113 | frames ++; 114 | 115 | if ( time > prevTime + 1000 ) { 116 | 117 | fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); 118 | fpsMin = Math.min( fpsMin, fps ); 119 | fpsMax = Math.max( fpsMax, fps ); 120 | 121 | fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; 122 | updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); 123 | 124 | prevTime = time; 125 | frames = 0; 126 | 127 | } 128 | 129 | return time; 130 | 131 | }, 132 | 133 | update: function () { 134 | 135 | startTime = this.end(); 136 | 137 | } 138 | 139 | } 140 | 141 | }; 142 | -------------------------------------------------------------------------------- /extra/loader.coffee: -------------------------------------------------------------------------------- 1 | fs = {} 2 | 3 | makeURL = (blob) -> 4 | return URL.createObjectURL blob 5 | 6 | makeBlob = (data, type) -> 7 | #builder = new BlobBuilder() 8 | #builder.append(data) 9 | #blob = builder.getBlob(type) 10 | #is recommended, but doesn't work in either Firefox or Chrome o_O 11 | blob = new Blob([data], type:type) 12 | return blob 13 | 14 | window.getURL = (data, mime) -> 15 | blob = makeBlob data, mime 16 | return makeURL blob 17 | 18 | resolvePath = (base, path) -> 19 | if path[0] == '/' 20 | return path 21 | else 22 | path = path.split '/' 23 | if base == '/' 24 | base = [''] 25 | else 26 | base = base.split '/' 27 | 28 | while base.length > 0 and path.length > 0 and path[0] == '..' 29 | base.pop() 30 | path.shift() 31 | 32 | if base.length == 0 || path.length == 0 || base[0] != '' 33 | throw "Invalid path: #{base.join '/'}/#{path.join '/'}" 34 | return "#{base.join('/')}/#{path.join('/')}" 35 | 36 | getJSON = (url, callback) -> 37 | request = new XMLHttpRequest() 38 | request.open 'GET', url, true 39 | request.onload = -> 40 | callback(JSON.parse(request.response)) 41 | request.send() 42 | 43 | getBuffer = (url, progress, callback) -> 44 | request = new XMLHttpRequest() 45 | request.open 'GET', url, true 46 | request.responseType = 'arraybuffer' 47 | request.onload = -> 48 | callback(request.response) 49 | request.onprogress = (event) -> 50 | if event.lengthComputable 51 | progress event.loaded/event.total 52 | request.send() 53 | 54 | isImage = (path) -> 55 | return path.match('\.jpg$|\.jpeg|\.gif$|\.png') 56 | 57 | window.loader = 58 | resolvePath: resolvePath 59 | main: -> 60 | main = @require 'main' 61 | if main.main 62 | main.main() 63 | else 64 | throw 'Main function is not defined in main module.' 65 | 66 | define: (path, code) -> 67 | dirname = path.split '/' 68 | dirname.pop() 69 | dirname = dirname.join '/' 70 | 71 | require = (modpath) -> 72 | abspath = resolvePath dirname, modpath 73 | node = fs["#{abspath}.js"] 74 | if not node 75 | node = fs["#{abspath}/module.js"] 76 | if not node then throw "Module not found: #{abspath}" 77 | if !node.value then node.create() 78 | return node.value 79 | 80 | get = (respath) -> 81 | abspath = resolvePath dirname, respath 82 | node = fs[abspath] 83 | if not node then throw "Resource not found: #{abspath}" 84 | return node 85 | 86 | get.exists = (respath) -> 87 | abspath = resolvePath dirname, respath 88 | node = fs[abspath] 89 | return node != undefined 90 | 91 | folder = get.folder = (folderpath) -> 92 | folder_abs = resolvePath dirname, folderpath 93 | return { 94 | path: folder_abs, 95 | name: folder_abs.split('/')[folder_abs.split('/').length-1] 96 | get: (respath) -> 97 | nodepath = resolvePath folder_abs, respath 98 | node = fs[nodepath] 99 | if not node then throw "Resource not found: #{nodepath}" 100 | return node 101 | 102 | exists: (respath) -> 103 | nodepath = resolvePath folder_abs, respath 104 | return fs[nodepath] != undefined 105 | 106 | listdir: (respath) -> 107 | if respath then nodepath = resolvepath folder_abs, respath 108 | else nodepath = folder_abs 109 | 110 | result = [] 111 | for name of fs 112 | match = name.match "#{folder_abs}/[a-zA-Z0-9-\.]+" 113 | if match 114 | match = match[0] 115 | if result.indexOf(match) == -1 116 | result.push match 117 | 118 | translated = [] 119 | for name in result 120 | if name.match /\.[a-z]+$/ 121 | translated.push name 122 | else 123 | translated.push folder name 124 | return translated 125 | } 126 | 127 | get.listdir = (respath, match) -> 128 | if respath 129 | abspath = resolvePath dirname, respath 130 | else 131 | abspath = dirname 132 | 133 | result = [] 134 | for name of fs 135 | if name.search(abspath) == 0 136 | if match 137 | if name.match match 138 | result.push name 139 | else 140 | result.push name 141 | return result 142 | 143 | fs[path] = 144 | path: path 145 | type: 'code' 146 | data: code 147 | create: -> 148 | @value = {} 149 | retval = code @value, require, get 150 | if retval 151 | @value = retval 152 | 153 | require: (modpath) -> 154 | abspath = resolvePath '/', modpath 155 | node = fs["#{abspath}.js"] 156 | if not node 157 | node = fs["#{abspath}/module.js"] 158 | 159 | if not node then throw "Module not found: #{abspath}" 160 | if !node.value then node.create() 161 | return node.value 162 | 163 | loadPack: ({url, progress, loaded}) -> 164 | files = {} 165 | hooks = @hooks 166 | getBuffer url, ((factor) -> if progress then progress(factor*0.5, 'network')), (data) -> 167 | decoding = 0 168 | decoded = 0 169 | 170 | doLoad = (name, info) -> 171 | if typeof info == 'object' and info.offset != undefined and info.size != undefined 172 | storage = new ArrayBuffer info.size 173 | dst = new Uint8Array storage 174 | src = new Uint8Array data, 8+length+info.offset, info.size 175 | dst.set src 176 | dst = dst.buffer 177 | 178 | if hooks 179 | for matcher, decode of hooks 180 | if name.match(matcher) 181 | decoding += 1 182 | decode name, dst, (result) -> 183 | decoded += 1 184 | files[name] = result 185 | if progress then progress(0.5+(decoded/decoding)*0.5, 'decode') 186 | if decoding == decoded and loaded then loaded(files) 187 | return 188 | files[name] = dst 189 | else 190 | if hooks 191 | for matcher, decode of hooks 192 | if name.match(matcher) 193 | decode name, info, (result) -> 194 | files[name] = result 195 | return 196 | files[name] = info 197 | 198 | length = new Uint32Array(data, 4, 1)[0] 199 | metadata = new Uint8Array(data, 8, length) 200 | result = '' 201 | for i in [0...length] 202 | result += String.fromCharCode(metadata[i]) 203 | result = JSON.parse(result) 204 | 205 | for name, info of result 206 | doLoad name, info, data 207 | 208 | if decoding == decoded and loaded then loaded(files) 209 | 210 | hooks: (@hooks) -> return @ 211 | 212 | mount: ({url, mountpoint, progress, loaded}) -> 213 | mountpoint ?= '/' 214 | @loadPack url: url, progress: progress, loaded: (data) -> 215 | for name, value of data 216 | fs[name] = value 217 | loaded(data, fs) 218 | -------------------------------------------------------------------------------- /extra/shims.coffee: -------------------------------------------------------------------------------- 1 | if window.performance 2 | if window.performance.now 3 | now = -> window.performance.now() 4 | else if window.performance.webkitNow 5 | now = -> window.performance.webkitNow() 6 | else if window.performance.mozNow 7 | now = -> window.performance.mozNow() 8 | else if window.performance.oNow 9 | now = -> window.performance.oNow() 10 | else 11 | now = -> Date.now() 12 | else 13 | now = -> Date.now() 14 | 15 | start = now() 16 | window.gettime = -> (now() - start)/1000 17 | 18 | if not window.requestAnimationFrame 19 | if window.webkitRequestAnimationFrame 20 | window.requestAnimationFrame = window.webkitRequestAnimationFrame 21 | else if window.mozRequestAnimationFrame 22 | window.requestAnimationFrame = window.mozRequestAnimationFrame 23 | else if window.oRequestAnimationFrame 24 | window.requestAnimationFrame = window.oRequestAnimationFrame 25 | else 26 | window.requestAnimationFrame = (fun) -> 27 | setTimeout(fun, 1000/30) 28 | 29 | window.URL = window.URL or window.mozURL or window.webkitURL or window.oURL 30 | window.BlobBuilder = window.BlobBuilder or window.MozBlobBuilder or window.WebKitBlobBuilder or window.OBlobBuilder 31 | 32 | log_count = 0 33 | window.console.logN = (n) -> 34 | if log_count < n 35 | log_count += 1 36 | args = [].slice.call(arguments, 1) 37 | console.log.apply console, args 38 | -------------------------------------------------------------------------------- /extra/shims.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var log_count, now, start; 4 | 5 | if (window.performance) { 6 | if (window.performance.now) { 7 | now = function() { 8 | return window.performance.now(); 9 | }; 10 | } else if (window.performance.webkitNow) { 11 | now = function() { 12 | return window.performance.webkitNow(); 13 | }; 14 | } else if (window.performance.mozNow) { 15 | now = function() { 16 | return window.performance.mozNow(); 17 | }; 18 | } else if (window.performance.oNow) { 19 | now = function() { 20 | return window.performance.oNow(); 21 | }; 22 | } else { 23 | now = function() { 24 | return Date.now(); 25 | }; 26 | } 27 | } else { 28 | now = function() { 29 | return Date.now(); 30 | }; 31 | } 32 | 33 | start = now(); 34 | 35 | window.gettime = function() { 36 | return (now() - start) / 1000; 37 | }; 38 | 39 | if (!window.requestAnimationFrame) { 40 | if (window.webkitRequestAnimationFrame) { 41 | window.requestAnimationFrame = window.webkitRequestAnimationFrame; 42 | } else if (window.mozRequestAnimationFrame) { 43 | window.requestAnimationFrame = window.mozRequestAnimationFrame; 44 | } else if (window.oRequestAnimationFrame) { 45 | window.requestAnimationFrame = window.oRequestAnimationFrame; 46 | } else { 47 | window.requestAnimationFrame = function(fun) { 48 | return setTimeout(fun, 1000 / 30); 49 | }; 50 | } 51 | } 52 | 53 | window.URL = window.URL || window.mozURL || window.webkitURL || window.oURL; 54 | 55 | window.BlobBuilder = window.BlobBuilder || window.MozBlobBuilder || window.WebKitBlobBuilder || window.OBlobBuilder; 56 | 57 | log_count = 0; 58 | 59 | window.console.logN = function(n) { 60 | var args; 61 | if (log_count < n) { 62 | log_count += 1; 63 | args = [].slice.call(arguments, 1); 64 | return console.log.apply(console, args); 65 | } 66 | }; 67 | 68 | }).call(this); 69 | -------------------------------------------------------------------------------- /extra/vector.coffee: -------------------------------------------------------------------------------- 1 | window.Vec3 = class Vec3 2 | @property 'x' 3 | get: -> @data[0] 4 | set: (val) -> @data[0] = val 5 | @property 'y' 6 | get: -> @data[1] 7 | set: (val) -> @data[1] = val 8 | @property 'z' 9 | get: -> @data[2] 10 | set: (val) -> @data[2] = val 11 | 12 | @property 'length' 13 | get: -> Math.sqrt(@x*@x + @y*@y + @z*@z) 14 | 15 | constructor: (@data) -> 16 | @data ?= new Float32Array 3 17 | 18 | sub: (other, dst=@) -> 19 | dst.x = @x - other.x 20 | dst.y = @y - other.y 21 | dst.z = @z - other.z 22 | return dst 23 | 24 | add: (other, dst=@) -> 25 | dst.x = @x + other.x 26 | dst.y = @y + other.y 27 | dst.z = @z + other.z 28 | return dst 29 | 30 | addVal3: (x, y, z, dst=@) -> 31 | dst.x = @x + x 32 | dst.y = @y + y 33 | dst.z = @z + z 34 | return dst 35 | 36 | mul: (scalar, dst=@) -> 37 | dst.x = @x * scalar 38 | dst.y = @y * scalar 39 | dst.z = @z * scalar 40 | return dst 41 | 42 | div: (scalar, dst=@) -> 43 | dst.x = @x / scalar 44 | dst.y = @y / scalar 45 | dst.z = @z / scalar 46 | return dst 47 | 48 | divVal3: (x, y, z, dst=@) -> 49 | dst.x = @x/x 50 | dst.y = @y/y 51 | dst.z = @z/z 52 | return dst 53 | 54 | dot: (other) -> 55 | return @x*other.x + @y*other.y + @z*other.z 56 | 57 | normalize: (dst=@) -> 58 | l = @length 59 | if l > 0 then @mul 1/@length, dst 60 | return dst 61 | 62 | set: (x,y,z) -> 63 | @x = x 64 | @y = y 65 | @z = z 66 | return @ 67 | 68 | window.Vec4 = class Vec4 69 | @property 'x' 70 | get: -> @data[0] 71 | set: (val) -> @data[0] = val 72 | @property 'y' 73 | get: -> @data[1] 74 | set: (val) -> @data[1] = val 75 | @property 'z' 76 | get: -> @data[2] 77 | set: (val) -> @data[2] = val 78 | @property 'w' 79 | get: -> @data[3] 80 | set: (val) -> @data[3] = val 81 | 82 | constructor: (@data) -> 83 | @data ?= new Float32Array 4 84 | 85 | sub: (other, dst=@) -> 86 | dst.x = @x - other.x 87 | dst.y = @y - other.y 88 | dst.z = @z - other.z 89 | dst.w = @w - other.w 90 | 91 | return dst 92 | 93 | dot: (other) -> 94 | return @x*other.x + @y*other.y + @z*other.z + @w*other.w 95 | 96 | toVec3: (dst) -> 97 | dst ?= new Vec3() 98 | dst.x = @x 99 | dst.y = @y 100 | dst.z = @z 101 | 102 | return dst 103 | -------------------------------------------------------------------------------- /extra/vector.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | (function() { 3 | var Vec3, Vec4; 4 | 5 | window.Vec3 = Vec3 = (function() { 6 | 7 | Vec3.property('x', { 8 | get: function() { 9 | return this.data[0]; 10 | }, 11 | set: function(val) { 12 | return this.data[0] = val; 13 | } 14 | }); 15 | 16 | Vec3.property('y', { 17 | get: function() { 18 | return this.data[1]; 19 | }, 20 | set: function(val) { 21 | return this.data[1] = val; 22 | } 23 | }); 24 | 25 | Vec3.property('z', { 26 | get: function() { 27 | return this.data[2]; 28 | }, 29 | set: function(val) { 30 | return this.data[2] = val; 31 | } 32 | }); 33 | 34 | Vec3.property('length', { 35 | get: function() { 36 | return Math.sqrt(this.x * this.x + this.y * this.y + this.z * this.z); 37 | } 38 | }); 39 | 40 | function Vec3(data) { 41 | var _ref; 42 | this.data = data; 43 | if ((_ref = this.data) == null) { 44 | this.data = new Float32Array(3); 45 | } 46 | } 47 | 48 | Vec3.prototype.sub = function(other, dst) { 49 | if (dst == null) { 50 | dst = this; 51 | } 52 | dst.x = this.x - other.x; 53 | dst.y = this.y - other.y; 54 | dst.z = this.z - other.z; 55 | return dst; 56 | }; 57 | 58 | Vec3.prototype.add = function(other, dst) { 59 | if (dst == null) { 60 | dst = this; 61 | } 62 | dst.x = this.x + other.x; 63 | dst.y = this.y + other.y; 64 | dst.z = this.z + other.z; 65 | return dst; 66 | }; 67 | 68 | Vec3.prototype.addVal3 = function(x, y, z, dst) { 69 | if (dst == null) { 70 | dst = this; 71 | } 72 | dst.x = this.x + x; 73 | dst.y = this.y + y; 74 | dst.z = this.z + z; 75 | return dst; 76 | }; 77 | 78 | Vec3.prototype.mul = function(scalar, dst) { 79 | if (dst == null) { 80 | dst = this; 81 | } 82 | dst.x = this.x * scalar; 83 | dst.y = this.y * scalar; 84 | dst.z = this.z * scalar; 85 | return dst; 86 | }; 87 | 88 | Vec3.prototype.div = function(scalar, dst) { 89 | if (dst == null) { 90 | dst = this; 91 | } 92 | dst.x = this.x / scalar; 93 | dst.y = this.y / scalar; 94 | dst.z = this.z / scalar; 95 | return dst; 96 | }; 97 | 98 | Vec3.prototype.divVal3 = function(x, y, z, dst) { 99 | if (dst == null) { 100 | dst = this; 101 | } 102 | dst.x = this.x / x; 103 | dst.y = this.y / y; 104 | dst.z = this.z / z; 105 | return dst; 106 | }; 107 | 108 | Vec3.prototype.dot = function(other) { 109 | return this.x * other.x + this.y * other.y + this.z * other.z; 110 | }; 111 | 112 | Vec3.prototype.normalize = function(dst) { 113 | var l; 114 | if (dst == null) { 115 | dst = this; 116 | } 117 | l = this.length; 118 | if (l > 0) { 119 | this.mul(1 / this.length, dst); 120 | } 121 | return dst; 122 | }; 123 | 124 | Vec3.prototype.set = function(x, y, z) { 125 | this.x = x; 126 | this.y = y; 127 | this.z = z; 128 | return this; 129 | }; 130 | 131 | return Vec3; 132 | 133 | })(); 134 | 135 | window.Vec4 = Vec4 = (function() { 136 | 137 | Vec4.property('x', { 138 | get: function() { 139 | return this.data[0]; 140 | }, 141 | set: function(val) { 142 | return this.data[0] = val; 143 | } 144 | }); 145 | 146 | Vec4.property('y', { 147 | get: function() { 148 | return this.data[1]; 149 | }, 150 | set: function(val) { 151 | return this.data[1] = val; 152 | } 153 | }); 154 | 155 | Vec4.property('z', { 156 | get: function() { 157 | return this.data[2]; 158 | }, 159 | set: function(val) { 160 | return this.data[2] = val; 161 | } 162 | }); 163 | 164 | Vec4.property('w', { 165 | get: function() { 166 | return this.data[3]; 167 | }, 168 | set: function(val) { 169 | return this.data[3] = val; 170 | } 171 | }); 172 | 173 | function Vec4(data) { 174 | var _ref; 175 | this.data = data; 176 | if ((_ref = this.data) == null) { 177 | this.data = new Float32Array(4); 178 | } 179 | } 180 | 181 | Vec4.prototype.sub = function(other, dst) { 182 | if (dst == null) { 183 | dst = this; 184 | } 185 | dst.x = this.x - other.x; 186 | dst.y = this.y - other.y; 187 | dst.z = this.z - other.z; 188 | dst.w = this.w - other.w; 189 | return dst; 190 | }; 191 | 192 | Vec4.prototype.dot = function(other) { 193 | return this.x * other.x + this.y * other.y + this.z * other.z + this.w * other.w; 194 | }; 195 | 196 | Vec4.prototype.toVec3 = function(dst) { 197 | if (dst == null) { 198 | dst = new Vec3(); 199 | } 200 | dst.x = this.x; 201 | dst.y = this.y; 202 | dst.z = this.z; 203 | return dst; 204 | }; 205 | 206 | return Vec4; 207 | 208 | })(); 209 | 210 | }).call(this); 211 | -------------------------------------------------------------------------------- /lib/audio.coffee: -------------------------------------------------------------------------------- 1 | class Backend 2 | constructor: -> 3 | @loading = 0 4 | @handlers = [] 5 | 6 | loaded: -> 7 | if @loading == 0 8 | for handler in @handlers 9 | if handler.event == 'loaded' then handler.callback.apply(handler) 10 | return 11 | 12 | bind: (event, callback) -> 13 | handler = 14 | event: event 15 | callback: callback 16 | @handlers.push handler 17 | return handler 18 | 19 | unbind: (handler) -> 20 | index = @handlers.indexOf handler 21 | if index >= 0 then @handlers.splice index, 1 22 | 23 | class HTMLAudio extends Backend 24 | @available = ( 25 | (window.Audio != undefined) and 26 | (window.URL != undefined) and 27 | (window.BlobBuilder != undefined) 28 | ) 29 | 30 | class Sample 31 | constructor: (@backend, data) -> 32 | @backend.loading += 1 33 | @url = blob.pack data, 'audio/ogg' 34 | 35 | play: (looping) -> 36 | voice = @backend.getFree() 37 | voice.play(@url, looping) if voice 38 | 39 | class Voice 40 | constructor: (backend, @id) -> 41 | self = @ 42 | @audio = new Audio() 43 | @audio.onended = -> 44 | backend.ended(self) 45 | 46 | play: (url) -> 47 | @audio.src = url 48 | @audio.play() 49 | 50 | constructor: -> 51 | @free = {} 52 | @playing = {} 53 | 54 | for id in [0...20] 55 | @free[id] = new Voice(@, id) 56 | 57 | setInterval @check, 100 58 | 59 | check: => 60 | #for id, voice of @playing 61 | #console.log voice.audio.duration 62 | 63 | getFree: -> 64 | for id, voice of @free 65 | delete @free[id] 66 | @playing[id] = voice 67 | return voice 68 | 69 | ended: (voice) -> 70 | @free[voice.id] = voice 71 | delete @playing[voice.id] 72 | 73 | createSample: (data) -> 74 | @start_time = gettime() 75 | return new Sample @, data 76 | 77 | class WebAudio extends Backend 78 | @available = window.webkitAudioContext != undefined 79 | 80 | constructor: -> 81 | super() 82 | @ctx = new webkitAudioContext() 83 | 84 | play: (buffer, looping=false) -> 85 | source = @ctx.createBufferSource() 86 | source.buffer = buffer 87 | source.loop = looping 88 | source.connect(@ctx.destination) 89 | source.noteOn(@ctx.currentTime) 90 | 91 | decode: (data, callback) -> 92 | @ctx.decodeAudioData data, (buffer) -> 93 | callback(buffer) 94 | 95 | if WebAudio.available 96 | backend = new WebAudio() 97 | 98 | exports.decode = (data, callback) -> 99 | backend.decode data, callback 100 | 101 | exports.play = (buffer) -> 102 | backend.play buffer 103 | else 104 | exports.decode = (data, callback) -> 105 | exports.play = (buffer) -> 106 | -------------------------------------------------------------------------------- /lib/audio.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Backend, HTMLAudio, WebAudio, backend, 3 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 4 | __hasProp = {}.hasOwnProperty, 5 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 6 | 7 | Backend = (function() { 8 | 9 | function Backend() { 10 | this.loading = 0; 11 | this.handlers = []; 12 | } 13 | 14 | Backend.prototype.loaded = function() { 15 | var handler, _i, _len, _ref; 16 | if (this.loading === 0) { 17 | _ref = this.handlers; 18 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 19 | handler = _ref[_i]; 20 | if (handler.event === 'loaded') { 21 | handler.callback.apply(handler); 22 | } 23 | } 24 | } 25 | }; 26 | 27 | Backend.prototype.bind = function(event, callback) { 28 | var handler; 29 | handler = { 30 | event: event, 31 | callback: callback 32 | }; 33 | this.handlers.push(handler); 34 | return handler; 35 | }; 36 | 37 | Backend.prototype.unbind = function(handler) { 38 | var index; 39 | index = this.handlers.indexOf(handler); 40 | if (index >= 0) { 41 | return this.handlers.splice(index, 1); 42 | } 43 | }; 44 | 45 | return Backend; 46 | 47 | })(); 48 | 49 | HTMLAudio = (function(_super) { 50 | var Sample, Voice; 51 | 52 | __extends(HTMLAudio, _super); 53 | 54 | HTMLAudio.available = (window.Audio !== void 0) && (window.URL !== void 0) && (window.BlobBuilder !== void 0); 55 | 56 | Sample = (function() { 57 | 58 | function Sample(backend, data) { 59 | this.backend = backend; 60 | this.backend.loading += 1; 61 | this.url = blob.pack(data, 'audio/ogg'); 62 | } 63 | 64 | Sample.prototype.play = function(looping) { 65 | var voice; 66 | voice = this.backend.getFree(); 67 | if (voice) { 68 | return voice.play(this.url, looping); 69 | } 70 | }; 71 | 72 | return Sample; 73 | 74 | })(); 75 | 76 | Voice = (function() { 77 | 78 | function Voice(backend, id) { 79 | var self; 80 | this.id = id; 81 | self = this; 82 | this.audio = new Audio(); 83 | this.audio.onended = function() { 84 | return backend.ended(self); 85 | }; 86 | } 87 | 88 | Voice.prototype.play = function(url) { 89 | this.audio.src = url; 90 | return this.audio.play(); 91 | }; 92 | 93 | return Voice; 94 | 95 | })(); 96 | 97 | function HTMLAudio() { 98 | this.check = __bind(this.check, this); 99 | 100 | var id, _i; 101 | this.free = {}; 102 | this.playing = {}; 103 | for (id = _i = 0; _i < 20; id = ++_i) { 104 | this.free[id] = new Voice(this, id); 105 | } 106 | setInterval(this.check, 100); 107 | } 108 | 109 | HTMLAudio.prototype.check = function() {}; 110 | 111 | HTMLAudio.prototype.getFree = function() { 112 | var id, voice, _ref; 113 | _ref = this.free; 114 | for (id in _ref) { 115 | voice = _ref[id]; 116 | delete this.free[id]; 117 | this.playing[id] = voice; 118 | return voice; 119 | } 120 | }; 121 | 122 | HTMLAudio.prototype.ended = function(voice) { 123 | this.free[voice.id] = voice; 124 | return delete this.playing[voice.id]; 125 | }; 126 | 127 | HTMLAudio.prototype.createSample = function(data) { 128 | this.start_time = gettime(); 129 | return new Sample(this, data); 130 | }; 131 | 132 | return HTMLAudio; 133 | 134 | })(Backend); 135 | 136 | WebAudio = (function(_super) { 137 | 138 | __extends(WebAudio, _super); 139 | 140 | WebAudio.available = window.webkitAudioContext !== void 0; 141 | 142 | function WebAudio() { 143 | WebAudio.__super__.constructor.call(this); 144 | this.ctx = new webkitAudioContext(); 145 | } 146 | 147 | WebAudio.prototype.play = function(buffer, looping) { 148 | var source; 149 | if (looping == null) { 150 | looping = false; 151 | } 152 | source = this.ctx.createBufferSource(); 153 | source.buffer = buffer; 154 | source.loop = looping; 155 | source.connect(this.ctx.destination); 156 | return source.noteOn(this.ctx.currentTime); 157 | }; 158 | 159 | WebAudio.prototype.decode = function(data, callback) { 160 | return this.ctx.decodeAudioData(data, function(buffer) { 161 | return callback(buffer); 162 | }); 163 | }; 164 | 165 | return WebAudio; 166 | 167 | })(Backend); 168 | 169 | if (WebAudio.available) { 170 | backend = new WebAudio(); 171 | exports.decode = function(data, callback) { 172 | return backend.decode(data, callback); 173 | }; 174 | exports.play = function(buffer) { 175 | return backend.play(buffer); 176 | }; 177 | } else { 178 | exports.decode = function(data, callback) {}; 179 | exports.play = function(buffer) {}; 180 | } 181 | -------------------------------------------------------------------------------- /lib/events.coffee: -------------------------------------------------------------------------------- 1 | return class Emitter 2 | constructor: -> 3 | @handlers = {} 4 | 5 | on: (name, callback) -> 6 | handlers = @handlers[name] 7 | if handlers == undefined 8 | handlers = @handlers[name] = [] 9 | handlers.push callback 10 | return @ 11 | 12 | trigger: (name, a1, a2, a3, a4, a5, a6) -> 13 | handlers = @handlers[name] 14 | if handlers != undefined 15 | for handler in handlers 16 | handler(a1, a2, a3, a4, a5, a6) 17 | return @ 18 | 19 | -------------------------------------------------------------------------------- /lib/events.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Emitter; 3 | 4 | return Emitter = (function() { 5 | 6 | function Emitter() { 7 | this.handlers = {}; 8 | } 9 | 10 | Emitter.prototype.on = function(name, callback) { 11 | var handlers; 12 | handlers = this.handlers[name]; 13 | if (handlers === void 0) { 14 | handlers = this.handlers[name] = []; 15 | } 16 | handlers.push(callback); 17 | return this; 18 | }; 19 | 20 | Emitter.prototype.trigger = function(name, a1, a2, a3, a4, a5, a6) { 21 | var handler, handlers, _i, _len; 22 | handlers = this.handlers[name]; 23 | if (handlers !== void 0) { 24 | for (_i = 0, _len = handlers.length; _i < _len; _i++) { 25 | handler = handlers[_i]; 26 | handler(a1, a2, a3, a4, a5, a6); 27 | } 28 | } 29 | return this; 30 | }; 31 | 32 | return Emitter; 33 | 34 | })(); 35 | -------------------------------------------------------------------------------- /lib/geometry.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | exports.AABB = (function() { 4 | 5 | function AABB(xmin, xmax, ymin, ymax, zmin, zmax) { 6 | this.xmin = xmin; 7 | this.xmax = xmax; 8 | this.ymin = ymin; 9 | this.ymax = ymax; 10 | this.zmin = zmin; 11 | this.zmax = zmax; 12 | } 13 | 14 | AABB.prototype.ray_intersect = function(ray) { 15 | var d, inv_x, inv_y, inv_z, o, tmax, tmin, ymax, ymin, zmax, zmin, _ref, _ref1, _ref2; 16 | o = ray.origin; 17 | d = ray.direction; 18 | inv_x = 1.0 / d.x; 19 | tmin = (this.xmin - o.x) * inv_x; 20 | tmax = (this.xmax - o.x) * inv_x; 21 | if (inv_x < 0) { 22 | _ref = [tmax, tmin], tmin = _ref[0], tmax = _ref[1]; 23 | } 24 | inv_y = 1.0 / d.y; 25 | ymin = (this.ymin - o.y) * inv_y; 26 | ymax = (this.ymax - o.y) * inv_y; 27 | if (inv_y < 0) { 28 | _ref1 = [ymax, ymin], ymin = _ref1[0], ymax = _ref1[1]; 29 | } 30 | if (tmin > ymax || ymin > tmax) { 31 | return null; 32 | } 33 | if (ymin > tmin) { 34 | tmin = ymin; 35 | } 36 | if (ymax < tmax) { 37 | tmax = ymax; 38 | } 39 | inv_z = 1.0 / d.z; 40 | zmin = (this.zmin - o.z) * inv_z; 41 | zmax = (this.zmax - o.z) * inv_z; 42 | if (inv_z < 0) { 43 | _ref2 = [zmax, zmin], zmin = _ref2[0], zmax = _ref2[1]; 44 | } 45 | if (tmin > zmax || zmin > tmax) { 46 | return null; 47 | } 48 | if (zmin > tmin) { 49 | tmin = zmin; 50 | } 51 | if (zmax < tmax) { 52 | tmax = zmax; 53 | } 54 | return [tmin, tmax]; 55 | }; 56 | 57 | return AABB; 58 | 59 | })(); 60 | 61 | exports.Ray = (function() { 62 | 63 | function Ray(origin, direction) { 64 | var _ref, _ref1; 65 | this.origin = origin; 66 | this.direction = direction; 67 | if ((_ref = this.origin) == null) { 68 | this.origin = new Vec4(); 69 | } 70 | if ((_ref1 = this.direction) == null) { 71 | this.direction = new Vec4(); 72 | } 73 | } 74 | 75 | Ray.prototype.interpolate = function(interval, vector) { 76 | var d, o, v; 77 | if (vector == null) { 78 | vector = new Vec4(); 79 | } 80 | o = this.origin; 81 | d = this.direction; 82 | v = vector; 83 | v.x = o.x + d.x * interval; 84 | v.y = o.y + d.y * interval; 85 | v.z = o.z + d.z * interval; 86 | v.w = o.w + d.w * interval; 87 | return vector; 88 | }; 89 | 90 | Ray.prototype.ray_nearest = function(ray) { 91 | var U, V, W, a, b, c, d, det, e, s, t; 92 | W = this.origin.sub(ray.origin, new Vec4); 93 | U = this.direction; 94 | V = ray.direction; 95 | a = U.dot(U); 96 | b = U.dot(V); 97 | c = V.dot(V); 98 | d = U.dot(W); 99 | e = V.dot(W); 100 | det = a * c - b * b; 101 | if (det === 0) { 102 | return null; 103 | } 104 | s = (b * e - c * d) / det; 105 | t = (a * e - b * d) / det; 106 | return [s, t]; 107 | }; 108 | 109 | Ray.prototype.point_distance = function(point) { 110 | var W, s; 111 | W = point.sub(this.origin, new Vec4); 112 | s = W.dot(this.direction) / this.direction.dot(this.direction); 113 | W.x -= this.direction.x * s; 114 | W.y -= this.direction.y * s; 115 | W.z -= this.direction.z * s; 116 | return Math.sqrt(W.dot(W)); 117 | }; 118 | 119 | return Ray; 120 | 121 | })(); 122 | 123 | exports.get_mouseray = function(x, y, inv_proj, inv_view, ray) { 124 | if (ray == null) { 125 | ray = new exports.Ray; 126 | } 127 | inv_proj.mulVal4(x, y, -1, 1, ray.direction); 128 | inv_view.mulVec3(ray.direction); 129 | ray.direction.w = 0; 130 | inv_view.mulVal4(0, 0, 0, 1, ray.origin); 131 | return ray; 132 | }; 133 | -------------------------------------------------------------------------------- /lib/hdr_clear.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | void main(){ 4 | gl_Position = vec4(position, 0.0, 1.0); 5 | } 6 | 7 | fragment: 8 | uniform vec4 clear_color; 9 | void main(){ 10 | gl_FragColor = clear_color; 11 | } 12 | -------------------------------------------------------------------------------- /lib/keys.coffee: -------------------------------------------------------------------------------- 1 | keymap = ({ 2 | 87: 'w', 3 | 65: 'a', 4 | 83: 's', 5 | 68: 'd', 6 | 81: 'q', 7 | 69: 'e', 8 | 37: 'left', 9 | 39: 'right', 10 | 38: 'up', 11 | 40: 'down', 12 | 13: 'enter', 13 | 27: 'esc', 14 | 32: 'space', 15 | 8: 'backspace', 16 | 16: 'shift', 17 | 17: 'ctrl', 18 | 18: 'alt', 19 | 91: 'start', 20 | 0: 'altc', 21 | 20: 'caps', 22 | 9: 'tab', 23 | 49: 'key1', 24 | 50: 'key2', 25 | 51: 'key3', 26 | 52: 'key4' 27 | }) 28 | 29 | key_handlers = {} 30 | 31 | keys = { 32 | press: (name, callback) -> 33 | handlers = key_handlers[name] = key_handlers[name] or [] 34 | handlers.push(callback) 35 | } 36 | 37 | $(document).keydown (event) -> 38 | if event.target == document.body 39 | name = keymap[event.which] 40 | keys[name] = true 41 | handlers = key_handlers[name] 42 | if handlers 43 | for handler in handlers 44 | handler() 45 | 46 | $(document).keyup (event) -> 47 | name = keymap[event.which] 48 | keys[name] = false 49 | 50 | return keys 51 | -------------------------------------------------------------------------------- /lib/keys.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var key_handlers, keymap, keys; 3 | 4 | keymap = { 5 | 87: 'w', 6 | 65: 'a', 7 | 83: 's', 8 | 68: 'd', 9 | 81: 'q', 10 | 69: 'e', 11 | 37: 'left', 12 | 39: 'right', 13 | 38: 'up', 14 | 40: 'down', 15 | 13: 'enter', 16 | 27: 'esc', 17 | 32: 'space', 18 | 8: 'backspace', 19 | 16: 'shift', 20 | 17: 'ctrl', 21 | 18: 'alt', 22 | 91: 'start', 23 | 0: 'altc', 24 | 20: 'caps', 25 | 9: 'tab', 26 | 49: 'key1', 27 | 50: 'key2', 28 | 51: 'key3', 29 | 52: 'key4' 30 | }; 31 | 32 | key_handlers = {}; 33 | 34 | keys = { 35 | press: function(name, callback) { 36 | var handlers; 37 | handlers = key_handlers[name] = key_handlers[name] || []; 38 | return handlers.push(callback); 39 | } 40 | }; 41 | 42 | $(document).keydown(function(event) { 43 | var handler, handlers, name, _i, _len, _results; 44 | if (event.target === document.body) { 45 | name = keymap[event.which]; 46 | keys[name] = true; 47 | handlers = key_handlers[name]; 48 | if (handlers) { 49 | _results = []; 50 | for (_i = 0, _len = handlers.length; _i < _len; _i++) { 51 | handler = handlers[_i]; 52 | _results.push(handler()); 53 | } 54 | return _results; 55 | } 56 | } 57 | }); 58 | 59 | $(document).keyup(function(event) { 60 | var name; 61 | name = keymap[event.which]; 62 | return keys[name] = false; 63 | }); 64 | 65 | return keys; 66 | -------------------------------------------------------------------------------- /lib/loading.coffee: -------------------------------------------------------------------------------- 1 | ui = $('#ui') 2 | hidden = true 3 | 4 | container = $('
').css 5 | position: 'absolute' 6 | top: '50%' 7 | left: '50%' 8 | width: 200 9 | height: 40 10 | marginLeft: -100 11 | marginTop: -20 12 | 13 | label = $('
').appendTo(container).css 14 | position: 'absolute' 15 | top: 0 16 | left: 0 17 | width: 200 18 | height: 20 19 | textAlign: 'center' 20 | color: 'white' 21 | 22 | loading = $('
').appendTo(container).css 23 | position: 'absolute' 24 | top: 20 25 | left: 0 26 | width: 200 27 | height: 20 28 | border: '1px solid white' 29 | 30 | bar = null 31 | makeBar = -> 32 | loading.empty() 33 | bar = $('
').appendTo(loading).css 34 | position: 'absolute' 35 | top: 0 36 | left: 0 37 | width: 0 38 | height: 20 39 | backgroundColor: 'white' 40 | '-webkit-transition': 'width 0.7s' 41 | 42 | exports.show = (text) -> 43 | label.text text 44 | makeBar() 45 | container 46 | .fadeIn('slow') 47 | .appendTo(ui) 48 | 49 | exports.hide = -> 50 | container.fadeOut 'slow', -> 51 | container.detach() 52 | 53 | exports.progress = (factor) -> 54 | bar.width(factor*200) 55 | -------------------------------------------------------------------------------- /lib/loading.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var bar, container, hidden, label, loading, makeBar, ui; 3 | 4 | ui = $('#ui'); 5 | 6 | hidden = true; 7 | 8 | container = $('
').css({ 9 | position: 'absolute', 10 | top: '50%', 11 | left: '50%', 12 | width: 200, 13 | height: 40, 14 | marginLeft: -100, 15 | marginTop: -20 16 | }); 17 | 18 | label = $('
').appendTo(container).css({ 19 | position: 'absolute', 20 | top: 0, 21 | left: 0, 22 | width: 200, 23 | height: 20, 24 | textAlign: 'center', 25 | color: 'white' 26 | }); 27 | 28 | loading = $('
').appendTo(container).css({ 29 | position: 'absolute', 30 | top: 20, 31 | left: 0, 32 | width: 200, 33 | height: 20, 34 | border: '1px solid white' 35 | }); 36 | 37 | bar = null; 38 | 39 | makeBar = function() { 40 | loading.empty(); 41 | return bar = $('
').appendTo(loading).css({ 42 | position: 'absolute', 43 | top: 0, 44 | left: 0, 45 | width: 0, 46 | height: 20, 47 | backgroundColor: 'white', 48 | '-webkit-transition': 'width 0.7s' 49 | }); 50 | }; 51 | 52 | exports.show = function(text) { 53 | label.text(text); 54 | makeBar(); 55 | return container.fadeIn('slow').appendTo(ui); 56 | }; 57 | 58 | exports.hide = function() { 59 | return container.fadeOut('slow', function() { 60 | return container.detach(); 61 | }); 62 | }; 63 | 64 | exports.progress = function(factor) { 65 | return bar.width(factor * 200); 66 | }; 67 | -------------------------------------------------------------------------------- /lib/schedule.coffee: -------------------------------------------------------------------------------- 1 | exports.run = (callback) -> 2 | last = gettime() 3 | 4 | step = -> 5 | current = gettime() 6 | delta = current-last 7 | last = current 8 | callback current, delta 9 | requestAnimationFrame step 10 | 11 | requestAnimationFrame step 12 | -------------------------------------------------------------------------------- /lib/schedule.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | 3 | exports.run = function(callback) { 4 | var last, step; 5 | last = gettime(); 6 | step = function() { 7 | var current, delta; 8 | current = gettime(); 9 | delta = current - last; 10 | last = current; 11 | callback(current, delta); 12 | return requestAnimationFrame(step); 13 | }; 14 | return requestAnimationFrame(step); 15 | }; 16 | -------------------------------------------------------------------------------- /lib/webgl-nuke-vendor-prefix.coffee: -------------------------------------------------------------------------------- 1 | if window.WebGLRenderingContext? 2 | vendors = ['WEBKIT', 'MOZ', 'MS', 'O'] 3 | vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/ 4 | 5 | getExtension = WebGLRenderingContext.prototype.getExtension 6 | WebGLRenderingContext.prototype.getExtension = (name) -> 7 | match = name.match vendorRe 8 | if match != null 9 | name = match[1] 10 | 11 | extobj = getExtension.call @, name 12 | if extobj == null 13 | for vendor in vendors 14 | extobj = getExtension.call @, vendor + '_' + name 15 | if extobj != null 16 | return extobj 17 | return null 18 | else 19 | return extobj 20 | 21 | getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions 22 | WebGLRenderingContext.prototype.getSupportedExtensions = -> 23 | supported = getSupportedExtensions.call @ 24 | result = [] 25 | 26 | for extension in supported 27 | match = extension.match vendorRe 28 | if match != null 29 | extension = match[1] 30 | 31 | if extension not in result 32 | result.push extension 33 | 34 | return result 35 | -------------------------------------------------------------------------------- /lib/webgl-nuke-vendor-prefix.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var getExtension, getSupportedExtensions, vendorRe, vendors, 3 | __indexOf = [].indexOf || function(item) { for (var i = 0, l = this.length; i < l; i++) { if (i in this && this[i] === item) return i; } return -1; }; 4 | 5 | if (window.WebGLRenderingContext != null) { 6 | vendors = ['WEBKIT', 'MOZ', 'MS', 'O']; 7 | vendorRe = /^WEBKIT_(.*)|MOZ_(.*)|MS_(.*)|O_(.*)/; 8 | getExtension = WebGLRenderingContext.prototype.getExtension; 9 | WebGLRenderingContext.prototype.getExtension = function(name) { 10 | var extobj, match, vendor, _i, _len; 11 | match = name.match(vendorRe); 12 | if (match !== null) { 13 | name = match[1]; 14 | } 15 | extobj = getExtension.call(this, name); 16 | if (extobj === null) { 17 | for (_i = 0, _len = vendors.length; _i < _len; _i++) { 18 | vendor = vendors[_i]; 19 | extobj = getExtension.call(this, vendor + '_' + name); 20 | if (extobj !== null) { 21 | return extobj; 22 | } 23 | } 24 | return null; 25 | } else { 26 | return extobj; 27 | } 28 | }; 29 | getSupportedExtensions = WebGLRenderingContext.prototype.getSupportedExtensions; 30 | WebGLRenderingContext.prototype.getSupportedExtensions = function() { 31 | var extension, match, result, supported, _i, _len; 32 | supported = getSupportedExtensions.call(this); 33 | result = []; 34 | for (_i = 0, _len = supported.length; _i < _len; _i++) { 35 | extension = supported[_i]; 36 | match = extension.match(vendorRe); 37 | if (match !== null) { 38 | extension = match[1]; 39 | } 40 | if (__indexOf.call(result, extension) < 0) { 41 | result.push(extension); 42 | } 43 | } 44 | return result; 45 | }; 46 | } 47 | -------------------------------------------------------------------------------- /lib/webgl/cube.coffee: -------------------------------------------------------------------------------- 1 | return class Cube extends require('drawable') 2 | attribs: ['position', 'normal', 'barycentric'] 3 | pointers: [ 4 | {name: 'position', size: 3, offset: 0, stride: 9}, 5 | {name: 'normal', size: 3, offset: 3, stride: 9}, 6 | {name: 'barycentric', size: 3, offset: 6, stride: 9}, 7 | ] 8 | 9 | constructor: (@gl, s=1) -> 10 | super() 11 | @size = 6 * 6 12 | vertices = [ 13 | -s, -s, -s, 0, 0, -1, 1,0,0, 14 | -s, s, -s, 0, 0, -1, 0,1,0, 15 | s, s, -s, 0, 0, -1, 0,0,1, 16 | s, -s, -s, 0, 0, -1, 1,0,0, 17 | -s, -s, -s, 0, 0, -1, 0,1,0, 18 | s, s, -s, 0, 0, -1, 0,0,1, 19 | 20 | s, s, s, 0, 0, 1, 1,0,0, 21 | -s, s, s, 0, 0, 1, 0,1,0, 22 | -s, -s, s, 0, 0, 1, 0,0,1, 23 | s, s, s, 0, 0, 1, 1,0,0, 24 | -s, -s, s, 0, 0, 1, 0,1,0, 25 | s, -s, s, 0, 0, 1, 0,0,1, 26 | 27 | -s, s, -s, 0, 1, 0, 1,0,0, 28 | -s, s, s, 0, 1, 0, 0,1,0, 29 | s, s, s, 0, 1, 0, 0,0,1, 30 | s, s, -s, 0, 1, 0, 1,0,0, 31 | -s, s, -s, 0, 1, 0, 0,1,0, 32 | s, s, s, 0, 1, 0, 0,0,1, 33 | 34 | s, -s, s, 0, -1, 0, 1,0,0, 35 | -s, -s, s, 0, -1, 0, 0,1,0, 36 | -s, -s, -s, 0, -1, 0, 0,0,1, 37 | s, -s, s, 0, -1, 0, 1,0,0, 38 | -s, -s, -s, 0, -1, 0, 0,1,0, 39 | s, -s, -s, 0, -1, 0, 0,0,1, 40 | 41 | -s, -s, -s, -1, 0, 0, 1,0,0, 42 | -s, -s, s, -1, 0, 0, 0,1,0, 43 | -s, s, s, -1, 0, 0, 0,0,1, 44 | -s, s, -s, -1, 0, 0, 1,0,0, 45 | -s, -s, -s, -1, 0, 0, 0,1,0, 46 | -s, s, s, -1, 0, 0, 0,0,1, 47 | 48 | s, s, s, 1, 0, 0, 1,0,0, 49 | s, -s, s, 1, 0, 0, 0,1,0, 50 | s, -s, -s, 1, 0, 0, 0,0,1, 51 | s, s, s, 1, 0, 0, 1,0,0, 52 | s, -s, -s, 1, 0, 0, 0,1,0, 53 | s, s, -s, 1, 0, 0, 0,0,1, 54 | ] 55 | 56 | @uploadList vertices 57 | -------------------------------------------------------------------------------- /lib/webgl/cube.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Cube, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | return Cube = (function(_super) { 7 | 8 | __extends(Cube, _super); 9 | 10 | Cube.prototype.attribs = ['position', 'normal', 'barycentric']; 11 | 12 | Cube.prototype.pointers = [ 13 | { 14 | name: 'position', 15 | size: 3, 16 | offset: 0, 17 | stride: 9 18 | }, { 19 | name: 'normal', 20 | size: 3, 21 | offset: 3, 22 | stride: 9 23 | }, { 24 | name: 'barycentric', 25 | size: 3, 26 | offset: 6, 27 | stride: 9 28 | } 29 | ]; 30 | 31 | function Cube(gl, s) { 32 | var vertices; 33 | this.gl = gl; 34 | if (s == null) { 35 | s = 1; 36 | } 37 | Cube.__super__.constructor.call(this); 38 | this.size = 6 * 6; 39 | vertices = [-s, -s, -s, 0, 0, -1, 1, 0, 0, -s, s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, -s, -s, 0, 0, -1, 1, 0, 0, -s, -s, -s, 0, 0, -1, 0, 1, 0, s, s, -s, 0, 0, -1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, s, s, 0, 0, 1, 0, 1, 0, -s, -s, s, 0, 0, 1, 0, 0, 1, s, s, s, 0, 0, 1, 1, 0, 0, -s, -s, s, 0, 0, 1, 0, 1, 0, s, -s, s, 0, 0, 1, 0, 0, 1, -s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, s, -s, 0, 1, 0, 1, 0, 0, -s, s, -s, 0, 1, 0, 0, 1, 0, s, s, s, 0, 1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, s, 0, -1, 0, 0, 1, 0, -s, -s, -s, 0, -1, 0, 0, 0, 1, s, -s, s, 0, -1, 0, 1, 0, 0, -s, -s, -s, 0, -1, 0, 0, 1, 0, s, -s, -s, 0, -1, 0, 0, 0, 1, -s, -s, -s, -1, 0, 0, 1, 0, 0, -s, -s, s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, -s, s, -s, -1, 0, 0, 1, 0, 0, -s, -s, -s, -1, 0, 0, 0, 1, 0, -s, s, s, -1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, s, 1, 0, 0, 0, 1, 0, s, -s, -s, 1, 0, 0, 0, 0, 1, s, s, s, 1, 0, 0, 1, 0, 0, s, -s, -s, 1, 0, 0, 0, 1, 0, s, s, -s, 1, 0, 0, 0, 0, 1]; 40 | this.uploadList(vertices); 41 | } 42 | 43 | return Cube; 44 | 45 | })(require('drawable')); 46 | -------------------------------------------------------------------------------- /lib/webgl/drawable.coffee: -------------------------------------------------------------------------------- 1 | return class Drawable 2 | float_size = Float32Array.BYTES_PER_ELEMENT 3 | 4 | constructor: () -> 5 | @first = 0 6 | @size = 0 7 | @buffer = @gl.createBuffer() 8 | @mode = @gl.TRIANGLES 9 | 10 | setPointer: (shader, name, size=3, start=0, stride=0) -> 11 | location = shader.attribLoc name 12 | if location >= 0 13 | @gl.vertexAttribPointer location, size, @gl.FLOAT, false, stride*float_size, start*float_size 14 | return @ 15 | 16 | setPointersForShader: (shader) -> 17 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 18 | for pointer in @pointers 19 | @setPointer shader, pointer.name, pointer.size, pointer.offset, pointer.stride 20 | return @ 21 | 22 | draw: (shader) -> 23 | if shader then @setPointersForShader shader 24 | @gl.drawArrays @mode, @first, @size 25 | if shader then @disableAttribs shader 26 | return @ 27 | 28 | drawRange: (start=@first, size=@size) -> 29 | @gl.drawArrays @mode, start, size 30 | 31 | disableAttribs: (shader) -> 32 | for name in @attribs 33 | location = shader.attribLoc name 34 | if location >= 0 then @gl.disableVertexAttribArray location 35 | return @ 36 | 37 | uploadList: (list) -> 38 | data = new Float32Array list 39 | @upload data 40 | 41 | upload: (data) -> 42 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 43 | @gl.bufferData @gl.ARRAY_BUFFER, data, @gl.STATIC_DRAW 44 | @gl.bindBuffer @gl.ARRAY_BUFFER, null 45 | -------------------------------------------------------------------------------- /lib/webgl/drawable.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Drawable; 3 | 4 | return Drawable = (function() { 5 | var float_size; 6 | 7 | float_size = Float32Array.BYTES_PER_ELEMENT; 8 | 9 | function Drawable() { 10 | this.first = 0; 11 | this.size = 0; 12 | this.buffer = this.gl.createBuffer(); 13 | this.mode = this.gl.TRIANGLES; 14 | } 15 | 16 | Drawable.prototype.setPointer = function(shader, name, size, start, stride) { 17 | var location; 18 | if (size == null) { 19 | size = 3; 20 | } 21 | if (start == null) { 22 | start = 0; 23 | } 24 | if (stride == null) { 25 | stride = 0; 26 | } 27 | location = shader.attribLoc(name); 28 | if (location >= 0) { 29 | this.gl.vertexAttribPointer(location, size, this.gl.FLOAT, false, stride * float_size, start * float_size); 30 | } 31 | return this; 32 | }; 33 | 34 | Drawable.prototype.setPointersForShader = function(shader) { 35 | var pointer, _i, _len, _ref; 36 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 37 | _ref = this.pointers; 38 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 39 | pointer = _ref[_i]; 40 | this.setPointer(shader, pointer.name, pointer.size, pointer.offset, pointer.stride); 41 | } 42 | return this; 43 | }; 44 | 45 | Drawable.prototype.draw = function(shader) { 46 | if (shader) { 47 | this.setPointersForShader(shader); 48 | } 49 | this.gl.drawArrays(this.mode, this.first, this.size); 50 | if (shader) { 51 | this.disableAttribs(shader); 52 | } 53 | return this; 54 | }; 55 | 56 | Drawable.prototype.drawRange = function(start, size) { 57 | if (start == null) { 58 | start = this.first; 59 | } 60 | if (size == null) { 61 | size = this.size; 62 | } 63 | return this.gl.drawArrays(this.mode, start, size); 64 | }; 65 | 66 | Drawable.prototype.disableAttribs = function(shader) { 67 | var location, name, _i, _len, _ref; 68 | _ref = this.attribs; 69 | for (_i = 0, _len = _ref.length; _i < _len; _i++) { 70 | name = _ref[_i]; 71 | location = shader.attribLoc(name); 72 | if (location >= 0) { 73 | this.gl.disableVertexAttribArray(location); 74 | } 75 | } 76 | return this; 77 | }; 78 | 79 | Drawable.prototype.uploadList = function(list) { 80 | var data; 81 | data = new Float32Array(list); 82 | return this.upload(data); 83 | }; 84 | 85 | Drawable.prototype.upload = function(data) { 86 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 87 | this.gl.bufferData(this.gl.ARRAY_BUFFER, data, this.gl.STATIC_DRAW); 88 | return this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null); 89 | }; 90 | 91 | return Drawable; 92 | 93 | })(); 94 | -------------------------------------------------------------------------------- /lib/webgl/framebuffer.coffee: -------------------------------------------------------------------------------- 1 | framebufferBinding = null 2 | 3 | exports.Framebuffer = class Framebuffer 4 | constructor: (@gl) -> 5 | @buffer = @gl.createFramebuffer() 6 | 7 | bind: -> 8 | if framebufferBinding isnt @ 9 | @gl.bindFramebuffer @gl.FRAMEBUFFER, @buffer 10 | framebufferBinding = @ 11 | 12 | return @ 13 | 14 | unbind: -> 15 | if framebufferBinding isnt null 16 | @gl.bindFramebuffer @gl.FRAMEBUFFER, null 17 | framebufferBinding = null 18 | 19 | return @ 20 | 21 | check: -> 22 | result = @gl.checkFramebufferStatus @gl.FRAMEBUFFER 23 | switch result 24 | when @gl.FRAMEBUFFER_UNSUPPORTED 25 | throw 'Framebuffer is unsupported' 26 | when @gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT 27 | throw 'Framebuffer incomplete attachment' 28 | when @gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS 29 | throw 'Framebuffer incomplete dimensions' 30 | when @gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 31 | throw 'Framebuffer incomplete missing attachment' 32 | return @ 33 | 34 | color: (texture, target=texture.target) -> 35 | @gl.framebufferTexture2D @gl.FRAMEBUFFER, @gl.COLOR_ATTACHMENT0, target, texture.handle, 0 36 | @check() 37 | return @ 38 | 39 | depth: (buffer) -> 40 | @gl.framebufferRenderbuffer @gl.FRAMEBUFFER, @gl.DEPTH_ATTACHMENT, @gl.RENDERBUFFER, buffer.id 41 | @check() 42 | return @ 43 | 44 | destroy: -> 45 | @gl.deleteFramebuffer @buffer 46 | 47 | class Renderbuffer 48 | constructor: (@gl) -> 49 | @id = @gl.createRenderbuffer() 50 | 51 | bind: -> 52 | @gl.bindRenderbuffer @gl.RENDERBUFFER, @id 53 | return @ 54 | 55 | setSize: (@width, @height) -> 56 | @bind() 57 | @gl.renderbufferStorage @gl.RENDERBUFFER, @gl[@format], @width, @height 58 | @unbind() 59 | 60 | unbind: -> 61 | @gl.bindRenderbuffer @gl.RENDERBUFFER, null 62 | return @ 63 | 64 | exports.Depthbuffer = class extends Renderbuffer 65 | format: 'DEPTH_COMPONENT16' 66 | -------------------------------------------------------------------------------- /lib/webgl/framebuffer.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Framebuffer, Renderbuffer, framebufferBinding, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | framebufferBinding = null; 7 | 8 | exports.Framebuffer = Framebuffer = (function() { 9 | 10 | function Framebuffer(gl) { 11 | this.gl = gl; 12 | this.buffer = this.gl.createFramebuffer(); 13 | } 14 | 15 | Framebuffer.prototype.bind = function() { 16 | if (framebufferBinding !== this) { 17 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, this.buffer); 18 | framebufferBinding = this; 19 | } 20 | return this; 21 | }; 22 | 23 | Framebuffer.prototype.unbind = function() { 24 | if (framebufferBinding !== null) { 25 | this.gl.bindFramebuffer(this.gl.FRAMEBUFFER, null); 26 | framebufferBinding = null; 27 | } 28 | return this; 29 | }; 30 | 31 | Framebuffer.prototype.check = function() { 32 | var result; 33 | result = this.gl.checkFramebufferStatus(this.gl.FRAMEBUFFER); 34 | switch (result) { 35 | case this.gl.FRAMEBUFFER_UNSUPPORTED: 36 | throw 'Framebuffer is unsupported'; 37 | break; 38 | case this.gl.FRAMEBUFFER_INCOMPLETE_ATTACHMENT: 39 | throw 'Framebuffer incomplete attachment'; 40 | break; 41 | case this.gl.FRAMEBUFFER_INCOMPLETE_DIMENSIONS: 42 | throw 'Framebuffer incomplete dimensions'; 43 | break; 44 | case this.gl.FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT: 45 | throw 'Framebuffer incomplete missing attachment'; 46 | } 47 | return this; 48 | }; 49 | 50 | Framebuffer.prototype.color = function(texture, target) { 51 | if (target == null) { 52 | target = texture.target; 53 | } 54 | this.gl.framebufferTexture2D(this.gl.FRAMEBUFFER, this.gl.COLOR_ATTACHMENT0, target, texture.handle, 0); 55 | this.check(); 56 | return this; 57 | }; 58 | 59 | Framebuffer.prototype.depth = function(buffer) { 60 | this.gl.framebufferRenderbuffer(this.gl.FRAMEBUFFER, this.gl.DEPTH_ATTACHMENT, this.gl.RENDERBUFFER, buffer.id); 61 | this.check(); 62 | return this; 63 | }; 64 | 65 | Framebuffer.prototype.destroy = function() { 66 | return this.gl.deleteFramebuffer(this.buffer); 67 | }; 68 | 69 | return Framebuffer; 70 | 71 | })(); 72 | 73 | Renderbuffer = (function() { 74 | 75 | function Renderbuffer(gl) { 76 | this.gl = gl; 77 | this.id = this.gl.createRenderbuffer(); 78 | } 79 | 80 | Renderbuffer.prototype.bind = function() { 81 | this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, this.id); 82 | return this; 83 | }; 84 | 85 | Renderbuffer.prototype.setSize = function(width, height) { 86 | this.width = width; 87 | this.height = height; 88 | this.bind(); 89 | this.gl.renderbufferStorage(this.gl.RENDERBUFFER, this.gl[this.format], this.width, this.height); 90 | return this.unbind(); 91 | }; 92 | 93 | Renderbuffer.prototype.unbind = function() { 94 | this.gl.bindRenderbuffer(this.gl.RENDERBUFFER, null); 95 | return this; 96 | }; 97 | 98 | return Renderbuffer; 99 | 100 | })(); 101 | 102 | exports.Depthbuffer = (function(_super) { 103 | 104 | __extends(_Class, _super); 105 | 106 | function _Class() { 107 | return _Class.__super__.constructor.apply(this, arguments); 108 | } 109 | 110 | _Class.prototype.format = 'DEPTH_COMPONENT16'; 111 | 112 | return _Class; 113 | 114 | })(Renderbuffer); 115 | -------------------------------------------------------------------------------- /lib/webgl/hexgrid.coffee: -------------------------------------------------------------------------------- 1 | clamp = (value, left, right) -> 2 | return if value < left then left else if value > right then right else value 3 | 4 | return class Hexgrid extends require('drawable') 5 | attribs: ['position', 'texcoord', 'barycentric'] 6 | 7 | constructor: (@gl, xsize=16, ysize=16, width=1, height=1) -> 8 | super() 9 | vertices = [] 10 | 11 | for x in [0..xsize] 12 | x1 = clamp((x-0.5)/xsize, 0, 1) 13 | x2 = clamp((x+0.0)/xsize, 0, 1) 14 | x3 = clamp((x+0.5)/xsize, 0, 1) 15 | x4 = clamp((x+1.0)/xsize, 0, 1) 16 | for y in [0...ysize] by 2 17 | t = (y+0)/ysize 18 | m = (y+1)/ysize 19 | b = (y+2)/ysize 20 | 21 | vertices.push( 22 | x2*width,0,m*height, x2,m, 0,0,1, 23 | x3*width,0,t*height, x3,t, 0,1,0, 24 | x1*width,0,t*height, x1,t, 1,0,0, 25 | 26 | x4*width,0,m*height, x4,m, 0,0,1, 27 | x3*width,0,t*height, x3,t, 0,1,0, 28 | x2*width,0,m*height, x2,m, 1,0,0, 29 | 30 | x3*width,0,b*height, x3,b, 0,0,1, 31 | x2*width,0,m*height, x2,m, 0,1,0, 32 | x1*width,0,b*height, x1,b, 1,0,0, 33 | 34 | x3*width,0,b*height, x3,b, 0,0,1 35 | x4*width,0,m*height, x4,m, 0,1,0, 36 | x2*width,0,m*height, x2,m, 1,0,0, 37 | ) 38 | @size = vertices.length/8 39 | @uploadList vertices 40 | 41 | setPointersForShader: (shader) -> 42 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 43 | @setPointer shader, 'position', 3, 0, 8 44 | @setPointer shader, 'texcoord', 2, 3, 8 45 | @setPointer shader, 'barycentric', 3, 5, 8 46 | 47 | return @ 48 | -------------------------------------------------------------------------------- /lib/webgl/hexgrid.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Hexgrid, clamp, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | clamp = function(value, left, right) { 7 | if (value < left) { 8 | return left; 9 | } else if (value > right) { 10 | return right; 11 | } else { 12 | return value; 13 | } 14 | }; 15 | 16 | return Hexgrid = (function(_super) { 17 | 18 | __extends(Hexgrid, _super); 19 | 20 | Hexgrid.prototype.attribs = ['position', 'texcoord', 'barycentric']; 21 | 22 | function Hexgrid(gl, xsize, ysize, width, height) { 23 | var b, m, t, vertices, x, x1, x2, x3, x4, y, _i, _j; 24 | this.gl = gl; 25 | if (xsize == null) { 26 | xsize = 16; 27 | } 28 | if (ysize == null) { 29 | ysize = 16; 30 | } 31 | if (width == null) { 32 | width = 1; 33 | } 34 | if (height == null) { 35 | height = 1; 36 | } 37 | Hexgrid.__super__.constructor.call(this); 38 | vertices = []; 39 | for (x = _i = 0; 0 <= xsize ? _i <= xsize : _i >= xsize; x = 0 <= xsize ? ++_i : --_i) { 40 | x1 = clamp((x - 0.5) / xsize, 0, 1); 41 | x2 = clamp((x + 0.0) / xsize, 0, 1); 42 | x3 = clamp((x + 0.5) / xsize, 0, 1); 43 | x4 = clamp((x + 1.0) / xsize, 0, 1); 44 | for (y = _j = 0; _j < ysize; y = _j += 2) { 45 | t = (y + 0) / ysize; 46 | m = (y + 1) / ysize; 47 | b = (y + 2) / ysize; 48 | vertices.push(x2 * width, 0, m * height, x2, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x1 * width, 0, t * height, x1, t, 1, 0, 0, x4 * width, 0, m * height, x4, m, 0, 0, 1, x3 * width, 0, t * height, x3, t, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x2 * width, 0, m * height, x2, m, 0, 1, 0, x1 * width, 0, b * height, x1, b, 1, 0, 0, x3 * width, 0, b * height, x3, b, 0, 0, 1, x4 * width, 0, m * height, x4, m, 0, 1, 0, x2 * width, 0, m * height, x2, m, 1, 0, 0); 49 | } 50 | } 51 | this.size = vertices.length / 8; 52 | this.uploadList(vertices); 53 | } 54 | 55 | Hexgrid.prototype.setPointersForShader = function(shader) { 56 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 57 | this.setPointer(shader, 'position', 3, 0, 8); 58 | this.setPointer(shader, 'texcoord', 2, 3, 8); 59 | this.setPointer(shader, 'barycentric', 3, 5, 8); 60 | return this; 61 | }; 62 | 63 | return Hexgrid; 64 | 65 | })(require('drawable')); 66 | -------------------------------------------------------------------------------- /lib/webgl/model.coffee: -------------------------------------------------------------------------------- 1 | return class Model extends require('drawable') 2 | attribs: ['position', 'normal', 'texcoord'] 3 | 4 | constructor: (@gl, data) -> 5 | super() 6 | @size = data.byteLength/(8*Float32Array.BYTES_PER_ELEMENT) 7 | @upload data 8 | 9 | setPointersForShader: (shader) -> 10 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 11 | @setPointer shader, 'position', 3, 0, 8 12 | @setPointer shader, 'normal', 3, 3, 8 13 | @setPointer shader, 'texcoord', 2, 6, 8 14 | 15 | return @ 16 | -------------------------------------------------------------------------------- /lib/webgl/model.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Model, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | return Model = (function(_super) { 7 | 8 | __extends(Model, _super); 9 | 10 | Model.prototype.attribs = ['position', 'normal', 'texcoord']; 11 | 12 | function Model(gl, data) { 13 | this.gl = gl; 14 | Model.__super__.constructor.call(this); 15 | this.size = data.byteLength / (8 * Float32Array.BYTES_PER_ELEMENT); 16 | this.upload(data); 17 | } 18 | 19 | Model.prototype.setPointersForShader = function(shader) { 20 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 21 | this.setPointer(shader, 'position', 3, 0, 8); 22 | this.setPointer(shader, 'normal', 3, 3, 8); 23 | this.setPointer(shader, 'texcoord', 2, 6, 8); 24 | return this; 25 | }; 26 | 27 | return Model; 28 | 29 | })(require('drawable')); 30 | -------------------------------------------------------------------------------- /lib/webgl/plane.coffee: -------------------------------------------------------------------------------- 1 | return class Plane extends require('drawable') 2 | attribs: ['position', 'normal', 'texcoord'] 3 | 4 | constructor: (@gl, s=1) -> 5 | super() 6 | @size = 6 7 | 8 | vertices = [ 9 | -s, 0, -s, 0, 1, 0, 0,0, 10 | -s, 0, s, 0, 1, 0, 0,1, 11 | s, 0, s, 0, 1, 0, 1,1, 12 | 13 | s, 0, -s, 0, 1, 0, 1,0, 14 | -s, 0, -s, 0, 1, 0, 0,0, 15 | s, 0, s, 0, 1, 0, 1,1, 16 | ] 17 | 18 | @uploadList vertices 19 | 20 | setPointersForShader: (shader) -> 21 | @gl.bindBuffer @gl.ARRAY_BUFFER, @buffer 22 | @setPointer shader, 'position', 3, 0, 8 23 | @setPointer shader, 'normal', 3, 3, 8 24 | @setPointer shader, 'texcoord', 2, 6, 8 25 | 26 | return @ 27 | -------------------------------------------------------------------------------- /lib/webgl/plane.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Plane, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | return Plane = (function(_super) { 7 | 8 | __extends(Plane, _super); 9 | 10 | Plane.prototype.attribs = ['position', 'normal', 'texcoord']; 11 | 12 | function Plane(gl, s) { 13 | var vertices; 14 | this.gl = gl; 15 | if (s == null) { 16 | s = 1; 17 | } 18 | Plane.__super__.constructor.call(this); 19 | this.size = 6; 20 | vertices = [-s, 0, -s, 0, 1, 0, 0, 0, -s, 0, s, 0, 1, 0, 0, 1, s, 0, s, 0, 1, 0, 1, 1, s, 0, -s, 0, 1, 0, 1, 0, -s, 0, -s, 0, 1, 0, 0, 0, s, 0, s, 0, 1, 0, 1, 1]; 21 | this.uploadList(vertices); 22 | } 23 | 24 | Plane.prototype.setPointersForShader = function(shader) { 25 | this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.buffer); 26 | this.setPointer(shader, 'position', 3, 0, 8); 27 | this.setPointer(shader, 'normal', 3, 3, 8); 28 | this.setPointer(shader, 'texcoord', 2, 6, 8); 29 | return this; 30 | }; 31 | 32 | return Plane; 33 | 34 | })(require('drawable')); 35 | -------------------------------------------------------------------------------- /lib/webgl/quad.coffee: -------------------------------------------------------------------------------- 1 | return class Quad extends require('drawable') 2 | attribs: ['position'] 3 | pointers: [ 4 | {name: 'position', size: 2, offset: 0, stride: 2}, 5 | ] 6 | 7 | constructor: (@gl) -> 8 | super() 9 | @size = 6 10 | 11 | vertices = [ 12 | -1, -1, 1, -1, 1, 1, 13 | -1, -1, 1, 1, -1, 1, 14 | ] 15 | 16 | @uploadList vertices 17 | -------------------------------------------------------------------------------- /lib/webgl/quad.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Quad, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | return Quad = (function(_super) { 7 | 8 | __extends(Quad, _super); 9 | 10 | Quad.prototype.attribs = ['position']; 11 | 12 | Quad.prototype.pointers = [ 13 | { 14 | name: 'position', 15 | size: 2, 16 | offset: 0, 17 | stride: 2 18 | } 19 | ]; 20 | 21 | function Quad(gl) { 22 | var vertices; 23 | this.gl = gl; 24 | Quad.__super__.constructor.call(this); 25 | this.size = 6; 26 | vertices = [-1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, 1]; 27 | this.uploadList(vertices); 28 | } 29 | 30 | return Quad; 31 | 32 | })(require('drawable')); 33 | -------------------------------------------------------------------------------- /lib/webgl/shader.coffee: -------------------------------------------------------------------------------- 1 | directives = [ 2 | '#ifdef GL_FRAGMENT_PRECISION_HIGH', 3 | 'precision highp int;', 4 | 'precision highp float;', 5 | '#else', 6 | 'precision mediump int;', 7 | 'precision mediump float;', 8 | '#endif', 9 | ] 10 | 11 | in_use = null 12 | 13 | return class Shader 14 | @lastError = '' 15 | 16 | @splitLines = (path, source) -> 17 | result = [] 18 | for line, i in source.split '\n' 19 | result.push 20 | line: i 21 | text: line 22 | path: path 23 | return result 24 | 25 | @error = 'ShaderError' 26 | 27 | constructor: (@gl, @path, source) -> 28 | dirname = @path.split '/' 29 | dirname.pop() 30 | @dirname = dirname.join '/' 31 | 32 | @program = @gl.createProgram() 33 | @vs = @gl.createShader gl.VERTEX_SHADER 34 | @fs = @gl.createShader gl.FRAGMENT_SHADER 35 | 36 | @gl.attachShader @program, @vs 37 | @gl.attachShader @program, @fs 38 | 39 | @link source 40 | 41 | preprocess: (source) -> 42 | lines = source.split '\n' 43 | shaders = {'global': [], 'fragment': [], 'vertex': []} 44 | type = 'global' 45 | for line, i in lines 46 | match = line.match /^(\w+):$/ 47 | if match 48 | type = match[1] 49 | else 50 | shaders[type].push 51 | line: i 52 | text: line 53 | path: @path 54 | 55 | global = @resolveLines(shaders.global) 56 | shaders.fragment = global.concat(@resolveLines(shaders.fragment)) 57 | shaders.vertex = global.concat(@resolveLines(shaders.vertex)) 58 | return shaders 59 | 60 | resolveLines: (stage) -> 61 | result = [] 62 | for line in stage 63 | match = line.text.match /^\s+#require (\S+)\s*$/ 64 | if match 65 | path = "#{match[1]}.shaderlib" 66 | abspath = loader.resolvePath(@dirname, path) 67 | lib = get abspath 68 | for line in lib 69 | result.push line 70 | else 71 | result.push line 72 | return result 73 | 74 | concat: (stage) -> 75 | result = '' 76 | for line in directives 77 | result += line + '\n' 78 | result += '#line 0\n' 79 | for line in stage 80 | result += line.text + '\n' 81 | return result 82 | 83 | link: (source) -> 84 | shaders = @preprocess source 85 | @compile @vs, shaders.vertex 86 | @compile @fs, shaders.fragment 87 | @gl.linkProgram @program 88 | 89 | if not @gl.getProgramParameter @program, @gl.LINK_STATUS 90 | error = "Shader Link Error for file: #{@path}:\n#{@gl.getProgramInfoLog(@program)}" 91 | console.error error 92 | Shader.lastError = error 93 | throw Shader.error 94 | 95 | @attrib_cache = {} 96 | @uniform_cache = {} 97 | @value_cache = {} 98 | 99 | compile: (shader, lines) -> 100 | source = @concat lines 101 | @gl.shaderSource shader, source 102 | @gl.compileShader shader 103 | 104 | if not @gl.getShaderParameter shader, @gl.COMPILE_STATUS 105 | error = @gl.getShaderInfoLog(shader) 106 | group = "Shader Compile Error for file: #{@path}" 107 | translated = @translateError(error, lines) 108 | text = group + '\n' + translated 109 | Shader.lastError = text 110 | 111 | console.group group 112 | console.warn translated 113 | console.groupEnd() 114 | 115 | throw Shader.error 116 | return 117 | 118 | translateError: (error, sourcelines) -> 119 | result = [] 120 | for line, i in error.split('\n') 121 | match = line.match /ERROR: \d+:(\d+): (.*)/ 122 | if match 123 | lineno = parseFloat(match[1]) 124 | message = match[2] 125 | sourceline = sourcelines[lineno-1] 126 | result.push "ERROR: Line #{sourceline.line+1}: File #{sourceline.path}: #{message} SOURCE: #{sourceline.text}" 127 | else 128 | result.push line 129 | return result.join('\n') 130 | 131 | attribLoc: (name) -> 132 | location = @attrib_cache[name] 133 | if location is undefined 134 | location = @attrib_cache[name] = @gl.getAttribLocation @program, name 135 | @gl.enableVertexAttribArray location if location >= 0 136 | return location 137 | 138 | use: -> 139 | if @ != in_use 140 | in_use = @ 141 | @gl.useProgram @program 142 | return @ 143 | 144 | unbind: -> 145 | if in_use 146 | in_use = null 147 | @gl.useProgram null 148 | return @ 149 | 150 | loc: (name) -> 151 | location = @uniform_cache[name] 152 | if location is undefined 153 | location = @uniform_cache[name] = @gl.getUniformLocation @program, name 154 | return location 155 | 156 | i: (name, value) -> 157 | cached = @value_cache[name] 158 | if cached != value 159 | @value_cache[name] = value 160 | loc = @loc name 161 | @gl.uniform1i loc, value if loc 162 | return @ 163 | 164 | f: (name, value) -> 165 | cached = @value_cache[name] 166 | if cached != value 167 | @value_cache[name] = value 168 | loc = @loc name 169 | @gl.uniform1f loc, value if loc 170 | return @ 171 | 172 | fv: (name, values) -> 173 | loc = @loc name 174 | @gl.uniform1fv loc, values if loc 175 | return @ 176 | 177 | val2: (name, a, b) -> 178 | cached = @value_cache[name] 179 | if cached 180 | if cached.a != a or cached.b != b 181 | cached.a = a; cached.b = b 182 | loc = @loc name 183 | @gl.uniform2f loc, a, b if loc 184 | else 185 | @value_cache[name] = {a:a, b:b} 186 | loc = @loc name 187 | @gl.uniform2f loc, a, b if loc 188 | return @ 189 | 190 | val3: (name, a, b, c) -> 191 | cached = @value_cache[name] 192 | if cached 193 | if cached.a != a or cached.b != b or cached.c != c 194 | cached.a = a; cached.b = b; cached.c = c 195 | loc = @loc name 196 | @gl.uniform3f loc, a, b, c if loc 197 | else 198 | @value_cache[name] = {a:a, b:b, c:c} 199 | loc = @loc name 200 | @gl.uniform3f loc, a, b, c if loc 201 | return @ 202 | 203 | vec2: (name, value) -> 204 | loc = @loc name 205 | @gl.uniform2fv loc, value if loc 206 | return @ 207 | 208 | vec3: (name, value) -> 209 | loc = @loc name 210 | @gl.uniform3fv loc, value if loc 211 | return @ 212 | 213 | val4: (name, a, b, c, d) -> 214 | loc = @loc name 215 | @gl.uniform4f loc, a, b, c, d if loc 216 | return @ 217 | 218 | vec4: (name, a, b, c, e) -> 219 | loc = @loc name 220 | @gl.uniform2f loc, a, b, c, e if loc 221 | return @ 222 | 223 | mat4: (name, value) -> 224 | loc = @loc name 225 | if loc 226 | if value instanceof Mat4 227 | @gl.uniformMatrix4fv loc, @gl.FALSE, value.data 228 | else 229 | @gl.uniformMatrix4fv loc, @gl.FALSE, value 230 | return @ 231 | 232 | mat3: (name, value) -> 233 | loc = @loc name 234 | @gl.uniformMatrix3fv loc, @gl.FALSE, value.data if loc 235 | return @ 236 | 237 | draw: (drawable) -> 238 | drawable 239 | .setPointersForShader(@) 240 | .draw() 241 | .disableAttribs(@) 242 | return @ 243 | -------------------------------------------------------------------------------- /lib/webgl/sphere.coffee: -------------------------------------------------------------------------------- 1 | phi = (1+Math.sqrt(5))/2 2 | 3 | midp = (v1, v2) -> 4 | x1 = v1[0] 5 | y1 = v1[1] 6 | z1 = v1[2] 7 | 8 | x2 = v2[0] 9 | y2 = v2[1] 10 | z2 = v2[2] 11 | 12 | x3 = (x1+x2)/2 13 | y3 = (y1+y2)/2 14 | z3 = (z1+z2)/2 15 | 16 | return [x3, y3, z3] 17 | 18 | normalize = (faces, r) -> 19 | r ?= 1 20 | result = [] 21 | for face in faces 22 | new_face = [] 23 | result.push new_face 24 | for vertex in face 25 | x = vertex[0] 26 | y = vertex[1] 27 | z = vertex[2] 28 | l = Math.sqrt(x*x + y*y + z*z) 29 | new_face.push [(r*x)/l, (r*y)/l, (r*z)/l] 30 | return result 31 | 32 | subdivide = (faces) -> 33 | result = [] 34 | for face in faces 35 | v0 = face[0] 36 | v1 = face[1] 37 | v2 = face[2] 38 | 39 | va = midp v0, v1 40 | vb = midp v1, v2 41 | vc = midp v2, v0 42 | 43 | result.push( 44 | [v0, va, vc], 45 | [va, v1, vb], 46 | [vc, vb, v2], 47 | [va, vb, vc], 48 | ) 49 | 50 | return result 51 | 52 | v1 = [ 1, phi, 0] 53 | v2 = [ -1, phi, 0] 54 | v3 = [ 0, 1, phi] 55 | v4 = [ 0, 1, -phi] 56 | v5 = [ phi, 0, 1] 57 | v6 = [-phi, 0, 1] 58 | v7 = [-phi, 0, -1] 59 | v8 = [ phi, 0, -1] 60 | v9 = [ 0, -1, phi] 61 | v10 = [ 0, -1, -phi] 62 | v11 = [ -1, -phi, 0] 63 | v12 = [ 1, -phi, 0] 64 | 65 | faces = [ 66 | [ v1, v2, v3], 67 | [ v2, v1, v4], 68 | [ v1, v3, v5], 69 | [ v2, v6, v3], 70 | [ v2, v7, v6], 71 | [ v2, v4, v7], 72 | [ v1, v5, v8], 73 | [ v1, v8, v4], 74 | [ v9, v3, v6], 75 | [ v3, v9, v5], 76 | [ v4, v10, v7], 77 | [ v4, v8, v10], 78 | [ v6, v7, v11], 79 | [ v6, v11, v9], 80 | [ v7, v10, v11], 81 | [ v5, v12, v8], 82 | [v12, v5, v9], 83 | [v12, v10, v8], 84 | [v11, v12, v9], 85 | [v12, v11, v10] 86 | ] 87 | 88 | icosahedron = normalize(faces) 89 | 90 | vertexlist = (faces) -> 91 | vertices = [] 92 | for face in faces 93 | for vertex in face 94 | x = vertex[0] 95 | y = vertex[1] 96 | z = vertex[2] 97 | vertices.push x, y, z 98 | return vertices 99 | 100 | return class Sphere extends require('drawable') 101 | attribs: ['position'] 102 | pointers: [ 103 | {name: 'position', size: 3, offset: 0, stride: 3}, 104 | ] 105 | 106 | @makeVertices = (radius=1, subdivisions=3) -> 107 | template = icosahedron 108 | for i in [0...subdivisions] 109 | template = subdivide template 110 | template = normalize template 111 | faces = normalize template, radius 112 | vertices = vertexlist faces 113 | return vertices 114 | 115 | constructor: (@gl, radius=1, subdivisions=3) -> 116 | super() 117 | vertices = Sphere.makeVertices radius, subdivisions 118 | @size = vertices.length/3 119 | @uploadList vertices 120 | -------------------------------------------------------------------------------- /lib/webgl/sphere.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Sphere, faces, icosahedron, midp, normalize, phi, subdivide, v1, v10, v11, v12, v2, v3, v4, v5, v6, v7, v8, v9, vertexlist, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | phi = (1 + Math.sqrt(5)) / 2; 7 | 8 | midp = function(v1, v2) { 9 | var x1, x2, x3, y1, y2, y3, z1, z2, z3; 10 | x1 = v1[0]; 11 | y1 = v1[1]; 12 | z1 = v1[2]; 13 | x2 = v2[0]; 14 | y2 = v2[1]; 15 | z2 = v2[2]; 16 | x3 = (x1 + x2) / 2; 17 | y3 = (y1 + y2) / 2; 18 | z3 = (z1 + z2) / 2; 19 | return [x3, y3, z3]; 20 | }; 21 | 22 | normalize = function(faces, r) { 23 | var face, l, new_face, result, vertex, x, y, z, _i, _j, _len, _len1; 24 | if (r == null) { 25 | r = 1; 26 | } 27 | result = []; 28 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 29 | face = faces[_i]; 30 | new_face = []; 31 | result.push(new_face); 32 | for (_j = 0, _len1 = face.length; _j < _len1; _j++) { 33 | vertex = face[_j]; 34 | x = vertex[0]; 35 | y = vertex[1]; 36 | z = vertex[2]; 37 | l = Math.sqrt(x * x + y * y + z * z); 38 | new_face.push([(r * x) / l, (r * y) / l, (r * z) / l]); 39 | } 40 | } 41 | return result; 42 | }; 43 | 44 | subdivide = function(faces) { 45 | var face, result, v0, v1, v2, va, vb, vc, _i, _len; 46 | result = []; 47 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 48 | face = faces[_i]; 49 | v0 = face[0]; 50 | v1 = face[1]; 51 | v2 = face[2]; 52 | va = midp(v0, v1); 53 | vb = midp(v1, v2); 54 | vc = midp(v2, v0); 55 | result.push([v0, va, vc], [va, v1, vb], [vc, vb, v2], [va, vb, vc]); 56 | } 57 | return result; 58 | }; 59 | 60 | v1 = [1, phi, 0]; 61 | 62 | v2 = [-1, phi, 0]; 63 | 64 | v3 = [0, 1, phi]; 65 | 66 | v4 = [0, 1, -phi]; 67 | 68 | v5 = [phi, 0, 1]; 69 | 70 | v6 = [-phi, 0, 1]; 71 | 72 | v7 = [-phi, 0, -1]; 73 | 74 | v8 = [phi, 0, -1]; 75 | 76 | v9 = [0, -1, phi]; 77 | 78 | v10 = [0, -1, -phi]; 79 | 80 | v11 = [-1, -phi, 0]; 81 | 82 | v12 = [1, -phi, 0]; 83 | 84 | faces = [[v1, v2, v3], [v2, v1, v4], [v1, v3, v5], [v2, v6, v3], [v2, v7, v6], [v2, v4, v7], [v1, v5, v8], [v1, v8, v4], [v9, v3, v6], [v3, v9, v5], [v4, v10, v7], [v4, v8, v10], [v6, v7, v11], [v6, v11, v9], [v7, v10, v11], [v5, v12, v8], [v12, v5, v9], [v12, v10, v8], [v11, v12, v9], [v12, v11, v10]]; 85 | 86 | icosahedron = normalize(faces); 87 | 88 | vertexlist = function(faces) { 89 | var face, vertex, vertices, x, y, z, _i, _j, _len, _len1; 90 | vertices = []; 91 | for (_i = 0, _len = faces.length; _i < _len; _i++) { 92 | face = faces[_i]; 93 | for (_j = 0, _len1 = face.length; _j < _len1; _j++) { 94 | vertex = face[_j]; 95 | x = vertex[0]; 96 | y = vertex[1]; 97 | z = vertex[2]; 98 | vertices.push(x, y, z); 99 | } 100 | } 101 | return vertices; 102 | }; 103 | 104 | return Sphere = (function(_super) { 105 | 106 | __extends(Sphere, _super); 107 | 108 | Sphere.prototype.attribs = ['position']; 109 | 110 | Sphere.prototype.pointers = [ 111 | { 112 | name: 'position', 113 | size: 3, 114 | offset: 0, 115 | stride: 3 116 | } 117 | ]; 118 | 119 | Sphere.makeVertices = function(radius, subdivisions) { 120 | var i, template, vertices, _i; 121 | if (radius == null) { 122 | radius = 1; 123 | } 124 | if (subdivisions == null) { 125 | subdivisions = 3; 126 | } 127 | template = icosahedron; 128 | for (i = _i = 0; 0 <= subdivisions ? _i < subdivisions : _i > subdivisions; i = 0 <= subdivisions ? ++_i : --_i) { 129 | template = subdivide(template); 130 | template = normalize(template); 131 | } 132 | faces = normalize(template, radius); 133 | vertices = vertexlist(faces); 134 | return vertices; 135 | }; 136 | 137 | function Sphere(gl, radius, subdivisions) { 138 | var vertices; 139 | this.gl = gl; 140 | if (radius == null) { 141 | radius = 1; 142 | } 143 | if (subdivisions == null) { 144 | subdivisions = 3; 145 | } 146 | Sphere.__super__.constructor.call(this); 147 | vertices = Sphere.makeVertices(radius, subdivisions); 148 | this.size = vertices.length / 3; 149 | this.uploadList(vertices); 150 | } 151 | 152 | return Sphere; 153 | 154 | })(require('drawable')); 155 | -------------------------------------------------------------------------------- /lib/webgl/texture.coffee: -------------------------------------------------------------------------------- 1 | {Framebuffer} = require 'framebuffer' 2 | 3 | class Texture 4 | bound = [] 5 | ids = 0 6 | 7 | constructor: -> 8 | @handle = @gl.createTexture() 9 | @id = ids++ 10 | @unit = null 11 | 12 | bind: (unit=0) -> 13 | @unit = unit 14 | if bound[unit] != @id 15 | @gl.activeTexture @gl.TEXTURE0+unit 16 | @gl.bindTexture @target, @handle 17 | bound[unit] = @id 18 | return @ 19 | 20 | unbind: (unit=@unit) -> 21 | if unit and bound[unit] == @id 22 | @gl.activeTexture @gl.TEXTURE0+unit 23 | @gl.bindTexture @target, null 24 | bound[unit] = null 25 | return @ 26 | 27 | mipmap: -> 28 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR 29 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR 30 | @gl.generateMipmap @target 31 | return @ 32 | 33 | mipmapNearest: -> 34 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST 35 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR_MIPMAP_LINEAR 36 | @gl.generateMipmap @target 37 | return @ 38 | 39 | linear: -> 40 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.LINEAR 41 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.LINEAR 42 | return @ 43 | 44 | nearest: -> 45 | @gl.texParameteri @target, @gl.TEXTURE_MAG_FILTER, @gl.NEAREST 46 | @gl.texParameteri @target, @gl.TEXTURE_MIN_FILTER, @gl.NEAREST 47 | return @ 48 | 49 | clampToEdge: -> 50 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.CLAMP_TO_EDGE 51 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.CLAMP_TO_EDGE 52 | return @ 53 | 54 | repeat: -> 55 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_S, @gl.REPEAT 56 | @gl.texParameteri @target, @gl.TEXTURE_WRAP_T, @gl.REPEAT 57 | return @ 58 | 59 | anisotropy: -> 60 | ext = @gl.getExtension 'WEBKIT_EXT_texture_filter_anisotropic' 61 | if ext 62 | max = @gl.getParameter ext.MAX_TEXTURE_MAX_ANISOTROPY_EXT 63 | @gl.texParameterf @target, ext.TEXTURE_MAX_ANISOTROPY_EXT, max 64 | return @ 65 | 66 | exports.Texture2D = class Texture2D extends Texture 67 | constructor: (@gl, {@channels, @format, @type}={}) -> 68 | super() 69 | @channels ?= @gl.RGBA 70 | @format ?= @gl.RGBA 71 | @type ?= @gl.UNSIGNED_BYTE 72 | @target = @gl.TEXTURE_2D 73 | 74 | upload: (image) -> 75 | @uploadImage image 76 | return @ 77 | 78 | uploadImage: (image) -> 79 | @width = image.width 80 | @height = image.height 81 | @gl.texImage2D @target, 0, @channels, @format, @type, image 82 | return @ 83 | 84 | uploadData: (data, @width, @height) -> 85 | @gl.texImage2D @target, 0, @channels, width, height, 0, @format, @type, data 86 | return @ 87 | 88 | setSize: (@width, @height) -> 89 | @gl.texImage2D @target, 0, @channels, width, height, 0, @format, @type, null 90 | return @ 91 | 92 | read: (dst=new Uint8Array(@width*@height*4)) -> 93 | if @fbo 94 | @fbo.bind() 95 | else 96 | @fbo = new Framebuffer(@gl).bind().color(@) 97 | 98 | @gl.readPixels 0, 0, @width, @height, @gl.RGBA, @gl.UNSIGNED_BYTE, dst 99 | @fbo.unbind() 100 | 101 | return dst 102 | 103 | toPNG: -> 104 | canvas = document.createElement 'canvas' 105 | canvas.height = @height 106 | canvas.width = @width 107 | ctx = canvas.getContext '2d' 108 | imgdata = ctx.createImageData @width, @height 109 | imgdata.data.set @read(), 0 110 | ctx.putImageData imgdata, 0, 0 111 | url = canvas.toDataURL 'image/png' 112 | data = atob(url.split(',')[1]) 113 | result = new Uint8Array(data.length) 114 | for i in [0...data.length] 115 | result[i] = data.charCodeAt i 116 | return result 117 | 118 | exports.Cubemap = class Cubemap extends Texture 119 | constructor: (@gl) -> 120 | super() 121 | @target = @gl.TEXTURE_CUBE_MAP 122 | 123 | @up = @gl.TEXTURE_CUBE_MAP_POSITIVE_Y 124 | @down = @gl.TEXTURE_CUBE_MAP_NEGATIVE_Y 125 | @right = @gl.TEXTURE_CUBE_MAP_POSITIVE_X 126 | @left = @gl.TEXTURE_CUBE_MAP_NEGATIVE_X 127 | @back = @gl.TEXTURE_CUBE_MAP_NEGATIVE_Z 128 | @front = @gl.TEXTURE_CUBE_MAP_POSITIVE_Z 129 | 130 | uploadSide: (name, image) -> 131 | @gl.texImage2D @gl['TEXTURE_CUBE_MAP_' + name], 0, @gl.RGBA, @gl.RGBA, @gl.UNSIGNED_BYTE, image 132 | 133 | upload: (folder, ext='jpg') -> 134 | @uploadSide 'POSITIVE_Y', folder.get "up.#{ext}" 135 | @uploadSide 'NEGATIVE_Y', folder.get "down.#{ext}" 136 | @uploadSide 'POSITIVE_X', folder.get "right.#{ext}" 137 | @uploadSide 'NEGATIVE_X', folder.get "left.#{ext}" 138 | @uploadSide 'POSITIVE_Z', folder.get "front.#{ext}" 139 | @uploadSide 'NEGATIVE_Z', folder.get "back.#{ext}" 140 | return @ 141 | 142 | setSize: (@size) -> 143 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_Y, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 144 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 145 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_X, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 146 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_X, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 147 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_POSITIVE_Z, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 148 | @gl.texImage2D @gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 0, @gl.RGBA, @size, @size, 0, @gl.RGBA, @gl.UNSIGNED_BYTE, null 149 | return @ 150 | -------------------------------------------------------------------------------- /license/bsd-license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Florian Boesch http://codeflow.org/ 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the name of the nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /license/mit-license: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Florian Boesch 2 | http://codeflow.com/ 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining 5 | a copy of this software and associated documentation files (the 6 | "Software"), to deal in the Software without restriction, including 7 | without limitation the rights to use, copy, modify, merge, publish, 8 | distribute, sublicense, and/or sell copies of the Software, and to 9 | permit persons to whom the Software is furnished to do so, subject to 10 | the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be 13 | included in all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /license/readme: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012, Florian Boesch http://codeflow.org/ 2 | 3 | Deferred Irradiance Volumes is licensed under any of the following licenses at your choosing: 4 | 5 | MIT: see mit-license 6 | GPL: see gplv*-license 7 | BSD: see bsd-license 8 | -------------------------------------------------------------------------------- /pack: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os, sys, struct, cjson, stat 4 | from os.path import join 5 | 6 | here = os.path.dirname(os.path.abspath(__file__)) 7 | 8 | global_shunts = ''' 9 | Function.prototype.property = function(prop, desc) { 10 | Object.defineProperty(this.prototype, prop, desc); 11 | }; 12 | ''' 13 | 14 | def packModules(path): 15 | result = '' 16 | template = ''' 17 | loader.define('%s', function(exports, require, get){ 18 | %s}); 19 | ''' 20 | fs_base = join(here, path) 21 | for path, dirs, names in os.walk(fs_base): 22 | relpath = path[len(fs_base):] 23 | if not relpath.startswith('/'): 24 | relpath = '/' + relpath 25 | 26 | for name in names: 27 | if name.endswith('.js'): 28 | packpath = join(relpath, name) 29 | fspath = join(path, name) 30 | data = open(fspath).read() 31 | module = template % (packpath, data) 32 | result += module 33 | return result 34 | 35 | def packCode(app, target, targetname): 36 | if not requireCodePack([app, 'extra', 'lib'], target, targetname): 37 | return 38 | 39 | print 'packing code: %s' % target 40 | result = '' 41 | result += global_shunts 42 | for path, dirs, names in os.walk(join(here, 'extra')): 43 | for name in names: 44 | if name.endswith('.js'): 45 | data = open(join(path, name)).read() 46 | result += '\n%s;\n' % data 47 | 48 | result += packModules(app) 49 | result += packModules('lib') 50 | 51 | result += '\n$(function(){loader.main()});' 52 | open(join(here, target, targetname), 'w').write(result) 53 | 54 | def requireCodePack(apps, target, targetname): 55 | target_path = join(here, target, targetname) 56 | if not os.path.exists(target_path): 57 | return True 58 | 59 | for app in apps: 60 | target_mtime = os.stat(target_path)[stat.ST_MTIME] 61 | for path, dirs, names in os.walk(join(here, app)): 62 | for name in names: 63 | if name.endswith('.js'): 64 | code_mtime = os.stat(join(path, name))[stat.ST_MTIME] 65 | if code_mtime > target_mtime: 66 | return True 67 | return False 68 | 69 | binary_types = [ 70 | 'png', 'jpg', 'ogg', 71 | 'indices', 'vertices', 'model', 'mesh', 'bones', 72 | ] 73 | text_types = ['txt', 'shader', 'shaderlib'] 74 | packtypes = binary_types + text_types + ['json'] 75 | 76 | def requireAssetPack(app, target, targetname): 77 | for app in [app, 'lib']: 78 | fs_base = join(here, app) 79 | target_path = join(here, target, targetname) 80 | if not os.path.exists(target_path): 81 | return True 82 | target_mtime = os.stat(target_path)[stat.ST_MTIME] 83 | 84 | for path, dirs, names in os.walk(fs_base): 85 | for name in names: 86 | ext = os.path.splitext(name)[1].lstrip('.') 87 | fspath = join(path, name) 88 | if ext in packtypes: 89 | asset_mtime = os.stat(fspath)[stat.ST_MTIME] 90 | if asset_mtime > target_mtime: 91 | return True 92 | return False 93 | 94 | def packAssets(app, target, targetname): 95 | if not requireAssetPack(app, target, targetname): 96 | return 97 | 98 | print 'packing assets: %s' % target 99 | chunks = '' 100 | meta = {} 101 | for app in [app, 'lib']: 102 | fs_base = join(here, app) 103 | for path, dirs, names in os.walk(fs_base): 104 | relpath = path[len(fs_base):] 105 | if not relpath.startswith('/'): 106 | relpath = '/' + relpath 107 | for name in names: 108 | ext = os.path.splitext(name)[1].lstrip('.') 109 | packpath = join(relpath, name) 110 | fspath = join(path, name) 111 | if ext in binary_types: 112 | data = open(fspath, 'rb').read() 113 | offset = len(chunks) 114 | size = len(data) 115 | chunks += data 116 | meta[packpath] = { 117 | 'offset': offset, 118 | 'size': size, 119 | } 120 | elif ext in text_types: 121 | meta[packpath] = open(fspath, 'r').read() 122 | elif ext == 'json': 123 | meta[packpath] = cjson.decode(open(fspath, 'r').read()) 124 | 125 | metadata = cjson.encode(meta) 126 | metasize = struct.pack('I', len(metadata)) 127 | open(join(here, target, targetname), 'wb').write('PACK'+metasize+metadata+chunks) 128 | 129 | def pack(app, target, codename=None, assetname=None): 130 | if codename: 131 | packCode(app, target, codename) 132 | if assetname: 133 | packAssets(app, target, assetname) 134 | 135 | if __name__ == '__main__': 136 | pack('src', 'www', 'code.js', 'assets.pack') 137 | -------------------------------------------------------------------------------- /src/albedo.shader: -------------------------------------------------------------------------------- 1 | varying vec2 vTexcoord; 2 | varying vec3 vPosition, vViewPosition, vNormal; 3 | uniform mat4 proj, view; 4 | uniform mat3 view_rot; 5 | 6 | vertex: 7 | attribute vec3 position, normal; 8 | attribute vec2 texcoord; 9 | 10 | void main(){ 11 | vTexcoord = texcoord; 12 | vPosition = position; 13 | vNormal = normal; 14 | vViewPosition = (view * vec4(position, 1.0)).xyz; 15 | 16 | gl_Position = proj * view * vec4(position, 1.0); 17 | } 18 | 19 | fragment: 20 | uniform sampler2D diffuse_texture; 21 | uniform float specularity; 22 | uniform vec3 diffuse_color; 23 | uniform float gamma; 24 | void main(){ 25 | vec3 diffuse = pow(texture2D(diffuse_texture, vTexcoord).rgb, vec3(gamma)); 26 | gl_FragColor = vec4(diffuse*pow(diffuse_color, vec3(gamma)), 1.0); 27 | } 28 | -------------------------------------------------------------------------------- /src/antialias/fxaa2_0.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D source; 10 | uniform vec2 viewport; 11 | 12 | #define FXAA_REDUCE_MIN (1.0/ 128.0) 13 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 14 | #define FXAA_SPAN_MAX 8.0 15 | 16 | vec4 fxaa(vec2 fragCoord, sampler2D tex) 17 | { 18 | vec4 color; 19 | vec2 inverseVP = vec2(1.0 / viewport.x, 1.0 / viewport.y); 20 | vec3 rgbNW = texture2D(tex, (fragCoord + vec2(-1.0, -1.0)) * inverseVP).xyz; 21 | vec3 rgbNE = texture2D(tex, (fragCoord + vec2(1.0, -1.0)) * inverseVP).xyz; 22 | vec3 rgbSW = texture2D(tex, (fragCoord + vec2(-1.0, 1.0)) * inverseVP).xyz; 23 | vec3 rgbSE = texture2D(tex, (fragCoord + vec2(1.0, 1.0)) * inverseVP).xyz; 24 | vec3 rgbM = texture2D(tex, fragCoord * inverseVP).xyz; 25 | vec3 luma = vec3(0.299, 0.587, 0.114); 26 | float lumaNW = dot(rgbNW, luma); 27 | float lumaNE = dot(rgbNE, luma); 28 | float lumaSW = dot(rgbSW, luma); 29 | float lumaSE = dot(rgbSE, luma); 30 | float lumaM = dot(rgbM, luma); 31 | float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); 32 | float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); 33 | 34 | vec2 dir; 35 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 36 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 37 | 38 | float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * 39 | (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); 40 | 41 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 42 | dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), 43 | max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), 44 | dir * rcpDirMin)) * inverseVP; 45 | 46 | vec3 rgbA = 0.5 * ( 47 | texture2D(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz + 48 | texture2D(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); 49 | vec3 rgbB = rgbA * 0.5 + 0.25 * ( 50 | texture2D(tex, fragCoord * inverseVP + dir * -0.5).xyz + 51 | texture2D(tex, fragCoord * inverseVP + dir * 0.5).xyz); 52 | 53 | float lumaB = dot(rgbB, luma); 54 | if ((lumaB < lumaMin) || (lumaB > lumaMax)) 55 | color = vec4(rgbA, 1.0); 56 | else 57 | color = vec4(rgbB, 1.0); 58 | return color; 59 | } 60 | 61 | void main(){ 62 | gl_FragColor = vec4(fxaa(gl_FragCoord.xy, source).rgb, 1.0); 63 | } 64 | -------------------------------------------------------------------------------- /src/antialias/fxaa3_11.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | #define FXAA_WebGL 1 10 | //#require fxaa3_11 11 | //#require fxaa3_11_stripped 12 | #require fxaa3_11_preprocessed 13 | 14 | uniform sampler2D source; 15 | uniform vec2 viewport; 16 | uniform float subpixel_aa, contrast_treshold, edge_treshold; 17 | 18 | void main(){ 19 | vec4 color = FxaaPixelShader( 20 | gl_FragCoord.xy/viewport, 21 | source, 22 | vec2(1.0)/viewport, 23 | subpixel_aa, 24 | contrast_treshold, 25 | edge_treshold 26 | ); 27 | gl_FragColor = vec4(color.rgb, 1.0); 28 | } 29 | 30 | -------------------------------------------------------------------------------- /src/antialias/fxaa3_11_preprocessed.shaderlib: -------------------------------------------------------------------------------- 1 | float FxaaLuma(vec4 rgba) { return rgba.y; } 2 | vec4 FxaaPixelShader( 3 | vec2 pos 4 | ,sampler2D tex 5 | ,vec2 fxaaQualityRcpFrame 6 | ,float fxaaQualitySubpix 7 | ,float fxaaQualityEdgeThreshold 8 | ,float fxaaQualityEdgeThresholdMin 9 | ) { 10 | vec2 posM; 11 | posM.x = pos.x; 12 | posM.y = pos.y; 13 | vec4 rgbyM = texture2D(tex, posM, 0.0); 14 | float lumaS = FxaaLuma(texture2D(tex, posM + (vec2( 0.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); 15 | float lumaE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)); 16 | float lumaN = FxaaLuma(texture2D(tex, posM + (vec2( 0.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); 17 | float lumaW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0, 0.0) * fxaaQualityRcpFrame.xy), 0.0)); 18 | 19 | float maxSM = max(lumaS, rgbyM.y); 20 | float minSM = min(lumaS, rgbyM.y); 21 | float maxESM = max(lumaE, maxSM); 22 | float minESM = min(lumaE, minSM); 23 | float maxWN = max(lumaN, lumaW); 24 | float minWN = min(lumaN, lumaW); 25 | float rangeMax = max(maxWN, maxESM); 26 | float rangeMin = min(minWN, minESM); 27 | float rangeMaxScaled = rangeMax * fxaaQualityEdgeThreshold; 28 | float range = rangeMax - rangeMin; 29 | float rangeMaxClamped = max(fxaaQualityEdgeThresholdMin, rangeMaxScaled); 30 | bool earlyExit = range < rangeMaxClamped; 31 | if(earlyExit) return rgbyM; 32 | float lumaNW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); 33 | float lumaSE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); 34 | float lumaNE = FxaaLuma(texture2D(tex, posM + (vec2( 1.0,-1.0) * fxaaQualityRcpFrame.xy), 0.0)); 35 | float lumaSW = FxaaLuma(texture2D(tex, posM + (vec2(-1.0, 1.0) * fxaaQualityRcpFrame.xy), 0.0)); 36 | 37 | float lumaNS = lumaN + lumaS; 38 | float lumaWE = lumaW + lumaE; 39 | float subpixRcpRange = 1.0/range; 40 | float subpixNSWE = lumaNS + lumaWE; 41 | float edgeHorz1 = (-2.0 * rgbyM.y) + lumaNS; 42 | float edgeVert1 = (-2.0 * rgbyM.y) + lumaWE; 43 | float lumaNESE = lumaNE + lumaSE; 44 | float lumaNWNE = lumaNW + lumaNE; 45 | float edgeHorz2 = (-2.0 * lumaE) + lumaNESE; 46 | float edgeVert2 = (-2.0 * lumaN) + lumaNWNE; 47 | float lumaNWSW = lumaNW + lumaSW; 48 | float lumaSWSE = lumaSW + lumaSE; 49 | float edgeHorz4 = (abs(edgeHorz1) * 2.0) + abs(edgeHorz2); 50 | float edgeVert4 = (abs(edgeVert1) * 2.0) + abs(edgeVert2); 51 | float edgeHorz3 = (-2.0 * lumaW) + lumaNWSW; 52 | float edgeVert3 = (-2.0 * lumaS) + lumaSWSE; 53 | float edgeHorz = abs(edgeHorz3) + edgeHorz4; 54 | float edgeVert = abs(edgeVert3) + edgeVert4; 55 | float subpixNWSWNESE = lumaNWSW + lumaNESE; 56 | float lengthSign = fxaaQualityRcpFrame.x; 57 | bool horzSpan = edgeHorz >= edgeVert; 58 | float subpixA = subpixNSWE * 2.0 + subpixNWSWNESE; 59 | if(!horzSpan) lumaN = lumaW; 60 | if(!horzSpan) lumaS = lumaE; 61 | if(horzSpan) lengthSign = fxaaQualityRcpFrame.y; 62 | float subpixB = (subpixA * (1.0/12.0)) - rgbyM.y; 63 | float gradientN = lumaN - rgbyM.y; 64 | float gradientS = lumaS - rgbyM.y; 65 | float lumaNN = lumaN + rgbyM.y; 66 | float lumaSS = lumaS + rgbyM.y; 67 | bool pairN = abs(gradientN) >= abs(gradientS); 68 | float gradient = max(abs(gradientN), abs(gradientS)); 69 | if(pairN) lengthSign = -lengthSign; 70 | float subpixC = clamp(abs(subpixB) * subpixRcpRange, 0.0, 1.0); 71 | vec2 posB; 72 | posB.x = posM.x; 73 | posB.y = posM.y; 74 | vec2 offNP; 75 | offNP.x = (!horzSpan) ? 0.0 : fxaaQualityRcpFrame.x; 76 | offNP.y = ( horzSpan) ? 0.0 : fxaaQualityRcpFrame.y; 77 | if(!horzSpan) posB.x += lengthSign * 0.5; 78 | if( horzSpan) posB.y += lengthSign * 0.5; 79 | vec2 posN; 80 | posN.x = posB.x - offNP.x * 1.0; 81 | posN.y = posB.y - offNP.y * 1.0; 82 | vec2 posP; 83 | posP.x = posB.x + offNP.x * 1.0; 84 | posP.y = posB.y + offNP.y * 1.0; 85 | float subpixD = ((-2.0)*subpixC) + 3.0; 86 | float lumaEndN = FxaaLuma(texture2D(tex, posN, 0.0)); 87 | float subpixE = subpixC * subpixC; 88 | float lumaEndP = FxaaLuma(texture2D(tex, posP, 0.0)); 89 | if(!pairN) lumaNN = lumaSS; 90 | float gradientScaled = gradient * 1.0/4.0; 91 | float lumaMM = rgbyM.y - lumaNN * 0.5; 92 | float subpixF = subpixD * subpixE; 93 | bool lumaMLTZero = lumaMM < 0.0; 94 | lumaEndN -= lumaNN * 0.5; 95 | lumaEndP -= lumaNN * 0.5; 96 | bool doneN = abs(lumaEndN) >= gradientScaled; 97 | bool doneP = abs(lumaEndP) >= gradientScaled; 98 | if(!doneN) posN.x -= offNP.x * 1.5; 99 | if(!doneN) posN.y -= offNP.y * 1.5; 100 | bool doneNP = (!doneN) || (!doneP); 101 | if(!doneP) posP.x += offNP.x * 1.5; 102 | if(!doneP) posP.y += offNP.y * 1.5; 103 | if(doneNP) { 104 | if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); 105 | if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); 106 | if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; 107 | if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; 108 | doneN = abs(lumaEndN) >= gradientScaled; 109 | doneP = abs(lumaEndP) >= gradientScaled; 110 | if(!doneN) posN.x -= offNP.x * 2.0; 111 | if(!doneN) posN.y -= offNP.y * 2.0; 112 | doneNP = (!doneN) || (!doneP); 113 | if(!doneP) posP.x += offNP.x * 2.0; 114 | if(!doneP) posP.y += offNP.y * 2.0; 115 | 116 | if(doneNP) { 117 | if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); 118 | if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); 119 | if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; 120 | if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; 121 | doneN = abs(lumaEndN) >= gradientScaled; 122 | doneP = abs(lumaEndP) >= gradientScaled; 123 | if(!doneN) posN.x -= offNP.x * 4.0; 124 | if(!doneN) posN.y -= offNP.y * 4.0; 125 | doneNP = (!doneN) || (!doneP); 126 | if(!doneP) posP.x += offNP.x * 4.0; 127 | if(!doneP) posP.y += offNP.y * 4.0; 128 | 129 | if(doneNP) { 130 | if(!doneN) lumaEndN = FxaaLuma(texture2D(tex, posN.xy, 0.0)); 131 | if(!doneP) lumaEndP = FxaaLuma(texture2D(tex, posP.xy, 0.0)); 132 | if(!doneN) lumaEndN = lumaEndN - lumaNN * 0.5; 133 | if(!doneP) lumaEndP = lumaEndP - lumaNN * 0.5; 134 | doneN = abs(lumaEndN) >= gradientScaled; 135 | doneP = abs(lumaEndP) >= gradientScaled; 136 | if(!doneN) posN.x -= offNP.x * 12.0; 137 | if(!doneN) posN.y -= offNP.y * 12.0; 138 | doneNP = (!doneN) || (!doneP); 139 | if(!doneP) posP.x += offNP.x * 12.0; 140 | if(!doneP) posP.y += offNP.y * 12.0; 141 | } 142 | 143 | } 144 | 145 | } 146 | float dstN = posM.x - posN.x; 147 | float dstP = posP.x - posM.x; 148 | if(!horzSpan) dstN = posM.y - posN.y; 149 | if(!horzSpan) dstP = posP.y - posM.y; 150 | bool goodSpanN = (lumaEndN < 0.0) != lumaMLTZero; 151 | float spanLength = (dstP + dstN); 152 | bool goodSpanP = (lumaEndP < 0.0) != lumaMLTZero; 153 | float spanLengthRcp = 1.0/spanLength; 154 | bool directionN = dstN < dstP; 155 | float dst = min(dstN, dstP); 156 | bool goodSpan = directionN ? goodSpanN : goodSpanP; 157 | float subpixG = subpixF * subpixF; 158 | float pixelOffset = (dst * (-spanLengthRcp)) + 0.5; 159 | float subpixH = subpixG * fxaaQualitySubpix; 160 | float pixelOffsetGood = goodSpan ? pixelOffset : 0.0; 161 | float pixelOffsetSubpix = max(pixelOffsetGood, subpixH); 162 | if(!horzSpan) posM.x += pixelOffsetSubpix * lengthSign; 163 | if( horzSpan) posM.y += pixelOffsetSubpix * lengthSign; 164 | return vec4(texture2D(tex, posM, 0.0).xyz, rgbyM.y); 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/antialias/module.coffee: -------------------------------------------------------------------------------- 1 | Rendernode = require '/rendernode' 2 | Quad = require '/webgl/quad' 3 | 4 | return class AntiAlias 5 | constructor: (@gl, gui, @source) -> 6 | gui.remember @ 7 | @node = new Rendernode @gl, 8 | #program: get 'fxaa.shader' 9 | program: get 'fxaa3_11.shader' 10 | drawable: quad 11 | 12 | @subpixel_aa = 0.75 13 | @contrast_treshold = 0.166 14 | @edge_treshold = 0.0 15 | folder = gui.addFolder('Antialias') 16 | folder.add(@, 'subpixel_aa', 0.0, 1.0).name('Subpixel aa') 17 | folder.add(@, 'contrast_treshold', 0.063, 0.333).name('Contrast Treshold') 18 | folder.add(@, 'edge_treshold', 0.0, 0.0833).name('Edge Treshold') 19 | 20 | apply: -> 21 | @node.start() 22 | .f('subpixel_aa', @subpixel_aa) 23 | .f('contrast_treshold', @contrast_treshold) 24 | .f('edge_treshold', @edge_treshold) 25 | .clear() 26 | .sampler('source', @source) 27 | .draw() 28 | .end() 29 | 30 | resize: (width, height) -> 31 | @node.resize width, height 32 | -------------------------------------------------------------------------------- /src/antialias/module.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var AntiAlias, Quad, Rendernode; 3 | 4 | Rendernode = require('/rendernode'); 5 | 6 | Quad = require('/webgl/quad'); 7 | 8 | return AntiAlias = (function() { 9 | 10 | function AntiAlias(gl, gui, source) { 11 | var folder; 12 | this.gl = gl; 13 | this.source = source; 14 | gui.remember(this); 15 | this.node = new Rendernode(this.gl, { 16 | program: get('fxaa3_11.shader'), 17 | drawable: quad 18 | }); 19 | this.subpixel_aa = 0.75; 20 | this.contrast_treshold = 0.166; 21 | this.edge_treshold = 0.0; 22 | folder = gui.addFolder('Antialias'); 23 | folder.add(this, 'subpixel_aa', 0.0, 1.0).name('Subpixel aa'); 24 | folder.add(this, 'contrast_treshold', 0.063, 0.333).name('Contrast Treshold'); 25 | folder.add(this, 'edge_treshold', 0.0, 0.0833).name('Edge Treshold'); 26 | } 27 | 28 | AntiAlias.prototype.apply = function() { 29 | return this.node.start().f('subpixel_aa', this.subpixel_aa).f('contrast_treshold', this.contrast_treshold).f('edge_treshold', this.edge_treshold).clear().sampler('source', this.source).draw().end(); 30 | }; 31 | 32 | AntiAlias.prototype.resize = function(width, height) { 33 | return this.node.resize(width, height); 34 | }; 35 | 36 | return AntiAlias; 37 | 38 | })(); 39 | -------------------------------------------------------------------------------- /src/blur/blur.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D source; 10 | uniform vec2 viewport; 11 | 12 | void main(){ 13 | vec4 sum = vec4(0.0); 14 | float divider = 0.0; 15 | for(float x=-2.0; x<=2.0; x++){ 16 | for(float y=-2.0; y<=2.0; y++){ 17 | vec2 coord = vec2(x,y); 18 | float l = length(coord)+1.0; 19 | float factor = 1.0/l; 20 | divider += factor; 21 | sum += texture2D(source, (gl_FragCoord.xy+vec2(x,y))/viewport) * factor; 22 | } 23 | } 24 | gl_FragColor = sum/divider; 25 | } 26 | -------------------------------------------------------------------------------- /src/blur/module.coffee: -------------------------------------------------------------------------------- 1 | Rendernode = require '/rendernode' 2 | 3 | return class Blur 4 | constructor: (gl, {width, height, type, filter}) -> 5 | type ?= gl.UNSIGNED_BYTE 6 | filter ?= 'linear' 7 | 8 | @output = new Rendernode gl, 9 | width: width 10 | height: height 11 | program: get 'blur.shader' 12 | drawable: quad 13 | filter: filter 14 | type: type 15 | 16 | update: (source) -> 17 | @output 18 | .start() 19 | .sampler('source', source) 20 | .draw() 21 | .end() 22 | 23 | resize: (width, height) -> 24 | @output.resize(width, height) 25 | -------------------------------------------------------------------------------- /src/blur/module.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Blur, Rendernode; 3 | 4 | Rendernode = require('/rendernode'); 5 | 6 | return Blur = (function() { 7 | 8 | function Blur(gl, _arg) { 9 | var filter, height, type, width; 10 | width = _arg.width, height = _arg.height, type = _arg.type, filter = _arg.filter; 11 | if (type == null) { 12 | type = gl.UNSIGNED_BYTE; 13 | } 14 | if (filter == null) { 15 | filter = 'linear'; 16 | } 17 | this.output = new Rendernode(gl, { 18 | width: width, 19 | height: height, 20 | program: get('blur.shader'), 21 | drawable: quad, 22 | filter: filter, 23 | type: type 24 | }); 25 | } 26 | 27 | Blur.prototype.update = function(source) { 28 | return this.output.start().sampler('source', source).draw().end(); 29 | }; 30 | 31 | Blur.prototype.resize = function(width, height) { 32 | return this.output.resize(width, height); 33 | }; 34 | 35 | return Blur; 36 | 37 | })(); 38 | -------------------------------------------------------------------------------- /src/composit.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D global, direct, albedo, debug, ssao; 10 | uniform vec2 viewport; 11 | uniform float gamma, brightness, saturation; 12 | uniform float probe_factor, di_factor, gi_factor, ao_factor; 13 | uniform vec3 sky_radiance, sun_radiance; 14 | 15 | void main(){ 16 | float occlusion = mix(1.0, texture2D(ssao, gl_FragCoord.xy/viewport).r, ao_factor); 17 | 18 | vec4 global_data = texture2D(global, gl_FragCoord.xy/viewport); 19 | vec3 global_irradiance = max(global_data.rgb/global_data.a, vec3(0.0)); 20 | 21 | vec3 direct_irradiance = texture2D(direct, gl_FragCoord.xy/viewport).rgb * sun_radiance; 22 | vec4 diffuse_color = texture2D(albedo, gl_FragCoord.xy/viewport); 23 | 24 | vec3 irradiance = (global_irradiance*gi_factor + direct_irradiance*di_factor)*occlusion; 25 | vec3 excident = mix(sky_radiance, diffuse_color.rgb*irradiance, diffuse_color.a); 26 | 27 | vec4 debug_data = texture2D(debug, gl_FragCoord.xy/viewport); 28 | vec3 color = mix(excident, debug_data.rgb, debug_data.a*probe_factor); 29 | 30 | color = brightness * color; 31 | 32 | vec3 luma_coeff = vec3(0.2125, 0.7154, 0.0721); 33 | vec3 intensity = vec3(dot(color, luma_coeff)); 34 | color = mix(intensity, color, saturation); 35 | 36 | gl_FragColor = vec4(clamp(pow(color, vec3(1.0/gamma)), vec3(0.0), vec3(1.0)), 1.0); 37 | } 38 | -------------------------------------------------------------------------------- /src/deferred_model.coffee: -------------------------------------------------------------------------------- 1 | Sphere = require '/webgl/sphere' 2 | 3 | return class DeferredModel extends require('webgl/drawable') 4 | attribs: ['position', 'lightprobe', 'center'] 5 | pointers: [ 6 | {name: 'position', size: 3, offset: 0, stride: 7}, 7 | {name: 'lightprobe', size: 4, offset: 3, stride: 7}, 8 | ] 9 | constructor: (@gl, probes) -> 10 | super() 11 | template = Sphere.makeVertices(5.1, 2) 12 | 13 | buffer = [] 14 | for probe, i in probes 15 | px = probe.x 16 | py = probe.y 17 | pz = probe.z 18 | for vi in [0...template.length] by 3 19 | x = template[vi] 20 | y = template[vi+1] 21 | z = template[vi+2] 22 | buffer.push(x,y,z,px,py,pz,i) 23 | 24 | @size = buffer.length/7 25 | @uploadList buffer 26 | -------------------------------------------------------------------------------- /src/deferred_model.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var DeferredModel, Sphere, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | Sphere = require('/webgl/sphere'); 7 | 8 | return DeferredModel = (function(_super) { 9 | 10 | __extends(DeferredModel, _super); 11 | 12 | DeferredModel.prototype.attribs = ['position', 'lightprobe', 'center']; 13 | 14 | DeferredModel.prototype.pointers = [ 15 | { 16 | name: 'position', 17 | size: 3, 18 | offset: 0, 19 | stride: 7 20 | }, { 21 | name: 'lightprobe', 22 | size: 4, 23 | offset: 3, 24 | stride: 7 25 | } 26 | ]; 27 | 28 | function DeferredModel(gl, probes) { 29 | var buffer, i, probe, px, py, pz, template, vi, x, y, z, _i, _j, _len, _ref; 30 | this.gl = gl; 31 | DeferredModel.__super__.constructor.call(this); 32 | template = Sphere.makeVertices(5.1, 2); 33 | buffer = []; 34 | for (i = _i = 0, _len = probes.length; _i < _len; i = ++_i) { 35 | probe = probes[i]; 36 | px = probe.x; 37 | py = probe.y; 38 | pz = probe.z; 39 | for (vi = _j = 0, _ref = template.length; _j < _ref; vi = _j += 3) { 40 | x = template[vi]; 41 | y = template[vi + 1]; 42 | z = template[vi + 2]; 43 | buffer.push(x, y, z, px, py, pz, i); 44 | } 45 | } 46 | this.size = buffer.length / 7; 47 | this.uploadList(buffer); 48 | } 49 | 50 | return DeferredModel; 51 | 52 | })(require('webgl/drawable')); 53 | -------------------------------------------------------------------------------- /src/depth/deferred_shadow_map.shader: -------------------------------------------------------------------------------- 1 | varying vec2 clip; 2 | 3 | vertex: 4 | attribute vec2 position; 5 | 6 | void main(){ 7 | clip = position; 8 | gl_Position = vec4(position, 0.0, 1.0); 9 | } 10 | 11 | fragment: 12 | #require variance 13 | uniform sampler2D eye_normaldepth; 14 | uniform mat4 inv_eye_proj, inv_eye_view; 15 | uniform vec2 viewport; 16 | 17 | void main(){ 18 | vec4 eye_data = texture2D(eye_normaldepth, gl_FragCoord.xy/viewport); 19 | vec3 normal = eye_data.xyz; 20 | float depth = eye_data.w; 21 | vec4 device_pos = inv_eye_proj * vec4(clip, 1.0, 1.0); 22 | vec3 eye_normal = normalize(device_pos.xyz); 23 | vec3 eye_pos = depth * eye_normal; 24 | vec3 position = (inv_eye_view * vec4(eye_pos, 1.0)).xyz; 25 | 26 | gl_FragColor = vec4(vec3(getIntensity(position, normal)), 1.0); 27 | } 28 | -------------------------------------------------------------------------------- /src/depth/depth.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vViewPosition; 2 | uniform mat4 proj, view; 3 | 4 | vertex: 5 | attribute vec3 position; 6 | 7 | void main(){ 8 | vViewPosition = (view * vec4(position, 1.0)).xyz; 9 | gl_Position = proj * view * vec4(position, 1.0); 10 | } 11 | 12 | fragment: 13 | #extension GL_OES_standard_derivatives : enable 14 | uniform float range; 15 | 16 | void main(){ 17 | float z = -vViewPosition.z/range; 18 | float dx = dFdx(z); 19 | float dy = dFdy(z); 20 | gl_FragColor = vec4(z, z*z + 0.25*(dx*dx + dy*dy), 0.0, 1.0); 21 | } 22 | -------------------------------------------------------------------------------- /src/depth/lightmap_shadow_map.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vPosition, vNormal; 2 | 3 | vertex: 4 | attribute vec3 position, normal; 5 | attribute vec2 texcoord; 6 | 7 | void main(){ 8 | vPosition = position; 9 | vNormal = normal; 10 | gl_Position = vec4(texcoord*2.0-1.0, 0.0, 1.0); 11 | } 12 | 13 | fragment: 14 | #require variance 15 | 16 | void main(){ 17 | gl_FragColor = vec4(vec3(getIntensity(vPosition, normalize(vNormal))), 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /src/depth/module.coffee: -------------------------------------------------------------------------------- 1 | Rendernode = require '/rendernode' 2 | Blur = require '/blur' 3 | 4 | exports.DepthRender = class DepthRender 5 | constructor: (gl, width, height, drawable, {blurred}={}) -> 6 | blurred ?= false 7 | 8 | floatExt = gl.getFloatExtension require: ['renderable', 'filterable'] 9 | 10 | @direct = new Rendernode gl, 11 | width: width 12 | height: height 13 | program: get 'depth.shader' 14 | drawable: drawable 15 | depthBuffer: true 16 | depthTest: true 17 | depthWrite: true 18 | filter: if blurred then 'nearest' else 'linear' 19 | #type: gl.FLOAT #float is required because of depth precision 20 | type: floatExt.type 21 | cullFace: 'BACK' 22 | 23 | if blurred 24 | @blurred = new Blur gl, 25 | width: width 26 | height: height 27 | #type: gl.FLOAT 28 | type: floatExt.type 29 | 30 | @output = if @blurred then @blurred.output else @direct 31 | 32 | update: (proj, view) -> 33 | @direct.start() 34 | .clearBoth(0,0,0,1) 35 | .mat4('proj', proj) 36 | .mat4('view', view) 37 | .f('range', 42) #FIXME 38 | .draw() 39 | .end() 40 | 41 | if @blurred 42 | @blurred.update(@direct) 43 | 44 | exports.DeferredShadowMap = class DeferredShadowMap 45 | constructor: (gl, {drawable, depthWidth, depthHeight, @eyeNormaldepth, @light, @camera, blurred}) -> 46 | @depth = new DepthRender gl, depthWidth, depthHeight, drawable, blurred:blurred 47 | @output = new Rendernode gl, 48 | program: get 'deferred_shadow_map.shader' 49 | drawable: quad 50 | 51 | @updateDepth() 52 | 53 | resize: (width, height) -> 54 | @output.resize width, height 55 | 56 | updateDepth: -> 57 | @depth.update @light.proj, @light.view 58 | 59 | updateShadow: -> 60 | @output 61 | .start() 62 | .clear(1, 0, 1) 63 | .sampler('eye_normaldepth', @eyeNormaldepth) 64 | .sampler('light_depth', @depth.output) 65 | .mat4('inv_eye_proj', @camera.inv_proj) 66 | .mat4('inv_eye_view', @camera.inv_view) 67 | .mat4('light_view', @light.view) 68 | .mat4('light_proj', @light.proj) 69 | .mat3('light_rot', @light.rot) 70 | .draw() 71 | .end() 72 | 73 | exports.LightmapShadowMap = class LightmappedShadowMap 74 | constructor: (gl, {drawable, depthWidth, depthHeight, lightmapSize, @light, blurred}) -> 75 | @depth = new DepthRender gl, depthWidth, depthHeight, drawable, blurred:blurred 76 | 77 | lightmapSize ?= 256 78 | 79 | @output = new Rendernode gl, 80 | width: lightmapSize 81 | height: lightmapSize 82 | program: get 'lightmap_shadow_map.shader' 83 | drawable: drawable 84 | 85 | @update() 86 | 87 | update: -> 88 | @depth.update @light.proj, @light.view 89 | @output 90 | .start() 91 | .sampler('light_depth', @depth.output) 92 | .mat4('light_view', @light.view) 93 | .mat4('light_proj', @light.proj) 94 | .mat3('light_rot', @light.rot) 95 | .draw() 96 | .end() 97 | -------------------------------------------------------------------------------- /src/depth/module.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Blur, DeferredShadowMap, DepthRender, LightmappedShadowMap, Rendernode; 3 | 4 | Rendernode = require('/rendernode'); 5 | 6 | Blur = require('/blur'); 7 | 8 | exports.DepthRender = DepthRender = (function() { 9 | 10 | function DepthRender(gl, width, height, drawable, _arg) { 11 | var blurred, floatExt; 12 | blurred = (_arg != null ? _arg : {}).blurred; 13 | if (blurred == null) { 14 | blurred = false; 15 | } 16 | floatExt = gl.getFloatExtension({ 17 | require: ['renderable', 'filterable'] 18 | }); 19 | this.direct = new Rendernode(gl, { 20 | width: width, 21 | height: height, 22 | program: get('depth.shader'), 23 | drawable: drawable, 24 | depthBuffer: true, 25 | depthTest: true, 26 | depthWrite: true, 27 | filter: blurred ? 'nearest' : 'linear', 28 | type: floatExt.type, 29 | cullFace: 'BACK' 30 | }); 31 | if (blurred) { 32 | this.blurred = new Blur(gl, { 33 | width: width, 34 | height: height, 35 | type: floatExt.type 36 | }); 37 | } 38 | this.output = this.blurred ? this.blurred.output : this.direct; 39 | } 40 | 41 | DepthRender.prototype.update = function(proj, view) { 42 | this.direct.start().clearBoth(0, 0, 0, 1).mat4('proj', proj).mat4('view', view).f('range', 42).draw().end(); 43 | if (this.blurred) { 44 | return this.blurred.update(this.direct); 45 | } 46 | }; 47 | 48 | return DepthRender; 49 | 50 | })(); 51 | 52 | exports.DeferredShadowMap = DeferredShadowMap = (function() { 53 | 54 | function DeferredShadowMap(gl, _arg) { 55 | var blurred, depthHeight, depthWidth, drawable; 56 | drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, this.eyeNormaldepth = _arg.eyeNormaldepth, this.light = _arg.light, this.camera = _arg.camera, blurred = _arg.blurred; 57 | this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { 58 | blurred: blurred 59 | }); 60 | this.output = new Rendernode(gl, { 61 | program: get('deferred_shadow_map.shader'), 62 | drawable: quad 63 | }); 64 | this.updateDepth(); 65 | } 66 | 67 | DeferredShadowMap.prototype.resize = function(width, height) { 68 | return this.output.resize(width, height); 69 | }; 70 | 71 | DeferredShadowMap.prototype.updateDepth = function() { 72 | return this.depth.update(this.light.proj, this.light.view); 73 | }; 74 | 75 | DeferredShadowMap.prototype.updateShadow = function() { 76 | return this.output.start().clear(1, 0, 1).sampler('eye_normaldepth', this.eyeNormaldepth).sampler('light_depth', this.depth.output).mat4('inv_eye_proj', this.camera.inv_proj).mat4('inv_eye_view', this.camera.inv_view).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); 77 | }; 78 | 79 | return DeferredShadowMap; 80 | 81 | })(); 82 | 83 | exports.LightmapShadowMap = LightmappedShadowMap = (function() { 84 | 85 | function LightmappedShadowMap(gl, _arg) { 86 | var blurred, depthHeight, depthWidth, drawable, lightmapSize; 87 | drawable = _arg.drawable, depthWidth = _arg.depthWidth, depthHeight = _arg.depthHeight, lightmapSize = _arg.lightmapSize, this.light = _arg.light, blurred = _arg.blurred; 88 | this.depth = new DepthRender(gl, depthWidth, depthHeight, drawable, { 89 | blurred: blurred 90 | }); 91 | if (lightmapSize == null) { 92 | lightmapSize = 256; 93 | } 94 | this.output = new Rendernode(gl, { 95 | width: lightmapSize, 96 | height: lightmapSize, 97 | program: get('lightmap_shadow_map.shader'), 98 | drawable: drawable 99 | }); 100 | this.update(); 101 | } 102 | 103 | LightmappedShadowMap.prototype.update = function() { 104 | this.depth.update(this.light.proj, this.light.view); 105 | return this.output.start().sampler('light_depth', this.depth.output).mat4('light_view', this.light.view).mat4('light_proj', this.light.proj).mat3('light_rot', this.light.rot).draw().end(); 106 | }; 107 | 108 | return LightmappedShadowMap; 109 | 110 | })(); 111 | -------------------------------------------------------------------------------- /src/depth/variance.shaderlib: -------------------------------------------------------------------------------- 1 | uniform sampler2D light_depth; 2 | uniform mat4 light_view, light_proj; 3 | uniform mat3 light_rot; 4 | 5 | float getOcclusion(vec2 uv, float z){ 6 | vec2 moments = texture2D(light_depth, uv).xy; 7 | float p = smoothstep(z-0.02, z-0.01, moments.x); 8 | float variance = moments.y - moments.x*moments.x; 9 | float d = z - moments.x; 10 | float p_max = variance/(variance+d*d); 11 | p_max = smoothstep(0.4, 1.0, p_max); 12 | return max(p, p_max); 13 | } 14 | 15 | float getIntensity(vec3 position, vec3 normal){ 16 | vec4 light_view_position = light_view * vec4(position, 1.0); 17 | vec4 light_device = light_proj * light_view_position; 18 | vec2 light_clip = light_device.xy/light_device.w; 19 | vec2 uv = light_clip*0.5+0.5; 20 | float z = -light_view_position.z/42.0; 21 | 22 | float occlusion = getOcclusion(uv, z); 23 | float lambert = clamp((light_rot * normal).z, 0.0, 1.0); 24 | return occlusion * lambert; 25 | } 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /src/direct_illumination.shader: -------------------------------------------------------------------------------- 1 | varying vec2 clip; 2 | 3 | vertex: 4 | attribute vec2 position; 5 | 6 | void main(){ 7 | clip = position; 8 | gl_Position = vec4(position, 0.0, 1.0); 9 | } 10 | 11 | fragment: 12 | uniform sampler2D sun_normaldepth, eye_normaldepth; 13 | uniform float eye_near, eye_far, sun_near, sun_far; 14 | uniform mat4 inv_eye_proj, inv_eye_view, sun_view, sun_proj; 15 | uniform mat3 sun_rot, inv_eye_rot; 16 | uniform vec2 sun_normaldepth_size; 17 | uniform vec2 viewport; 18 | uniform float epsilon, shadow_bias, shadow_distance; 19 | 20 | vec2 getMoments(vec2 uv, float x, float y){ 21 | vec4 sun_data = texture2D(sun_normaldepth, uv + vec2(x,y)/sun_normaldepth_size); 22 | return sun_data.rg; 23 | } 24 | float linstep(float low, float high, float v){ 25 | return clamp((v-low)/(high-low), 0.0, 1.0); 26 | } 27 | 28 | float occlusionFun(vec2 moments, float z){ 29 | float p = smoothstep(z-0.02, z-0.01, moments.x); 30 | float variance = moments.y - moments.x*moments.x; 31 | float d = z - moments.x; 32 | float p_max = variance/(variance+d*d); 33 | //p_max = linstep(0.4, 1.0, p_max); 34 | p_max = smoothstep(0.4, 1.0, p_max); 35 | return max(p, p_max); 36 | } 37 | 38 | float getOcclusion(vec2 uv, float z, float x, float y){ 39 | vec2 moments = getMoments(uv, x, y); 40 | return occlusionFun(moments, z); 41 | } 42 | 43 | void main(){ 44 | vec4 eye_data = texture2D(eye_normaldepth, gl_FragCoord.xy/viewport); 45 | vec3 normal = eye_data.xyz; 46 | float depth = eye_data.w; 47 | vec4 device_pos = inv_eye_proj * vec4(clip, 1.0, 1.0); 48 | vec3 eye_normal = normalize(device_pos.xyz); 49 | vec3 eye_pos = depth * eye_normal; 50 | vec3 position = (inv_eye_view * vec4(eye_pos, 1.0)).xyz; 51 | 52 | vec4 sun_view_position = sun_view * vec4(position, 1.0); 53 | vec4 sun_device = sun_proj * sun_view_position; 54 | vec2 sun_clip = sun_device.xy/sun_device.w; 55 | vec2 uv = sun_clip*0.5+0.5; 56 | float z = -sun_view_position.z/42.0; 57 | 58 | //float occlusion = getOcclusion(uv, z, 0.0, 0.0); 59 | float occlusion = getOcclusion(uv, z, 0.0, 0.0); 60 | float lambert = clamp((sun_rot * normal).z, 0.0, 1.0); 61 | //float intensity = clamp(min(occlusion, lambert), 0.0, 1.0); 62 | float intensity = occlusion * lambert; 63 | //float intensity = (1.0 - occlusion)*lambert; 64 | vec3 color = intensity * vec3(1.0, 0.95, 1.0); 65 | gl_FragColor = vec4(color, 1.0); 66 | //gl_FragColor = vec4(vec3(occlusion), 1.0); 67 | } 68 | -------------------------------------------------------------------------------- /src/dist3d.coffee: -------------------------------------------------------------------------------- 1 | vsub = (p1, p2) -> 2 | return [ 3 | p1[0] - p2[0], 4 | p1[1] - p2[1], 5 | p1[2] - p2[2], 6 | ] 7 | 8 | vadd = (p1, p2) -> 9 | return [ 10 | p1[0] + p2[0], 11 | p1[1] + p2[1], 12 | p1[2] + p2[2], 13 | ] 14 | 15 | sadd = (s, p) -> 16 | x=p[0]; y=p[1]; z=p[2] 17 | return [x+s, y+s, z+s] 18 | 19 | slength = (p) -> 20 | x=p[0]; y=p[1]; z=p[2] 21 | return x*x + y*y + z*z 22 | 23 | length = (p) -> 24 | return Math.sqrt(slength(p)) 25 | 26 | dot = (p1, p2) -> 27 | x1=p1[0]; y1=p1[1]; z1=p1[2] 28 | x2=p2[0]; y2=p2[1]; z2=p2[2] 29 | return x1*x2 + y1*y2 + z1*z2 30 | 31 | smul = (s, p) -> 32 | x=p[0]; y=p[1]; z=p[2] 33 | return [x*s, y*s, z*s] 34 | 35 | cross = (p1, p2) -> 36 | x1=p1[0]; y1=p1[1]; z1=p1[2] 37 | x2=p2[0]; y2=p2[1]; z2=p2[2] 38 | 39 | return [ 40 | y1*z2 - z1*y2, 41 | z1*x2 - x1*z2, 42 | x1*y2 - y1*x2, 43 | ] 44 | 45 | exports.closestPointTriangle = (p, a, b, c) -> 46 | ab = vsub(b, a) 47 | ac = vsub(c, a) 48 | bc = vsub(c, b) 49 | 50 | snom = dot(vsub(p,a), ab); sdenom = dot(vsub(p,b), vsub(a,b)) 51 | tnom = dot(vsub(p, a), ac); tdenom = dot(vsub(p,c), vsub(a,c)) 52 | if snom <= 0 and tnom <= 0 then return a 53 | 54 | unom = dot(vsub(p,b), bc); udenom = dot(vsub(p,c), vsub(b,c)) 55 | if sdenom <= 0 and unom <= 0 then return b 56 | if tdenom <= 0 and udenom <= 0 then return c 57 | 58 | n = cross(vsub(b,a), vsub(c,a)) 59 | vc = dot(n, cross(vsub(a,p), vsub(b,p))) 60 | if vc <= 0 and snom >= 0 and sdenom >= 0 61 | return vadd(a, smul(snom/(snom + sdenom), ab)) 62 | 63 | va = dot(n, cross(vsub(b,p), vsub(c,p))) 64 | if va <= 0 and unom >= 0 and udenom >= 0 65 | return vadd(b, smul(unom/(unom + udenom), bc)) 66 | 67 | vb = dot(n, cross(vsub(c,p), vsub(a,p))) 68 | if vb <= 0 and tnom > 0 and tdenom >= 0 69 | return vadd(a, smul(tnom/(tnom + tdenom), ac)) 70 | 71 | u = va/(va+vb+vc) 72 | v = vb/(va+vb+vc) 73 | w = 1-u-v 74 | return vadd(smul(u, a), vadd(smul(v,b), smul(w,c))) 75 | 76 | exports.pointTriangleDist = (p, v0, v1, v2) -> 77 | [cx, cy, cz] = exports.closestPointTriangle(p, v0, v1, v2) 78 | [px, py, pz] = p 79 | dx=cx-px; dy=cy-py; dz=cz-pz 80 | return Math.sqrt(dx*dx + dy*dy + dz*dz) 81 | -------------------------------------------------------------------------------- /src/dist3d.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var cross, dot, length, sadd, slength, smul, vadd, vsub; 3 | 4 | vsub = function(p1, p2) { 5 | return [p1[0] - p2[0], p1[1] - p2[1], p1[2] - p2[2]]; 6 | }; 7 | 8 | vadd = function(p1, p2) { 9 | return [p1[0] + p2[0], p1[1] + p2[1], p1[2] + p2[2]]; 10 | }; 11 | 12 | sadd = function(s, p) { 13 | var x, y, z; 14 | x = p[0]; 15 | y = p[1]; 16 | z = p[2]; 17 | return [x + s, y + s, z + s]; 18 | }; 19 | 20 | slength = function(p) { 21 | var x, y, z; 22 | x = p[0]; 23 | y = p[1]; 24 | z = p[2]; 25 | return x * x + y * y + z * z; 26 | }; 27 | 28 | length = function(p) { 29 | return Math.sqrt(slength(p)); 30 | }; 31 | 32 | dot = function(p1, p2) { 33 | var x1, x2, y1, y2, z1, z2; 34 | x1 = p1[0]; 35 | y1 = p1[1]; 36 | z1 = p1[2]; 37 | x2 = p2[0]; 38 | y2 = p2[1]; 39 | z2 = p2[2]; 40 | return x1 * x2 + y1 * y2 + z1 * z2; 41 | }; 42 | 43 | smul = function(s, p) { 44 | var x, y, z; 45 | x = p[0]; 46 | y = p[1]; 47 | z = p[2]; 48 | return [x * s, y * s, z * s]; 49 | }; 50 | 51 | cross = function(p1, p2) { 52 | var x1, x2, y1, y2, z1, z2; 53 | x1 = p1[0]; 54 | y1 = p1[1]; 55 | z1 = p1[2]; 56 | x2 = p2[0]; 57 | y2 = p2[1]; 58 | z2 = p2[2]; 59 | return [y1 * z2 - z1 * y2, z1 * x2 - x1 * z2, x1 * y2 - y1 * x2]; 60 | }; 61 | 62 | exports.closestPointTriangle = function(p, a, b, c) { 63 | var ab, ac, bc, n, sdenom, snom, tdenom, tnom, u, udenom, unom, v, va, vb, vc, w; 64 | ab = vsub(b, a); 65 | ac = vsub(c, a); 66 | bc = vsub(c, b); 67 | snom = dot(vsub(p, a), ab); 68 | sdenom = dot(vsub(p, b), vsub(a, b)); 69 | tnom = dot(vsub(p, a), ac); 70 | tdenom = dot(vsub(p, c), vsub(a, c)); 71 | if (snom <= 0 && tnom <= 0) { 72 | return a; 73 | } 74 | unom = dot(vsub(p, b), bc); 75 | udenom = dot(vsub(p, c), vsub(b, c)); 76 | if (sdenom <= 0 && unom <= 0) { 77 | return b; 78 | } 79 | if (tdenom <= 0 && udenom <= 0) { 80 | return c; 81 | } 82 | n = cross(vsub(b, a), vsub(c, a)); 83 | vc = dot(n, cross(vsub(a, p), vsub(b, p))); 84 | if (vc <= 0 && snom >= 0 && sdenom >= 0) { 85 | return vadd(a, smul(snom / (snom + sdenom), ab)); 86 | } 87 | va = dot(n, cross(vsub(b, p), vsub(c, p))); 88 | if (va <= 0 && unom >= 0 && udenom >= 0) { 89 | return vadd(b, smul(unom / (unom + udenom), bc)); 90 | } 91 | vb = dot(n, cross(vsub(c, p), vsub(a, p))); 92 | if (vb <= 0 && tnom > 0 && tdenom >= 0) { 93 | return vadd(a, smul(tnom / (tnom + tdenom), ac)); 94 | } 95 | u = va / (va + vb + vc); 96 | v = vb / (va + vb + vc); 97 | w = 1 - u - v; 98 | return vadd(smul(u, a), vadd(smul(v, b), smul(w, c))); 99 | }; 100 | 101 | exports.pointTriangleDist = function(p, v0, v1, v2) { 102 | var cx, cy, cz, dx, dy, dz, px, py, pz, _ref; 103 | _ref = exports.closestPointTriangle(p, v0, v1, v2), cx = _ref[0], cy = _ref[1], cz = _ref[2]; 104 | px = p[0], py = p[1], pz = p[2]; 105 | dx = cx - px; 106 | dy = cy - py; 107 | dz = cz - pz; 108 | return Math.sqrt(dx * dx + dy * dy + dz * dz); 109 | }; 110 | -------------------------------------------------------------------------------- /src/global_illumination.shader: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | Conclusion of is that the shader is fillrate bound, not lookup bound 4 | 5 | 42 fps without GI 6 | 42 fps when discarding in VS 7 | 21 fps with unified geometry 8 | 21 fps with early z and discard 9 | 21 fps with early z and gl_FragColor write 10 | 21 fps when discarding in FS 11 | 17 fps with GI 12 | 17 fps without discard 13 | 17 fps without light lookup 14 | 13 fps without conditional/discard 15 | */ 16 | 17 | varying vec3 vViewPosition; 18 | varying vec4 vLightprobe; 19 | uniform mat4 proj, view; 20 | uniform sampler2D normaldepth; 21 | 22 | vertex: 23 | attribute vec3 position, center; 24 | attribute vec4 lightprobe; 25 | 26 | void main(){ 27 | vec3 pos = lightprobe.xyz + position; 28 | vViewPosition = (view * vec4(pos, 1.0)).xyz; 29 | vLightprobe = lightprobe; 30 | 31 | gl_Position = proj * view * vec4(pos, 1.0); 32 | 33 | /* // gives some speed, difficult to get rid of artifacts, very uneven performance 34 | vec4 center_view = view * vec4(lightprobe.xyz+center, 1.0); 35 | vec4 center_proj = proj * center_view; 36 | clip = center_proj.xy/center_proj.w; 37 | float scene_depth = texture2D(normaldepth, clip*0.5+0.5).w; 38 | float center_depth = length(center_view.xyz); 39 | float dist = distance(normalize(center_view.xyz) * scene_depth, (view * vec4(lightprobe.xyz, 1.0)).xyz); 40 | if(abs(clip.x) < 1.0 && abs(clip.y) < 1.0 && dist < 5.0){ 41 | 42 | gl_Position = proj * view * vec4(pos, 1.0); 43 | } 44 | else{ 45 | gl_Position = vec4(2.0); 46 | } 47 | */ 48 | } 49 | 50 | fragment: 51 | #require harmonics 52 | 53 | uniform mat4 inv_view; 54 | uniform vec2 viewport; 55 | uniform float gi_gain; 56 | //uniform float index; 57 | 58 | void main(){ 59 | vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); 60 | vec3 normal = data.xyz; 61 | float depth = data.w; 62 | vec3 eye_pos = depth * normalize(vViewPosition); 63 | vec3 position = (inv_view * vec4(eye_pos, 1.0)).xyz; 64 | 65 | // apply the global illumination 66 | float dist = distance(vLightprobe.xyz, position); 67 | float lambert = dot(normal, normalize(vLightprobe.xyz-position)); 68 | if(dist < 5.0 && lambert > 0.0){ 69 | float falloff = 1.0 - clamp(dist/5.0, 0.0, 1.0); 70 | float intensity = clamp(pow(falloff, 1.5) * lambert, 0.0, 1.0); 71 | vec3 irradiance = sphericalHarmonics(vLightprobe.w, normal)*gi_gain; 72 | gl_FragColor = vec4(irradiance * intensity, intensity); 73 | } 74 | else{ 75 | discard; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/harmonics.shaderlib: -------------------------------------------------------------------------------- 1 | uniform vec2 coefficients_size; 2 | uniform sampler2D coefficients; 3 | uniform float shconst[5]; 4 | 5 | vec3 getCoefficient(float index, float m){ 6 | vec2 coord = vec2(m+0.5, index+0.5)/coefficients_size; 7 | return texture2D(coefficients, coord).rgb; 8 | } 9 | 10 | vec3 sphericalHarmonics(float index, vec3 normal){ 11 | float x = normal.x; 12 | float y = normal.y; 13 | float z = normal.z; 14 | 15 | vec3 l00 = getCoefficient(index, 0.0); 16 | 17 | vec3 l10 = getCoefficient(index, 1.0); 18 | vec3 l11 = getCoefficient(index, 2.0); 19 | vec3 l12 = getCoefficient(index, 3.0); 20 | 21 | vec3 l20 = getCoefficient(index, 4.0); 22 | vec3 l21 = getCoefficient(index, 5.0); 23 | vec3 l22 = getCoefficient(index, 6.0); 24 | vec3 l23 = getCoefficient(index, 7.0); 25 | vec3 l24 = getCoefficient(index, 8.0); 26 | 27 | vec3 result = ( 28 | l00 * shconst[0] + 29 | 30 | l12 * shconst[1] * x + 31 | l10 * shconst[1] * y + 32 | l11 * shconst[1] * z + 33 | 34 | l20 * shconst[2] * x*y + 35 | l21 * shconst[2] * y*z + 36 | l22 * shconst[3] * (3.0*z*z - 1.0) + 37 | l23 * shconst[2] * x*z + 38 | l24 * shconst[4] * (x*x - y*y) 39 | ); 40 | return max(result, vec3(0.0)); 41 | } 42 | -------------------------------------------------------------------------------- /src/illumination/bounce.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vPosition, vNormal; 2 | varying vec4 vLightprobe; 3 | 4 | vertex: 5 | attribute vec3 position, normal; 6 | attribute vec2 texcoord; 7 | attribute vec4 lightprobe; 8 | 9 | void main(){ 10 | vPosition = position; 11 | vNormal = normal; 12 | vLightprobe = lightprobe; 13 | gl_Position = vec4(texcoord*2.0-1.0, 0.0, 1.0); 14 | } 15 | 16 | fragment: 17 | #require /harmonics 18 | uniform vec2 viewport; 19 | uniform float gi_gain; 20 | 21 | void main(){ 22 | // compute world normal 23 | vec3 normal = normalize(vNormal); 24 | 25 | // apply the global illumination 26 | float dist = distance(vLightprobe.xyz, vPosition); 27 | float lambert = dot(normal, normalize(vLightprobe.xyz-vPosition)); 28 | if(dist < 5.0 && lambert > 0.0){ 29 | float falloff = 1.0 - clamp(dist/5.0, 0.0, 1.0); 30 | float intensity = clamp(pow(falloff, 1.5) * lambert, 0.0, 1.0); 31 | vec3 irradiance = sphericalHarmonics(vLightprobe.w, normal)*gi_gain; 32 | gl_FragColor = vec4(irradiance * intensity, intensity); 33 | } 34 | else{ 35 | discard; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/illumination/bounce_model.coffee: -------------------------------------------------------------------------------- 1 | {pointTriangleDist} = require '/dist3d' 2 | 3 | return class BounceModel extends require('/webgl/drawable') 4 | attribs: ['position', 'texcoord', 'normal', 'lightprobe'] 5 | pointers: [ 6 | {name: 'position', size: 3, offset: 0, stride: 12}, 7 | {name: 'texcoord', size: 2, offset: 3, stride: 12}, 8 | {name: 'normal', size: 3, offset: 5, stride: 12}, 9 | {name: 'lightprobe', size: 4, offset: 8, stride: 12}, 10 | ] 11 | 12 | constructor: (@gl, model, probes) -> 13 | super() 14 | 15 | start = gettime() 16 | 17 | vertices = model.vertices 18 | vertex_count = vertices.length/8 19 | face_count = vertex_count/3 20 | 21 | result = [] 22 | 23 | for i in [0...face_count] 24 | verti = i*3 25 | 26 | vali = verti*8 27 | x1 = vertices[vali+0] 28 | y1 = vertices[vali+1] 29 | z1 = vertices[vali+2] 30 | u1 = vertices[vali+3] 31 | v1 = vertices[vali+4] 32 | nx1 = vertices[vali+5] 33 | ny1 = vertices[vali+6] 34 | nz1 = vertices[vali+7] 35 | 36 | vali = verti*8+8 37 | x2 = vertices[vali+0] 38 | y2 = vertices[vali+1] 39 | z2 = vertices[vali+2] 40 | u2 = vertices[vali+3] 41 | v2 = vertices[vali+4] 42 | nx2 = vertices[vali+5] 43 | ny2 = vertices[vali+6] 44 | nz2 = vertices[vali+7] 45 | 46 | vali = verti*8+16 47 | x3 = vertices[vali+0] 48 | y3 = vertices[vali+1] 49 | z3 = vertices[vali+2] 50 | u3 = vertices[vali+3] 51 | v3 = vertices[vali+4] 52 | nx3 = vertices[vali+5] 53 | ny3 = vertices[vali+6] 54 | nz3 = vertices[vali+7] 55 | for probe, i in probes 56 | px = probe.x; py = probe.y; pz = probe.z 57 | 58 | dx1 = px-x1; dy1 = py-y1; dz1 = pz-z1 59 | l = Math.sqrt(dx1*dx1+dy1*dy1+dz1*dz1) 60 | dx1/=l; dy1/=l; dz1/=l 61 | dot1 = dx1*nx1 + dy1*ny1 + dz1*nz1 62 | 63 | dx2 = px-x2; dy2 = py-y2; dz2 = pz-z2 64 | l = Math.sqrt(dx2*dx2+dy2*dy2+dz2*dz2) 65 | dx2/=l; dy2/=l; dz2/=l 66 | dot2 = dx2*nx2 + dy2*ny2 + dz2*nz2 67 | 68 | dx3 = px-x3; dy3 = py-y3; dz3 = pz-z3 69 | l = Math.sqrt(dx3*dx3+dy3*dy3+dz3*dz3) 70 | dx3/=l; dy3/=l; dz3/=l 71 | dot3 = dx3*nx3 + dy3*ny3 + dz3*nz3 72 | 73 | 74 | # triangle plane 75 | tx=x2-x1; ty=y2-y1; tz=z2-z1 76 | btx=x3-x1; bty=y3-y1; btz=z3-z1 77 | 78 | fnx = ty*btz - tz*bty 79 | fny = tz*btx - tx*btz 80 | fnz = tx*bty - ty*btx 81 | l = Math.sqrt(fnx*fnx + fny*fny + fnz*fnz) 82 | fnx/=l; fny/=l; fnz/=l 83 | det = fnx*x1 + fny*y1 + fnz*z1 84 | dist = Math.abs((fnx*px + fny*py + fnz*pz)-det) 85 | 86 | if (dot1 >= 0 or dot2 >= 0 or dot3 >= 0) and dist <= 5.0 87 | if pointTriangleDist([px, py, pz], [x1, y1, z1], [x2, y2, z2], [x3, y3, z3]) <= 5.0 88 | result.push( 89 | x1, y1, z1, u1, v1, nx1, ny1, nz1, px, py, pz, i, 90 | x2, y2, z2, u2, v2, nx2, ny2, nz2, px, py, pz, i, 91 | x3, y3, z3, u3, v3, nx3, ny3, nz3, px, py, pz, i, 92 | ) 93 | 94 | #console.log (result.length/12)/((vertices.length/8)*probes.length) 95 | @size = result.length/12 96 | @uploadList result 97 | #console.log gettime() - start 98 | 99 | -------------------------------------------------------------------------------- /src/illumination/bounce_model.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var BounceModel, pointTriangleDist, 3 | __hasProp = {}.hasOwnProperty, 4 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 5 | 6 | pointTriangleDist = require('/dist3d').pointTriangleDist; 7 | 8 | return BounceModel = (function(_super) { 9 | 10 | __extends(BounceModel, _super); 11 | 12 | BounceModel.prototype.attribs = ['position', 'texcoord', 'normal', 'lightprobe']; 13 | 14 | BounceModel.prototype.pointers = [ 15 | { 16 | name: 'position', 17 | size: 3, 18 | offset: 0, 19 | stride: 12 20 | }, { 21 | name: 'texcoord', 22 | size: 2, 23 | offset: 3, 24 | stride: 12 25 | }, { 26 | name: 'normal', 27 | size: 3, 28 | offset: 5, 29 | stride: 12 30 | }, { 31 | name: 'lightprobe', 32 | size: 4, 33 | offset: 8, 34 | stride: 12 35 | } 36 | ]; 37 | 38 | function BounceModel(gl, model, probes) { 39 | var btx, bty, btz, det, dist, dot1, dot2, dot3, dx1, dx2, dx3, dy1, dy2, dy3, dz1, dz2, dz3, face_count, fnx, fny, fnz, i, l, nx1, nx2, nx3, ny1, ny2, ny3, nz1, nz2, nz3, probe, px, py, pz, result, start, tx, ty, tz, u1, u2, u3, v1, v2, v3, vali, vertex_count, verti, vertices, x1, x2, x3, y1, y2, y3, z1, z2, z3, _i, _j, _len; 40 | this.gl = gl; 41 | BounceModel.__super__.constructor.call(this); 42 | start = gettime(); 43 | vertices = model.vertices; 44 | vertex_count = vertices.length / 8; 45 | face_count = vertex_count / 3; 46 | result = []; 47 | for (i = _i = 0; 0 <= face_count ? _i < face_count : _i > face_count; i = 0 <= face_count ? ++_i : --_i) { 48 | verti = i * 3; 49 | vali = verti * 8; 50 | x1 = vertices[vali + 0]; 51 | y1 = vertices[vali + 1]; 52 | z1 = vertices[vali + 2]; 53 | u1 = vertices[vali + 3]; 54 | v1 = vertices[vali + 4]; 55 | nx1 = vertices[vali + 5]; 56 | ny1 = vertices[vali + 6]; 57 | nz1 = vertices[vali + 7]; 58 | vali = verti * 8 + 8; 59 | x2 = vertices[vali + 0]; 60 | y2 = vertices[vali + 1]; 61 | z2 = vertices[vali + 2]; 62 | u2 = vertices[vali + 3]; 63 | v2 = vertices[vali + 4]; 64 | nx2 = vertices[vali + 5]; 65 | ny2 = vertices[vali + 6]; 66 | nz2 = vertices[vali + 7]; 67 | vali = verti * 8 + 16; 68 | x3 = vertices[vali + 0]; 69 | y3 = vertices[vali + 1]; 70 | z3 = vertices[vali + 2]; 71 | u3 = vertices[vali + 3]; 72 | v3 = vertices[vali + 4]; 73 | nx3 = vertices[vali + 5]; 74 | ny3 = vertices[vali + 6]; 75 | nz3 = vertices[vali + 7]; 76 | for (i = _j = 0, _len = probes.length; _j < _len; i = ++_j) { 77 | probe = probes[i]; 78 | px = probe.x; 79 | py = probe.y; 80 | pz = probe.z; 81 | dx1 = px - x1; 82 | dy1 = py - y1; 83 | dz1 = pz - z1; 84 | l = Math.sqrt(dx1 * dx1 + dy1 * dy1 + dz1 * dz1); 85 | dx1 /= l; 86 | dy1 /= l; 87 | dz1 /= l; 88 | dot1 = dx1 * nx1 + dy1 * ny1 + dz1 * nz1; 89 | dx2 = px - x2; 90 | dy2 = py - y2; 91 | dz2 = pz - z2; 92 | l = Math.sqrt(dx2 * dx2 + dy2 * dy2 + dz2 * dz2); 93 | dx2 /= l; 94 | dy2 /= l; 95 | dz2 /= l; 96 | dot2 = dx2 * nx2 + dy2 * ny2 + dz2 * nz2; 97 | dx3 = px - x3; 98 | dy3 = py - y3; 99 | dz3 = pz - z3; 100 | l = Math.sqrt(dx3 * dx3 + dy3 * dy3 + dz3 * dz3); 101 | dx3 /= l; 102 | dy3 /= l; 103 | dz3 /= l; 104 | dot3 = dx3 * nx3 + dy3 * ny3 + dz3 * nz3; 105 | tx = x2 - x1; 106 | ty = y2 - y1; 107 | tz = z2 - z1; 108 | btx = x3 - x1; 109 | bty = y3 - y1; 110 | btz = z3 - z1; 111 | fnx = ty * btz - tz * bty; 112 | fny = tz * btx - tx * btz; 113 | fnz = tx * bty - ty * btx; 114 | l = Math.sqrt(fnx * fnx + fny * fny + fnz * fnz); 115 | fnx /= l; 116 | fny /= l; 117 | fnz /= l; 118 | det = fnx * x1 + fny * y1 + fnz * z1; 119 | dist = Math.abs((fnx * px + fny * py + fnz * pz) - det); 120 | if ((dot1 >= 0 || dot2 >= 0 || dot3 >= 0) && dist <= 5.0) { 121 | if (pointTriangleDist([px, py, pz], [x1, y1, z1], [x2, y2, z2], [x3, y3, z3]) <= 5.0) { 122 | result.push(x1, y1, z1, u1, v1, nx1, ny1, nz1, px, py, pz, i, x2, y2, z2, u2, v2, nx2, ny2, nz2, px, py, pz, i, x3, y3, z3, u3, v3, nx3, ny3, nz3, px, py, pz, i); 123 | } 124 | } 125 | } 126 | } 127 | this.size = result.length / 12; 128 | this.uploadList(result); 129 | } 130 | 131 | return BounceModel; 132 | 133 | })(require('/webgl/drawable')); 134 | -------------------------------------------------------------------------------- /src/illumination/cube_diffuse.shader: -------------------------------------------------------------------------------- 1 | varying vec2 vTexcoord; 2 | varying vec3 vPosition; 3 | uniform mat4 proj, view; 4 | 5 | vertex: 6 | attribute vec3 position; 7 | attribute vec2 texcoord; 8 | 9 | void main(){ 10 | vTexcoord = texcoord; 11 | vPosition = position; 12 | gl_Position = proj * view * vec4(position, 1.0); 13 | } 14 | 15 | fragment: 16 | uniform sampler2D diffuse_texture; 17 | uniform vec3 diffuse_color; 18 | void main(){ 19 | vec3 diffuse = pow(texture2D(diffuse_texture, vTexcoord).rgb, vec3(1.8)); 20 | gl_FragColor = vec4(diffuse*pow(diffuse_color, vec3(1.8)), 1.0); 21 | } 22 | -------------------------------------------------------------------------------- /src/illumination/cubeprobe.shader: -------------------------------------------------------------------------------- 1 | varying vec2 vTexcoord; 2 | varying vec3 vPosition; 3 | uniform mat4 proj, view; 4 | 5 | vertex: 6 | attribute vec3 position; 7 | attribute vec2 texcoord; 8 | 9 | void main(){ 10 | vTexcoord = texcoord; 11 | vPosition = position; 12 | gl_Position = proj * view * vec4(position, 1.0); 13 | } 14 | 15 | fragment: 16 | void main(){ 17 | gl_FragColor = vec4(vTexcoord, 0.0, 1.0); 18 | } 19 | -------------------------------------------------------------------------------- /src/illumination/debug.shader: -------------------------------------------------------------------------------- 1 | varying vec3 vPosition, vViewPosition; 2 | uniform mat4 proj, view; 3 | 4 | vertex: 5 | attribute vec3 position; 6 | uniform vec3 offset; 7 | 8 | void main(){ 9 | vPosition = position; 10 | vViewPosition = (view * vec4(position+offset, 1.0)).xyz; 11 | gl_Position = proj * view * vec4(position+offset, 1.0); 12 | } 13 | 14 | fragment: 15 | #require /harmonics 16 | uniform sampler2D normaldepth; 17 | uniform vec2 viewport; 18 | uniform float gi_gain; 19 | uniform float index; 20 | 21 | void main(){ 22 | vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); 23 | float depth = data.w; 24 | if(length(vViewPosition) < depth){ 25 | gl_FragColor = vec4(sphericalHarmonics(index, normalize(vPosition))*gi_gain, 1.0); 26 | } 27 | else{ 28 | discard; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/illumination/diffusemap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/illumination/diffusemap.jpg -------------------------------------------------------------------------------- /src/illumination/harmonics.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D lightprobes; 10 | uniform vec2 lightprobes_size; 11 | uniform float shconst[5]; 12 | 13 | float pi = 3.141592653589793; 14 | 15 | mat3 front = mat3( 16 | 1.0, 0.0, 0.0, 17 | 0.0, 1.0, 0.0, 18 | 0.0, 0.0, 1.0 19 | ); 20 | 21 | mat3 back = mat3( 22 | -1.0, 0.0, 0.0, 23 | 0.0, 1.0, 0.0, 24 | 0.0, 0.0, -1.0 25 | ); 26 | 27 | mat3 left = mat3( 28 | 0.0, 0.0, -1.0, 29 | 0.0, 1.0, 0.0, 30 | 1.0, 0.0, 0.0 31 | ); 32 | 33 | mat3 right = mat3( 34 | 0.0, 0.0, 1.0, 35 | 0.0, 1.0, 0.0, 36 | -1.0, 0.0, 0.0 37 | ); 38 | 39 | mat3 up = mat3( 40 | 1.0, 0.0, 0.0, 41 | 0.0, 0.0, 1.0, 42 | 0.0, -1.0, 0.0 43 | ); 44 | 45 | mat3 down = mat3( 46 | 1.0, 0.0, 0.0, 47 | 0.0, 0.0, -1.0, 48 | 0.0, 1.0, 0.0 49 | ); 50 | 51 | float harmonics(vec3 normal){ 52 | int index = int(gl_FragCoord.x); 53 | 54 | float x = normal.x; 55 | float y = normal.y; 56 | float z = normal.z; 57 | 58 | /* 59 | if(index==0){ 60 | return shconst[0]; 61 | } 62 | else if(index==1){ 63 | return shconst[1] * y; 64 | } 65 | else if(index==2){ 66 | return shconst[1] * z; 67 | } 68 | else if(index==3){ 69 | return shconst[1] * x; 70 | } 71 | else if(index==4){ 72 | return shconst[2]*x*y; 73 | } 74 | else if(index==5){ 75 | return shconst[2]*y*z; 76 | } 77 | else if(index==6){ 78 | return shconst[3] * (3.0*z*z - 1.0); 79 | } 80 | else if(index==7){ 81 | return shconst[2]*x*z; 82 | } 83 | else{ 84 | return shconst[4]*(x*x - y*y); 85 | } 86 | */ 87 | if(index==0){ 88 | return 1.0; 89 | } 90 | else if(index==1){ 91 | return y; 92 | } 93 | else if(index==2){ 94 | return z; 95 | } 96 | else if(index==3){ 97 | return x; 98 | } 99 | else if(index==4){ 100 | return x*y; 101 | } 102 | else if(index==5){ 103 | return y*z; 104 | } 105 | else if(index==6){ 106 | return 3.0*z*z - 1.0; 107 | } 108 | else if(index==7){ 109 | return x*z; 110 | } 111 | else{ 112 | return x*x - y*y; 113 | } 114 | } 115 | 116 | #define probe_size 16 117 | 118 | vec3 sampleSide(float side, mat3 rot){ 119 | vec3 result = vec3(0.0); 120 | 121 | float divider = 0.0; 122 | //(140,6): error X3511: unable to unroll loop, loop does not appear to terminate in a timely manner (9 iterations), use the [unroll(n)] attribute to force an exact higher number 123 | /* 124 | for(int y=0; y 0.0){ 17 | return sky_radiance; 18 | } 19 | else{ 20 | vec3 diffuse_color = texture2D(diffusemap, coord).rgb; 21 | vec4 bounce_data = texture2D(bounce, uv.xy); 22 | vec3 direct_irradiance = texture2D(lightmap, uv.xy).rgb*sun_radiance; 23 | vec3 global_irradiance = bounce_data.rgb/bounce_data.a; 24 | return diffuse_color * (direct_irradiance + global_irradiance); 25 | } 26 | } 27 | void main(){ 28 | vec3 result = ( 29 | get(-0.5, -0.5) + 30 | get(0.5, 0.5) + 31 | get(-0.5, 0.5) + 32 | get(0.5, -0.5) 33 | )/4.0; 34 | gl_FragColor = vec4(max(result, vec3(0.0)), 1.0); 35 | } 36 | 37 | -------------------------------------------------------------------------------- /src/main.coffee: -------------------------------------------------------------------------------- 1 | audio = require 'audio' 2 | loading = require 'loading' 3 | Shader = require 'webgl/shader' 4 | Quad = require 'webgl/quad' 5 | require 'webgl-nuke-vendor-prefix' 6 | require 'webgl-texture-float-extension-shims' 7 | 8 | #crashes firefox 9 | #worker = new Worker('worker.js') 10 | #worker.postMessage('Ping') 11 | #worker.onmessage = (event) -> 12 | # console.log event 13 | 14 | load_hooks = 15 | '\.jpg$|\.jpeg$|\.gif$|\.png': (name, buffer, callback) -> 16 | ext =name.split('.').pop() 17 | switch ext 18 | when 'png' then mime = 'image/png' 19 | when 'gif' then mime = 'image/gif' 20 | when 'jpg', 'jpeg' then mime = 'image/jpeg' 21 | 22 | image = new Image() 23 | image.src = getURL(buffer, mime) 24 | image.onload = -> 25 | callback image 26 | '\.mpg$|\.ogg$|\.wav$': (name, buffer, callback) -> 27 | audio.decode buffer, (result) -> 28 | callback result 29 | 30 | errorContainer = (title) -> 31 | canvas.remove() 32 | $('#ui').empty() 33 | return $('
') 34 | .css( 35 | position: 'absolute', 36 | width: 300, 37 | left: '50%', 38 | top: 50, 39 | marginLeft: -100 40 | ) 41 | .append($('

').text(title)) 42 | .appendTo('#ui') 43 | 44 | disableSelect = -> 45 | $('*').each -> 46 | $(@) 47 | .attr('unselectable', 'on') 48 | .css 49 | '-moz-user-select':'none', 50 | '-webkit-user-select':'none', 51 | 'user-select':'none', 52 | '-ms-user-select':'none' 53 | @onselectstart = -> false 54 | 55 | document.oncontextmenu = -> 56 | return false 57 | 58 | enableSelect = -> 59 | $('*').each -> 60 | $(@) 61 | .removeAttr('unselectable') 62 | .css 63 | '-moz-user-select':'text', 64 | '-webkit-user-select':'text', 65 | 'user-select':'text', 66 | '-ms-user-select':'text' 67 | @onselectstart = undefined 68 | 69 | document.oncontextmenu = undefined 70 | 71 | exports.main = -> 72 | disableSelect() 73 | window.canvas = $ 'canvas' 74 | window.onerror = (error) -> 75 | if error.search(Shader.error) > 0 76 | return true 77 | 78 | try 79 | window.gl = canvas[0].getContext 'experimental-webgl' 80 | if not window.gl 81 | window.gl = canvas[0].getContext 'webgl' 82 | 83 | if window.gl 84 | window.quad = new Quad window.gl 85 | 86 | stddev = gl.getExtension 'OES_standard_derivatives' 87 | if not stddev 88 | return errorContainer('Missing Extension: Standard Derivatives') 89 | .append(''' 90 |

This application requires the WebGL Standard Derivatives extension which you do not have, sorry.

91 | ''') 92 | 93 | floatExt = gl.getFloatExtension require: ['renderable'], prefer:['filterable', 'half'], throws:false 94 | if not floatExt 95 | return errorContainer('Missing Extension: Floating Point Textures') 96 | .append(''' 97 |

This application requires the WebGL Floating Point Textures extension which you do not have, sorry.

98 | ''') 99 | 100 | Application = require('application').Application 101 | application = null 102 | 103 | loading.show 'Loading ...' 104 | 105 | loader.hooks(load_hooks).mount 106 | url: 'assets.pack', 107 | loaded: (files, fs) -> 108 | for name, value of files 109 | if name.match('\.shaderlib$') 110 | fs[name] = Shader.splitLines name, value 111 | try 112 | for name, value of files 113 | if name.match('\.shader$') 114 | fs[name] = new Shader gl, name, value 115 | application = new Application(window.canvas, window.gl) 116 | catch error 117 | if error == 'ShaderError' 118 | enableSelect() 119 | container = errorContainer('Shader Error').append(''' 120 |

121 | An error occured when compiling a shader, you can paste me the error. 122 |

123 | ''') 124 | container.css 125 | width: 600 126 | marginLeft: -300 127 | 128 | $('
')
129 |                             .text(Shader.lastError)
130 |                             .css('overflow', 'auto')
131 |                             .appendTo(container)
132 |                     else
133 |                         throw error
134 |                     
135 |             progress: loading.progress
136 |     else
137 |         container = errorContainer('You dont have WebGL')
138 |         if $.browser.msie
139 |             container.append('''
140 |                 

141 | You have Internet Explorer, please install 142 | Google Chrome or 143 | Firefox 144 |

145 | ''') 146 | else if $.browser.webkit 147 | container.append(''' 148 |

149 | If you use OSX Safari, please enable WebGL manually. 150 | If you use iOS Safari, you cannot use WebGL. 151 | If you use Android, please try Firefox Mobile or 152 | Opera Mobile 153 |

154 | ''') 155 | 156 | container.append(''' 157 |

158 | Please consult the support pages 159 | on how to get WebGL for your machine. 160 |

161 | ''') 162 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Quad, Shader, audio, disableSelect, enableSelect, errorContainer, load_hooks, loading; 3 | 4 | audio = require('audio'); 5 | 6 | loading = require('loading'); 7 | 8 | Shader = require('webgl/shader'); 9 | 10 | Quad = require('webgl/quad'); 11 | 12 | require('webgl-nuke-vendor-prefix'); 13 | 14 | require('webgl-texture-float-extension-shims'); 15 | 16 | load_hooks = { 17 | '\.jpg$|\.jpeg$|\.gif$|\.png': function(name, buffer, callback) { 18 | var ext, image, mime; 19 | ext = name.split('.').pop(); 20 | switch (ext) { 21 | case 'png': 22 | mime = 'image/png'; 23 | break; 24 | case 'gif': 25 | mime = 'image/gif'; 26 | break; 27 | case 'jpg': 28 | case 'jpeg': 29 | mime = 'image/jpeg'; 30 | } 31 | image = new Image(); 32 | image.src = getURL(buffer, mime); 33 | return image.onload = function() { 34 | return callback(image); 35 | }; 36 | }, 37 | '\.mpg$|\.ogg$|\.wav$': function(name, buffer, callback) { 38 | return audio.decode(buffer, function(result) { 39 | return callback(result); 40 | }); 41 | } 42 | }; 43 | 44 | errorContainer = function(title) { 45 | canvas.remove(); 46 | $('#ui').empty(); 47 | return $('
').css({ 48 | position: 'absolute', 49 | width: 300, 50 | left: '50%', 51 | top: 50, 52 | marginLeft: -100 53 | }).append($('

').text(title)).appendTo('#ui'); 54 | }; 55 | 56 | disableSelect = function() { 57 | $('*').each(function() { 58 | $(this).attr('unselectable', 'on').css({ 59 | '-moz-user-select': 'none', 60 | '-webkit-user-select': 'none', 61 | 'user-select': 'none', 62 | '-ms-user-select': 'none' 63 | }); 64 | return this.onselectstart = function() { 65 | return false; 66 | }; 67 | }); 68 | return document.oncontextmenu = function() { 69 | return false; 70 | }; 71 | }; 72 | 73 | enableSelect = function() { 74 | $('*').each(function() { 75 | $(this).removeAttr('unselectable').css({ 76 | '-moz-user-select': 'text', 77 | '-webkit-user-select': 'text', 78 | 'user-select': 'text', 79 | '-ms-user-select': 'text' 80 | }); 81 | return this.onselectstart = void 0; 82 | }); 83 | return document.oncontextmenu = void 0; 84 | }; 85 | 86 | exports.main = function() { 87 | var Application, application, container, floatExt, stddev; 88 | disableSelect(); 89 | window.canvas = $('canvas'); 90 | window.onerror = function(error) { 91 | if (error.search(Shader.error) > 0) { 92 | return true; 93 | } 94 | }; 95 | try { 96 | window.gl = canvas[0].getContext('experimental-webgl'); 97 | if (!window.gl) { 98 | window.gl = canvas[0].getContext('webgl'); 99 | } 100 | } catch (_error) {} 101 | if (window.gl) { 102 | window.quad = new Quad(window.gl); 103 | stddev = gl.getExtension('OES_standard_derivatives'); 104 | if (!stddev) { 105 | return errorContainer('Missing Extension: Standard Derivatives').append('

This application requires the WebGL Standard Derivatives extension which you do not have, sorry.

'); 106 | } 107 | floatExt = gl.getFloatExtension({ 108 | require: ['renderable'], 109 | prefer: ['filterable', 'half'], 110 | throws: false 111 | }); 112 | if (!floatExt) { 113 | return errorContainer('Missing Extension: Floating Point Textures').append('

This application requires the WebGL Floating Point Textures extension which you do not have, sorry.

'); 114 | } 115 | Application = require('application').Application; 116 | application = null; 117 | loading.show('Loading ...'); 118 | return loader.hooks(load_hooks).mount({ 119 | url: 'assets.pack', 120 | loaded: function(files, fs) { 121 | var container, name, value; 122 | for (name in files) { 123 | value = files[name]; 124 | if (name.match('\.shaderlib$')) { 125 | fs[name] = Shader.splitLines(name, value); 126 | } 127 | } 128 | try { 129 | for (name in files) { 130 | value = files[name]; 131 | if (name.match('\.shader$')) { 132 | fs[name] = new Shader(gl, name, value); 133 | } 134 | } 135 | return application = new Application(window.canvas, window.gl); 136 | } catch (error) { 137 | if (error === 'ShaderError') { 138 | enableSelect(); 139 | container = errorContainer('Shader Error').append('

\n An error occured when compiling a shader, you can paste me the error.\n

'); 140 | container.css({ 141 | width: 600, 142 | marginLeft: -300 143 | }); 144 | return $('
').text(Shader.lastError).css('overflow', 'auto').appendTo(container);
145 |           } else {
146 |             throw error;
147 |           }
148 |         }
149 |       },
150 |       progress: loading.progress
151 |     });
152 |   } else {
153 |     container = errorContainer('You dont have WebGL');
154 |     if ($.browser.msie) {
155 |       container.append('

\n You have Internet Explorer, please install\n Google Chrome or\n Firefox\n

'); 156 | } else if ($.browser.webkit) { 157 | container.append('

\n If you use OSX Safari, please enable WebGL manually.\n If you use iOS Safari, you cannot use WebGL.\n If you use Android, please try Firefox Mobile or\n Opera Mobile\n

'); 158 | } 159 | return container.append('

\n Please consult the support pages\n on how to get WebGL for your machine.\n

'); 160 | } 161 | }; 162 | -------------------------------------------------------------------------------- /src/model/bump/01_S_kap-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/01_S_kap-bump.png -------------------------------------------------------------------------------- /src/model/bump/01_s_kap-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/01_s_kap-bump.png -------------------------------------------------------------------------------- /src/model/bump/01_st_kp-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/01_st_kp-bump.png -------------------------------------------------------------------------------- /src/model/bump/01_stub-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/01_stub-bump.png -------------------------------------------------------------------------------- /src/model/bump/flat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/flat.png -------------------------------------------------------------------------------- /src/model/bump/kamen-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/kamen-bump.png -------------------------------------------------------------------------------- /src/model/bump/level.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/level.png -------------------------------------------------------------------------------- /src/model/bump/reljef-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/reljef-bump.png -------------------------------------------------------------------------------- /src/model/bump/sp_luk-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/sp_luk-bump.png -------------------------------------------------------------------------------- /src/model/bump/x01_st-bump.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/bump/x01_st-bump.png -------------------------------------------------------------------------------- /src/model/diffuse/00_skap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/00_skap.jpg -------------------------------------------------------------------------------- /src/model/diffuse/01_s_ba.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/01_s_ba.jpg -------------------------------------------------------------------------------- /src/model/diffuse/01_s_kap.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/01_s_kap.jpg -------------------------------------------------------------------------------- /src/model/diffuse/01_st_kp.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/01_st_kp.jpg -------------------------------------------------------------------------------- /src/model/diffuse/01_stub.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/01_stub.jpg -------------------------------------------------------------------------------- /src/model/diffuse/kamen-stup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/kamen-stup.jpg -------------------------------------------------------------------------------- /src/model/diffuse/kamen.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/kamen.jpg -------------------------------------------------------------------------------- /src/model/diffuse/prozor1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/prozor1.jpg -------------------------------------------------------------------------------- /src/model/diffuse/reljef.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/reljef.jpg -------------------------------------------------------------------------------- /src/model/diffuse/sp_luk.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/sp_luk.jpg -------------------------------------------------------------------------------- /src/model/diffuse/vrata_ko.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/vrata_ko.jpg -------------------------------------------------------------------------------- /src/model/diffuse/vrata_kr.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/vrata_kr.jpg -------------------------------------------------------------------------------- /src/model/diffuse/white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/white.png -------------------------------------------------------------------------------- /src/model/diffuse/x01_st.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/diffuse/x01_st.jpg -------------------------------------------------------------------------------- /src/model/lowres.vertices: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/lowres.vertices -------------------------------------------------------------------------------- /src/model/materials.json: -------------------------------------------------------------------------------- 1 | [{"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "00_skap.jpg", "diffuse_color": {"r": 0.713726, "b": 0.658824, "g": 0.705882}, "start": 0, "bumpmap": "00_skap.jpg", "size": 2304}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "vrata_ko.jpg", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 2304, "bumpmap": "vrata_ko.jpg", "size": 726}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_st_kp.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 3030, "bumpmap": "01_st_kp-bump.jpg", "size": 16560}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 19590, "bumpmap": "kamen-bump.jpg", "size": 144}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "reljef.jpg", "diffuse_color": {"r": 0.529412, "b": 0.490196, "g": 0.498039}, "start": 19734, "bumpmap": "reljef-bump.jpg", "size": 48}, {"specular_color": {"r": 0.034039, "b": 0.029333, "g": 0.032314}, "specularity": 1.0, "diffuse_texture": "kamen-stup.jpg", "diffuse_color": {"r": 0.941177, "b": 0.737255, "g": 0.866667}, "start": 19782, "bumpmap": "kamen-stup.jpg", "size": 33822}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "00_skap.jpg", "diffuse_color": {"r": 0.713726, "b": 0.658824, "g": 0.705882}, "start": 53604, "bumpmap": "00_skap.jpg", "size": 17748}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 71352, "bumpmap": "kamen-bump.jpg", "size": 7503}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 78855, "bumpmap": "01_stub-bump.jpg", "size": 192}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_s_ba.jpg", "diffuse_color": {"r": 0.8, "b": 0.74902, "g": 0.784314}, "start": 79047, "bumpmap": "01_s_ba.jpg", "size": 2736}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "kamen.jpg", "diffuse_color": {"r": 0.627451, "b": 0.560784, "g": 0.572549}, "start": 81783, "bumpmap": "kamen-bump.png", "size": 210}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "white.png", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 81993, "bumpmap": "level.png", "size": 1440}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 83433, "bumpmap": "01_stub-bump.jpg", "size": 144}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_s_ba.jpg", "diffuse_color": {"r": 0.8, "b": 0.74902, "g": 0.784314}, "start": 83577, "bumpmap": "01_s_ba.jpg", "size": 14931}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "prozor1.jpg", "diffuse_color": {"r": 1.0, "b": 1.0, "g": 1.0}, "start": 98508, "bumpmap": "prozor1.jpg", "size": 882}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 19.999998, "diffuse_texture": "vrata_kr.jpg", "diffuse_color": {"r": 0.784314, "b": 0.784314, "g": 0.784314}, "start": 99390, "bumpmap": "vrata_kr.jpg", "size": 4878}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "01_stub.jpg", "diffuse_color": {"r": 0.737255, "b": 0.670588, "g": 0.709804}, "start": 104268, "bumpmap": "01_stub-bump.jpg", "size": 696}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "x01_st.jpg", "diffuse_color": {"r": 0.827451, "b": 0.768628, "g": 0.8}, "start": 104964, "bumpmap": "level.png", "size": 6756}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "sp_luk.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 111720, "bumpmap": "sp_luk-bump.jpg", "size": 25056}, {"specular_color": {"r": 0.0, "b": 0.0, "g": 0.0}, "specularity": 50.0, "diffuse_texture": "sp_luk.jpg", "diffuse_color": {"r": 0.745098, "b": 0.67451, "g": 0.709804}, "start": 136776, "bumpmap": "sp_luk-bump.jpg", "size": 62574}] 2 | -------------------------------------------------------------------------------- /src/model/sponza.indices: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/sponza.indices -------------------------------------------------------------------------------- /src/model/sponza.vertices: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/src/model/sponza.vertices -------------------------------------------------------------------------------- /src/normaldepth.shader: -------------------------------------------------------------------------------- 1 | varying vec2 vTexcoord; 2 | varying vec3 vPosition, vViewPosition, vNormal, vViewNormal; 3 | uniform mat4 proj, view; 4 | uniform mat3 view_rot; 5 | 6 | vertex: 7 | attribute vec3 position, normal; 8 | attribute vec2 texcoord; 9 | 10 | void main(){ 11 | vTexcoord = texcoord; 12 | vPosition = position; 13 | vNormal = normal; 14 | vViewNormal = view_rot * normal; 15 | vViewPosition = (view * vec4(position, 1.0)).xyz; 16 | 17 | gl_Position = proj * view * vec4(position, 1.0); 18 | } 19 | 20 | fragment: 21 | #extension GL_OES_standard_derivatives : enable 22 | uniform sampler2D bumpmap; 23 | 24 | vec3 perturbedNormal(vec3 normal, float bumpheight){ 25 | vec3 vSigmaS = dFdx(vPosition); 26 | vec3 vSigmaT = dFdy(vPosition); 27 | vec3 vR1 = cross(vSigmaT, normal); 28 | vec3 vR2 = cross(normal, vSigmaS); 29 | float fDet = dot(vSigmaS, vR1); 30 | float dBs = dFdx(bumpheight); 31 | float dBt = dFdy(bumpheight); 32 | vec3 vSurfGrad = sign(fDet) * (dBs*vR1 + dBt*vR2); 33 | return normalize(abs(fDet)*normal-vSurfGrad); 34 | } 35 | 36 | void main(){ 37 | vec3 normal = normalize(vNormal); 38 | float bumpheight = texture2D(bumpmap, vTexcoord).r; 39 | vec3 perturbed_normal = perturbedNormal(normal, bumpheight/96.0); 40 | float depth = length(vViewPosition); 41 | vec3 eye_dir = normalize(vViewPosition); 42 | float displacement = dot(eye_dir, -(view_rot*normal)) * bumpheight*0.05; 43 | 44 | gl_FragColor = vec4(perturbed_normal, depth-displacement); 45 | } 46 | -------------------------------------------------------------------------------- /src/presets/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "remembered": { 3 | "Default": { 4 | "0": { 5 | "resolution_label": "1:2 default", 6 | "show_fps": false 7 | }, 8 | "1": { 9 | "inputGamma": 1.8, 10 | "outputGamma": 1.99100719424, 11 | "brightness": 1.51079136691, 12 | "saturation": 1.0071942446 13 | }, 14 | "2": { 15 | "orientation": 104, 16 | "elevation": 53.8686131387 17 | }, 18 | "3": { 19 | "draw_probes": false, 20 | "gi": 0.762589928058, 21 | "di": 0.172661870504, 22 | "ao": 0.700729927007 23 | }, 24 | "4": { 25 | "sunColor": [ 26 | 255, 27 | 248.823529412, 28 | 202.5 29 | ], 30 | "sunRadiance": 9.4964028777, 31 | "skyColor": [ 32 | 9.90196078431, 33 | 66.9838523645, 34 | 252.5 35 | ], 36 | "skyRadiance": 1.0071942446, 37 | "giGain": 0.7, 38 | "bounces": 3 39 | }, 40 | "5": { 41 | "c1": 1.15107913669, 42 | "c2": 2.41726618705, 43 | "band3": 0.517985611511, 44 | "c3": 3.13669064748, 45 | "c4": 0.546762589928, 46 | "c5": 1.87050359712 47 | }, 48 | "6": { 49 | "x": -9.38136276391469, 50 | "y": 3.176749303559157, 51 | "z": 0.09001503384880467, 52 | "go": 92.0781892939057, 53 | "p": 0.2106932539339603 54 | }, 55 | "7": { 56 | "subpixel_aa": 0.75, 57 | "contrast_treshold": 0.166, 58 | "edge_treshold": 0 59 | }, 60 | "8": { 61 | "show_all": false, 62 | "window_label": "Antialiased", 63 | "show_label": false 64 | } 65 | } 66 | }, 67 | "folders": { 68 | "Performance": { 69 | "folders": {}, 70 | "preset": "Default", 71 | "closed": false 72 | }, 73 | "Picture": { 74 | "folders": {}, 75 | "preset": "Default", 76 | "closed": false 77 | }, 78 | "Compositing": { 79 | "folders": {}, 80 | "preset": "Default", 81 | "closed": true 82 | }, 83 | "Lighting": { 84 | "folders": {}, 85 | "preset": "Default", 86 | "closed": true 87 | }, 88 | "Harmonics": { 89 | "folders": {}, 90 | "preset": "Default", 91 | "closed": true 92 | }, 93 | "Camera": { 94 | "folders": {}, 95 | "preset": "Default", 96 | "closed": true 97 | }, 98 | "Sun": { 99 | "folders": {}, 100 | "preset": "Default", 101 | "closed": false 102 | }, 103 | "Antialias": { 104 | "folders": {}, 105 | "preset": "Default", 106 | "closed": true 107 | }, 108 | "Views": { 109 | "folders": {}, 110 | "preset": "Default", 111 | "closed": false 112 | } 113 | }, 114 | "preset": "Default", 115 | "closed": true 116 | } 117 | -------------------------------------------------------------------------------- /src/presets/new.json: -------------------------------------------------------------------------------- 1 | { 2 | "remembered": { 3 | "Apple Gamma": { 4 | "1": { 5 | "outputGamma": 2.5, 6 | "brightness": 1.3218978102189782, 7 | "saturation": 0.95071942446 8 | } 9 | }, 10 | "Default": { 11 | "0": { 12 | "resolution_label": "1:2 default", 13 | "show_fps": false 14 | }, 15 | "1": { 16 | "inputGamma": 1.8, 17 | "outputGamma": 2.1, 18 | "brightness": 1.0218978102189782, 19 | "saturation": 1.0071942446 20 | }, 21 | "2": { 22 | "orientation": 102.48175182481751, 23 | "elevation": 51.240875912408754 24 | }, 25 | "3": { 26 | "draw_probes": false, 27 | "gi": 0.9927007299270073, 28 | "di": 0.8321167883211679, 29 | "ao": 0.700729927007 30 | }, 31 | "4": { 32 | "sunColor": [ 33 | 255, 34 | 248.823529412, 35 | 202.5 36 | ], 37 | "sunRadiance": 5.5, 38 | "skyColor": [ 39 | 0, 40 | 35, 41 | 255 42 | ], 43 | "skyRadiance": 0.8, 44 | "giGain": 1.6058394160583942, 45 | "bounces": 3 46 | }, 47 | "5": { 48 | "c1": 1.0359712230215827, 49 | "c2": 1.841726618705036, 50 | "band3": 0.8345323741007195, 51 | "c3": 2.935251798561151, 52 | "c4": 0.5755395683453237, 53 | "c5": 1.064748201438849 54 | }, 55 | "6": { 56 | "x": -9.904411786477144, 57 | "y": 1.864264505425676, 58 | "z": -0.5129541306187764, 59 | "go": 112.499999992655, 60 | "p": -4.316055717198253 61 | }, 62 | "7": { 63 | "subpixel_aa": 0.75, 64 | "contrast_treshold": 0.166, 65 | "edge_treshold": 0 66 | }, 67 | "8": { 68 | "show_all": false, 69 | "window_label": "Antialiased", 70 | "show_label": false 71 | } 72 | }, 73 | "Bright Day": { 74 | "1": { 75 | "brightness": 1.0218978102189782 76 | }, 77 | "2": { 78 | "orientation": 102.48175182481751, 79 | "elevation": 51.240875912408754 80 | }, 81 | "4": { 82 | "sunColor": [ 83 | 255, 84 | 248.823529412, 85 | 202.5 86 | ], 87 | "sunRadiance": 5.5, 88 | "skyColor": [ 89 | 0, 90 | 35, 91 | 255 92 | ], 93 | "skyRadiance": 0.8 94 | } 95 | }, 96 | "Morning Mood": { 97 | "1": { 98 | "brightness": 1.7266187050359714 99 | }, 100 | "2": { 101 | "orientation": 102.481751825, 102 | "elevation": 7.769784172661871 103 | }, 104 | "4": { 105 | "sunColor": [ 106 | 252.5, 107 | 178.81776239907728, 108 | 138.62745098039215 109 | ], 110 | "sunRadiance": 2.5899280575539567, 111 | "skyColor": [ 112 | 74.63235294117646, 113 | 94.24163783160323, 114 | 217.5 115 | ], 116 | "skyRadiance": 0.8 117 | } 118 | }, 119 | "Noon": { 120 | "1": { 121 | "brightness": 1.0218978102189782 122 | }, 123 | "2": { 124 | "orientation": 160.57553956834533, 125 | "elevation": 78.34532374100719 126 | }, 127 | "4": { 128 | "sunColor": [ 129 | 255, 130 | 255, 131 | 255 132 | ], 133 | "sunRadiance": 6.9064748201438855, 134 | "skyColor": [ 135 | 140, 136 | 155.7843137254902, 137 | 255 138 | ], 139 | "skyRadiance": 2.5899280575539567 140 | } 141 | } 142 | }, 143 | "folders": { 144 | "Performance": { 145 | "folders": {}, 146 | "preset": "Default", 147 | "closed": false 148 | }, 149 | "Picture": { 150 | "folders": {}, 151 | "preset": "Default", 152 | "closed": false 153 | }, 154 | "Compositing": { 155 | "folders": {}, 156 | "preset": "Default", 157 | "closed": true 158 | }, 159 | "Lighting": { 160 | "folders": {}, 161 | "preset": "Default", 162 | "closed": true 163 | }, 164 | "Harmonics": { 165 | "folders": {}, 166 | "preset": "Default", 167 | "closed": true 168 | }, 169 | "Camera": { 170 | "folders": {}, 171 | "preset": "Default", 172 | "closed": true 173 | }, 174 | "Sun": { 175 | "folders": {}, 176 | "preset": "Default", 177 | "closed": false 178 | }, 179 | "Antialias": { 180 | "folders": {}, 181 | "preset": "Default", 182 | "closed": true 183 | }, 184 | "Views": { 185 | "folders": {}, 186 | "preset": "Default", 187 | "closed": false 188 | } 189 | }, 190 | "preset": "Default", 191 | "closed": true 192 | } 193 | -------------------------------------------------------------------------------- /src/ssao/module.coffee: -------------------------------------------------------------------------------- 1 | Rendernode = require '/rendernode' 2 | Blur = require '/blur' 3 | 4 | return class SSAO 5 | constructor: (gl, @normaldepth) -> 6 | floatExt = gl.getFloatExtension require: ['renderable', 'filterable'] 7 | 8 | @moments = new Rendernode gl, 9 | program: get 'moments.shader' 10 | type: floatExt.type 11 | drawable: quad 12 | 13 | @blur = new Blur gl, type: floatExt.type 14 | 15 | @output = new Rendernode gl, 16 | program: get 'ssao.shader' 17 | drawable: quad 18 | 19 | update: -> 20 | @moments.start() 21 | .sampler('normaldepth', @normaldepth) 22 | .f('range', 42) 23 | .clear() 24 | .draw() 25 | .end() 26 | 27 | @blur.update(@moments) 28 | 29 | @output.start() 30 | .sampler('normaldepth', @normaldepth) 31 | .sampler('momentsmap', @blur.output) 32 | .f('range', 42) 33 | .clear() 34 | .draw() 35 | .end() 36 | 37 | resize: (width, height) -> 38 | @moments.resize width/2, height/2 39 | @blur.resize width/4, height/4 40 | @output.resize width, height 41 | -------------------------------------------------------------------------------- /src/ssao/module.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Blur, Rendernode, SSAO; 3 | 4 | Rendernode = require('/rendernode'); 5 | 6 | Blur = require('/blur'); 7 | 8 | return SSAO = (function() { 9 | 10 | function SSAO(gl, normaldepth) { 11 | var floatExt; 12 | this.normaldepth = normaldepth; 13 | floatExt = gl.getFloatExtension({ 14 | require: ['renderable', 'filterable'] 15 | }); 16 | this.moments = new Rendernode(gl, { 17 | program: get('moments.shader'), 18 | type: floatExt.type, 19 | drawable: quad 20 | }); 21 | this.blur = new Blur(gl, { 22 | type: floatExt.type 23 | }); 24 | this.output = new Rendernode(gl, { 25 | program: get('ssao.shader'), 26 | drawable: quad 27 | }); 28 | } 29 | 30 | SSAO.prototype.update = function() { 31 | this.moments.start().sampler('normaldepth', this.normaldepth).f('range', 42).clear().draw().end(); 32 | this.blur.update(this.moments); 33 | return this.output.start().sampler('normaldepth', this.normaldepth).sampler('momentsmap', this.blur.output).f('range', 42).clear().draw().end(); 34 | }; 35 | 36 | SSAO.prototype.resize = function(width, height) { 37 | this.moments.resize(width / 2, height / 2); 38 | this.blur.resize(width / 4, height / 4); 39 | return this.output.resize(width, height); 40 | }; 41 | 42 | return SSAO; 43 | 44 | })(); 45 | -------------------------------------------------------------------------------- /src/ssao/moments.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D normaldepth; 10 | uniform vec2 viewport; 11 | uniform float range; 12 | 13 | void main(){ 14 | vec4 data = texture2D(normaldepth, gl_FragCoord.xy/viewport); 15 | float depth = data.w; 16 | float scaled = clamp(depth/range, 0.0, 1.0); 17 | gl_FragColor = vec4(scaled, scaled*scaled, 0.0, 1.0); 18 | //float dx = dFdx(scaled); 19 | //float dy = dFdy(scaled); 20 | //gl_FragColor = vec4(scaled, scaled*scaled + 0.25*(dx*dx + dy*dy), 0.0, 1.0); 21 | } 22 | 23 | -------------------------------------------------------------------------------- /src/ssao/ssao.shader: -------------------------------------------------------------------------------- 1 | vertex: 2 | attribute vec2 position; 3 | 4 | void main(){ 5 | gl_Position = vec4(position, 0.0, 1.0); 6 | } 7 | 8 | fragment: 9 | uniform sampler2D normaldepth, momentsmap; 10 | uniform vec2 viewport; 11 | uniform float range; 12 | 13 | float linstep(float low, float high, float v){ 14 | return clamp((v-low)/(high-low), 0.0, 1.0); 15 | } 16 | 17 | float getOcclusion(float depth, vec2 moments, float offset){ 18 | float p = smoothstep(depth-offset/range, depth, moments.x); 19 | //float p = step(depth-offset/range, moments.x); 20 | float variance = moments.y - moments.x*moments.x; 21 | float d = depth - moments.x; 22 | float p_max = variance/(variance+d*d); 23 | p_max = smoothstep(0.5, 1.0, p_max); 24 | return 1.0-clamp(max(p, p_max), 0.0, 1.0); 25 | } 26 | 27 | void main(){ 28 | float depth = clamp(texture2D(normaldepth, gl_FragCoord.xy/viewport).w/range, 0.0, 1.0); 29 | vec2 moments = texture2D(momentsmap, gl_FragCoord.xy/viewport).xy; 30 | float factor = smoothstep((depth-0.2/range), depth, moments.x); 31 | float result = getOcclusion(depth, moments, 0.025)*clamp(factor, 0.0, 1.0); 32 | gl_FragColor = vec4(1.0-pow(result, 0.5)); 33 | } 34 | -------------------------------------------------------------------------------- /src/sun.coffee: -------------------------------------------------------------------------------- 1 | return class Sun extends require('events') 2 | constructor: (gui, @orientation=104, @elevation=60) -> 3 | gui.remember @ 4 | super() 5 | folder = gui.addFolder('Sun') 6 | folder.add(@, 'orientation', 0, 360).onChange(@update) 7 | folder.add(@, 'elevation', 0, 90).onChange(@update) 8 | 9 | @near = -1 10 | @far = 41 11 | @proj = new Mat4().ortho(@near, @far, 21, -21, -21, 21) 12 | @view = new Mat4() 13 | @rot = new Mat3() 14 | @update() 15 | 16 | update: => 17 | @view.identity() 18 | .translateVal3(0, 0, -21) 19 | .rotatex(@elevation) 20 | .rotatey(@orientation) 21 | .translateVal3(0, -7.5, 0) 22 | .toMat3 @rot.identity() 23 | 24 | @trigger('change') 25 | return @ 26 | 27 | -------------------------------------------------------------------------------- /src/sun.js: -------------------------------------------------------------------------------- 1 | // Generated by CoffeeScript 1.3.3 2 | var Sun, 3 | __bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, 4 | __hasProp = {}.hasOwnProperty, 5 | __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }; 6 | 7 | return Sun = (function(_super) { 8 | 9 | __extends(Sun, _super); 10 | 11 | function Sun(gui, orientation, elevation) { 12 | var folder; 13 | this.orientation = orientation != null ? orientation : 104; 14 | this.elevation = elevation != null ? elevation : 60; 15 | this.update = __bind(this.update, this); 16 | 17 | gui.remember(this); 18 | Sun.__super__.constructor.call(this); 19 | folder = gui.addFolder('Sun'); 20 | folder.add(this, 'orientation', 0, 360).onChange(this.update); 21 | folder.add(this, 'elevation', 0, 90).onChange(this.update); 22 | this.near = -1; 23 | this.far = 41; 24 | this.proj = new Mat4().ortho(this.near, this.far, 21, -21, -21, 21); 25 | this.view = new Mat4(); 26 | this.rot = new Mat3(); 27 | this.update(); 28 | } 29 | 30 | Sun.prototype.update = function() { 31 | this.view.identity().translateVal3(0, 0, -21).rotatex(this.elevation).rotatey(this.orientation).translateVal3(0, -7.5, 0).toMat3(this.rot.identity()); 32 | this.trigger('change'); 33 | return this; 34 | }; 35 | 36 | return Sun; 37 | 38 | })(require('events')); 39 | -------------------------------------------------------------------------------- /src/windows/module.coffee: -------------------------------------------------------------------------------- 1 | Quad = require '/webgl/quad' 2 | Rendernode = require '/rendernode' 3 | keys = require '/keys' 4 | 5 | class Window 6 | constructor: (@index, @texture, @node, @x, @y) -> 7 | @label = @texture.label 8 | 9 | if @texture.diva 10 | @diva = 1 11 | else 12 | @diva = 0 13 | 14 | if @texture.gamma == false 15 | @gamma = 0 16 | else 17 | @gamma = 1 18 | 19 | if @texture.affine 20 | @mul = @texture.affine[0] 21 | @add = @texture.affine[1] 22 | else 23 | @mul = 1 24 | @add = 0 25 | 26 | draw: (xscale, yscale, one2one, cx, cy, active) -> 27 | width = @texture.tex.width 28 | height = @texture.tex.height 29 | max = Math.max width, height 30 | 31 | w = width/max 32 | h = height/max 33 | 34 | s = 1/Math.max(w*xscale, h*yscale) 35 | w = w * (1-one2one) + s*w*one2one 36 | h = h * (1-one2one) + s*h*one2one 37 | 38 | @node 39 | .sampler('source', @texture.tex) 40 | .f('mixgamma', @gamma) 41 | .f('diva', @diva) 42 | .f('border_factor', active) 43 | .val2('affine', @mul, @add) 44 | .val2('size', w*xscale, h*yscale) 45 | .val2('offset', (@x-cx)*xscale, (@y-cy)*yscale) 46 | .draw() 47 | 48 | return class Windows 49 | constructor: (@gl, gui, @textures) -> 50 | gui.remember @ 51 | 52 | @label = $('
test
').appendTo('#ui').hide() 53 | @show_all = false 54 | @show_label = false 55 | @needs_clear = $.browser.mozilla 56 | #console.log @needs_clear 57 | 58 | @node = new Rendernode @gl, 59 | front: true 60 | program: get 'window.shader' 61 | drawable: quad 62 | 63 | @windows = [] 64 | @labelmap = {} 65 | labels = [] 66 | 67 | gridsize = Math.ceil(Math.sqrt(@textures.length)) 68 | for texture, i in @textures 69 | x = i%gridsize 70 | y = gridsize - Math.floor(i/gridsize) - 1 71 | window = new Window i, texture, @node, x*2.2, y*2.2 72 | @labelmap[window.label] = window 73 | @windows.push window 74 | labels.push window.label 75 | 76 | minx = null 77 | maxx = null 78 | miny = null 79 | maxy = null 80 | 81 | for window in @windows 82 | minx = if minx!=null then Math.min(window.x, minx) else window.x 83 | maxx = if maxx!=null then Math.max(window.x, maxx) else window.x 84 | miny = if miny!=null then Math.min(window.y, miny) else window.y 85 | maxy = if maxy!=null then Math.max(window.y, maxy) else window.y 86 | 87 | @cx = (minx+maxx)/2 88 | @cy = (miny+maxy)/2 89 | @full_scale = 1.9/Math.min(maxx-minx+2, maxy-miny+2) 90 | 91 | @zoom = 0.0 92 | 93 | @active = @windows.length - 1 94 | keys.press 'right', @next 95 | keys.press 'left', @prev 96 | 97 | keys.press 'down', => 98 | new_value = @active + gridsize 99 | if new_value < @windows.length 100 | @active = new_value 101 | else 102 | @active = @active % gridsize 103 | @setActive() 104 | 105 | keys.press 'up', => 106 | new_value = @active - gridsize 107 | if new_value >= 0 108 | @active = new_value 109 | else 110 | new_value = gridsize*gridsize + new_value 111 | while new_value >= @windows.length 112 | new_value -= gridsize 113 | @active = new_value 114 | @setActive() 115 | 116 | keys.press 'space', => 117 | @show_all = not @show_all 118 | @all_ctrl.setValue(@show_all) 119 | keys.press 'enter', => 120 | @show_all = not @show_all 121 | @all_ctrl.setValue(@show_all) 122 | 123 | active = @getActive() 124 | @x = active.x 125 | @y = active.y 126 | 127 | folder = gui.addFolder('Views') 128 | @all_ctrl = folder.add(@, 'show_all').name('Overview') 129 | folder.add(@, 'next').name('Next view') 130 | folder.add(@, 'prev').name('Prev view') 131 | 132 | @window_label = active.label 133 | @guiLabel = folder.add(@, 'window_label', labels).name('View').onChange(@guiLabelChange) 134 | folder.add(@, 'show_label').name('Labels').onChange @labelVisibilityChange 135 | @guiLabelChange() 136 | 137 | labelVisibilityChange: => 138 | if @show_label 139 | @label.clearQueue().fadeIn() 140 | else 141 | @label.clearQueue().fadeOut() 142 | 143 | 144 | guiLabelChange: => 145 | window = @labelmap[@window_label] 146 | @active = window.index 147 | @setActive() 148 | 149 | getActive: -> @windows[@active] 150 | 151 | setActive: -> 152 | text = @getActive().label 153 | @window_label = text 154 | @guiLabel.updateDisplay() 155 | @label.text(text) 156 | 157 | next: => 158 | @active = (@active+1) % @windows.length 159 | @setActive() 160 | prev: => 161 | if @active == 0 162 | @active = @windows.length - 1 163 | else 164 | @active -= 1 165 | @setActive() 166 | 167 | step: -> 168 | active = @getActive() 169 | tx = active.x 170 | ty = active.y 171 | 172 | @x = @x+(tx-@x)*0.1 173 | @y = @y+(ty-@y)*0.1 174 | 175 | if @show_all 176 | @zoom = @zoom + (1-@zoom)*0.1 177 | else 178 | @zoom = @zoom + (0-@zoom)*0.1 179 | 180 | draw: (gamma) -> 181 | @step() 182 | if @needs_clear == true 183 | @gl.clearColor 0, 0, 0, 0 184 | @gl.clear @gl.COLOR_BUFFER_BIT | @gl.DEPTH_BUFFER_BIT 185 | @node 186 | .start() 187 | .f('gamma', gamma) 188 | 189 | width = @node.width 190 | height = @node.height 191 | 192 | if width > height 193 | xscale = height/width 194 | yscale = 1 195 | else 196 | xscale = 1 197 | yscale = width/height 198 | 199 | factor = 1.0-@zoom + @zoom * @full_scale 200 | #factor = @full_scale 201 | xscale *= factor 202 | yscale *= factor 203 | 204 | active = @getActive() 205 | 206 | for window in @windows 207 | if window != active 208 | @drawWindow xscale, yscale, window, 0 209 | 210 | @drawWindow xscale, yscale, active, 1 211 | 212 | @node.end() 213 | 214 | drawWindow: (xscale, yscale, window, active) -> 215 | dx = window.x - @x 216 | dy = window.y - @y 217 | l = Math.sqrt(dx*dx+dy*dy) 218 | if l > 0 219 | one2one = Math.min(1/(l*2), 1) 220 | else 221 | one2one = 1 222 | 223 | x = @x*(1-@zoom) + @cx*@zoom 224 | y = @y*(1-@zoom) + @cy*@zoom 225 | active = Math.pow(one2one, 2.0) * @zoom 226 | 227 | window.draw(xscale, yscale, one2one*(1-@zoom), x, y, active) 228 | 229 | -------------------------------------------------------------------------------- /src/windows/window.shader: -------------------------------------------------------------------------------- 1 | varying vec2 vTexcoord, vPosition; 2 | 3 | vertex: 4 | attribute vec2 position; 5 | uniform vec2 size; 6 | uniform vec2 offset; 7 | 8 | void main(){ 9 | vPosition = position; 10 | vTexcoord = position * 0.5 + 0.5; 11 | gl_Position = vec4(position*size+offset, 0.0, 1.0); 12 | } 13 | 14 | fragment: 15 | #extension GL_OES_standard_derivatives : enable 16 | uniform sampler2D source; 17 | uniform float diva, gamma, mixgamma, border_factor; 18 | uniform vec2 affine; 19 | 20 | float edgeFactor(vec2 src){ 21 | vec2 d = fwidth(src); 22 | vec2 a3 = smoothstep(vec2(0.0), d*1.5, src); 23 | return min(a3.x, a3.y); 24 | } 25 | 26 | void main(){ 27 | vec4 data = texture2D(source, vTexcoord); 28 | vec3 transformed = data.rgb*affine.x + affine.y; 29 | vec3 color; 30 | if(data.a == 0.0){ 31 | color = transformed; 32 | } 33 | else{ 34 | color = mix(transformed, data.rgb/data.a, diva); 35 | } 36 | vec3 corrected = pow(color, vec3(1.0/gamma)); 37 | vec3 output_color = mix(color, corrected, mixgamma); 38 | vec3 clamped = clamp(output_color, vec3(0.0), vec3(1.0)); 39 | 40 | vec2 dpos = fwidth(vPosition); 41 | vec2 pos = smoothstep(1.0-dpos*4.0, vec2(1.0), abs(vPosition)); 42 | float near = border_factor * max(pos.x, pos.y); 43 | gl_FragColor = vec4(mix(clamped, vec3(1.0, 0.5, 0.0), near), 1.0); 44 | } 45 | -------------------------------------------------------------------------------- /www/assets.pack: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pyalot/webgl-deferred-irradiance-volumes/72513c3cc92794e932374ff3d12f026de34de98a/www/assets.pack -------------------------------------------------------------------------------- /www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Deferred Irradiance Volumes 5 | 6 | 68 | 69 | 70 |
71 | Blog Entry 72 | 73 | 74 | --------------------------------------------------------------------------------