├── .gitignore ├── .gitmodules ├── CNAME ├── README.md ├── cubemaps └── Tropical Beach │ ├── back.jpg │ ├── bottom.jpg │ ├── front.jpg │ ├── left.jpg │ ├── right.jpg │ └── top.jpg ├── external ├── css │ ├── materialize.css │ └── materialize.min.css ├── fonts │ └── roboto │ │ ├── Roboto-Bold.eot │ │ ├── Roboto-Bold.ttf │ │ ├── Roboto-Bold.woff │ │ ├── Roboto-Bold.woff2 │ │ ├── Roboto-Light.eot │ │ ├── Roboto-Light.ttf │ │ ├── Roboto-Light.woff │ │ ├── Roboto-Light.woff2 │ │ ├── Roboto-Medium.eot │ │ ├── Roboto-Medium.ttf │ │ ├── Roboto-Medium.woff │ │ ├── Roboto-Medium.woff2 │ │ ├── Roboto-Regular.eot │ │ ├── Roboto-Regular.ttf │ │ ├── Roboto-Regular.woff │ │ ├── Roboto-Regular.woff2 │ │ ├── Roboto-Thin.eot │ │ ├── Roboto-Thin.ttf │ │ ├── Roboto-Thin.woff │ │ └── Roboto-Thin.woff2 └── js │ ├── jquery.min.js │ ├── materialize.min.js │ └── stats.min.js ├── index.css ├── index.html ├── index.js ├── shaders ├── pathtracing │ └── raytracer.glsl ├── tonemapper.glsl └── vert.glsl ├── textures ├── rgba_noise │ ├── rgba_noise1024.png │ ├── rgba_noise2048.png │ ├── rgba_noise256.png │ └── rgba_noise512.png ├── tex0.png ├── tex1.png ├── tex2.png └── tex3.png ├── tools.js └── vector.js /.gitignore: -------------------------------------------------------------------------------- 1 | camera.js 2 | bbox.js 3 | mesh.js 4 | bvh.js 5 | gpurt.js 6 | .atom-live-server.json 7 | /shaders/pathtracing/MIS.glsl 8 | /models/Stanford/Happy Buddha.obj 9 | cubemaps -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/nWGL"] 2 | path = external/nWGL 3 | url = https://github.com/Mourtz/nWGL 4 | -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | webgl2-raytracer.amourtziapis.com -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Pathtracing using the WebGL API 2 | 3 | This is a **not** functional raytracer. 4 | It is entirely written in Javascript, using the **WebGL2** API to carry out all computations. 5 | Is kinda modular, allowing the end user to toggle some basic functionalities like "**un/biased** rendering", "**procedural sky**", 6 | "next event estimation" and "**Bi-Directional** path tracing". 7 | 8 | On low end devices, the **GLSL** compiler is usually crashing due to the large amount of code 9 | that is being passed by the main **fragment** shader. 10 | Of course the amount of computational power needed to run the renderer depends on the complexity of the scene. 11 | On my current system I have a GTX 960m and the driver doesn't crash that regularly, so any graphics card with 1.5 GFLOPS and above should be running fine. 12 | 13 | It seems to be working fine on Windows using Angle, Google Chrome is highly suggested as it gives me the lowest render times among 14 | of all the browsers I 've tested. 15 | 16 | ### List of features 17 | * generative texture mapping 18 | * procedurally generated textures 19 | * supports **color**, **emission** and **specular** textures 20 | * supports raytracing of **Quadric** & **Euclidean** surfaces 21 | * supports **raymarching** of signed distance fields 22 | * **multiple** light sampling 23 | * **Reinhard** tone mapping 24 | * cosine weighted **importance sampling** 25 | 26 | [**Demo**](https://mourtz.github.io/raytracer-0/) (Let it fetch all the required data before hitting the render button!) 27 | 28 | --- 29 | 30 | ### @ToDo List 31 | * bump mapping 32 | * split the main shader into three (**1st** for eye path tracing, **2nd** for light path tracing, **3rd** for merging the paths) to reduce the probability of the driver crashing during compilation. 33 | * HLBVH support(some work has been done already) 34 | * participating media 35 | * volumetric SDF 36 | * functional camera controls 37 | * BSSRDF 38 | 39 | ## Future Plans 40 | I have already started working on my next raytracer written in C++. The library I chose to use for GPU acceleration is **OpenCL** 1.2 by Khronos. 41 | However, **Arrayfire** seems like a good tool I could make use of in the future. 42 | 43 | ## Gallery 44 | ![alt text](https://lh3.googleusercontent.com/jTXK1CFNzp9Fg8qose2BI3qdcA1xQX1YAPDUekB7av0U4dfk777A1yy78dO1ibnvU-S4VSY0d2dOxuu6-O4g3KFd_nBECdUTmnXWKUv3Km2nxodMkS827Guhd9A_H4eOcxdzrtIS8T8I8C5xuYq20k_eUnZNNCuE6nCOLV5Ljsd9E28EFVTufcXEv4yByu4XNErTgAp_CYJIcr-_qDtNyRWwmkWSGn23iZ3h1R7wGRrXMu9aTv8vN4JEdQOfMpbgh7OKFE_I0QyU9QkEJEgwfBnbzpy3dg-7izfLb2M0oWVxAXQzIXrvIbTlc-657EPFC8MuWiA_skTwZHtfdC2n7-fXlGrPrdJqR-e1_Fsgfgm6wetNp90dmGepklehN66WH8ljT1Gxw5tKNNhhHRAH74zTJoJLOh13l3_Xo06ocE5ngBO4SUOX7e2U5mVloHk_M7F6z9DMJ_EJ3EIcYmYmNcaFnALkxoEhW9KwuULn0SGq_df3CKTn5dlfzy2ozH6u73l1tY3xfkIs-0rZIsOZ3fL1WIflwPtwyn0MP_Tc5SPknnJ1bfc6pU8n5moUmZ838NAMsNKFJsmhwOC9UsD83J5c1wJH2J4xaR6XASdo_WZZ1uDBgU4hE_Ka1GDPGc5DTBSkJO6C-8PY0UC76RNGX9dCQBPkBA8yezI=s936-no) 45 | 46 | ![alt text](https://lh3.googleusercontent.com/g3vbb8KmefWG8mxd1orXT0TauAkQAQenXuIHnN1sKt29lmzuT_FLnKdtmmtU-1w8brZqn0yXvRfE_b-vrLBXKmq9oVTJ9s1PEkrhwOOnLnL7bo7rLd1AJgpow1zoy3reEUAURux5eJO1dqQgSwBAiYXxnv-_03L1KGZsHw3fTUCc3rOKhFo8dvVa6uRMTmokfNIiIRLFpDK8QKuygLOka4i_au2_HEfLZmWtPtIaOPJMzWT9_jWhJ_lEz0HzF1HwZZR-8PkpGgNfPlvdM5syaFxEsCnHWg4dwep6yoUqHHSZaBb4MhCDx_LjNbPV89JQhhgkkDGqCewY2thD5sk2P0LfDgMv4lbR-S5iKi3YR0RxFh2iOS0TCNMW6BYFHr65jUf2TYxMBzHPL8yVIGUGqIx79tSb4p6bBpjQ2ZEgwxJi2P3u4k1yDsAJzk2HY81NuhE-kthjzeG5T4V6FcQGC7M6QWeZBUHtA0-4Cm-6KvHSJ9K7MnNU0VoIYrFilFTtt9ljKPqWIZhYdL-f1gZERb6oNV_Lv-OIm93PBHf0YCryumA_XzJIWbE1jqNky9L5S3QgPyO7RL0viT8lSmZufgsIZKqC0R_wkux5rqzFwjNA6_Cs7ZHt23xKI1PxhmWOm9Pm7oWXy2bAq26DddWxcHLKfoCrZV91oJc=s936-no) 47 | 48 | ![alt text](https://lh3.googleusercontent.com/j82WJfHnX_PdGW_bkmX77cV2FRjgH9Egr4WC6Fnt8_z_UA8Os92NNOWmc1dXoWcKahJ4TPilBaLF0okbMzES2tMqA9oLV9NleOeUPNmLrGhJe7cBnQGeDHD5tUY6ypc-4JWaqCAvYQvqohnW8h7jqQ0Z3Tgs0vd305GI_B7LOsYgXpHjncEATer3AwlI4jGlshvmXh99bCAJnpjDOo3V0XQ9O8k5acUGYB6jxpj6Ag_8bnqB7px037LZFPsLHQWOIuYgusHLlTh_GsztoWo8xczKq42ayjYEq98jk0wNauFmyZiSozI6Ut8umGJxm--_8tJpFYMvBbjHmcMCCZJgiM--6tAueOyyqcjBoWAF3YarfpxyD9Nd9kqrpy-IJcSCkhsHJns8bJFd8O97xHVuKmtp8S-okfd9JJF5uyd6DkKkIVCC5fjrvc1bWNn1Z6_T2wIDA3MJyvuCRG9Ko2h-5S1IPzM5vy4Uss0JMuZJT_UC_0E461xynUTimRMjXh2JJlx_Vm3xeS41x0rTBvIj5L52rRYSJWQgeCLdMblb37CGFVj3YDMXvPIX4FQTqrRqzDQxevMHFU6tKtCxcWMhLSXX2ekaYq3vwbd8PJDmyr3eX4R4ZUcp0L1FyijpbDeSOafUknXYVHqp51o5Il76xa1nGSInGj9psE8=s936-no) 49 | 50 | [Show more](https://goo.gl/photos/4zafwXUs4Ph9rux48) 51 | 52 | ## Credits 53 | [iq](http://www.iquilezles.org/) - Inigo quilez
54 | [Toshiya Hachisuka](http://www.ci.i.u-tokyo.ac.jp/~hachisuka/) - Toshiya Hachisuka
55 | [reinder](http://reindernijhoff.net/) - Reinder Nijhoff
56 | [erichlof](https://github.com/erichlof) - Erich Loftis
57 | -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/back.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/back.jpg -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/bottom.jpg -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/front.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/front.jpg -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/left.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/left.jpg -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/right.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/right.jpg -------------------------------------------------------------------------------- /cubemaps/Tropical Beach/top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/cubemaps/Tropical Beach/top.jpg -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Bold.eot -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Bold.ttf -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Bold.woff -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Bold.woff2 -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Light.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Light.eot -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Light.ttf -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Light.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Light.woff -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Light.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Light.woff2 -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Medium.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Medium.eot -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Medium.ttf -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Medium.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Medium.woff -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Medium.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Medium.woff2 -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Regular.eot -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Regular.ttf -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Regular.woff -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Regular.woff2 -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Thin.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Thin.eot -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Thin.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Thin.ttf -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Thin.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Thin.woff -------------------------------------------------------------------------------- /external/fonts/roboto/Roboto-Thin.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/external/fonts/roboto/Roboto-Thin.woff2 -------------------------------------------------------------------------------- /external/js/jquery.min.js: -------------------------------------------------------------------------------- 1 | /*! jQuery v3.2.1 | (c) JS Foundation and other contributors | jquery.org/license */ 2 | !function(a,b){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){"use strict";var c=[],d=a.document,e=Object.getPrototypeOf,f=c.slice,g=c.concat,h=c.push,i=c.indexOf,j={},k=j.toString,l=j.hasOwnProperty,m=l.toString,n=m.call(Object),o={};function p(a,b){b=b||d;var c=b.createElement("script");c.text=a,b.head.appendChild(c).parentNode.removeChild(c)}var q="3.2.1",r=function(a,b){return new r.fn.init(a,b)},s=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,t=/^-ms-/,u=/-([a-z])/g,v=function(a,b){return b.toUpperCase()};r.fn=r.prototype={jquery:q,constructor:r,length:0,toArray:function(){return f.call(this)},get:function(a){return null==a?f.call(this):a<0?this[a+this.length]:this[a]},pushStack:function(a){var b=r.merge(this.constructor(),a);return b.prevObject=this,b},each:function(a){return r.each(this,a)},map:function(a){return this.pushStack(r.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(f.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(a<0?b:0);return this.pushStack(c>=0&&c0&&b-1 in a)}var x=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+1*new Date,v=a.document,w=0,x=0,y=ha(),z=ha(),A=ha(),B=function(a,b){return a===b&&(l=!0),0},C={}.hasOwnProperty,D=[],E=D.pop,F=D.push,G=D.push,H=D.slice,I=function(a,b){for(var c=0,d=a.length;c+~]|"+K+")"+K+"*"),S=new RegExp("="+K+"*([^\\]'\"]*?)"+K+"*\\]","g"),T=new RegExp(N),U=new RegExp("^"+L+"$"),V={ID:new RegExp("^#("+L+")"),CLASS:new RegExp("^\\.("+L+")"),TAG:new RegExp("^("+L+"|[*])"),ATTR:new RegExp("^"+M),PSEUDO:new RegExp("^"+N),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+K+"*(even|odd|(([+-]|)(\\d*)n|)"+K+"*(?:([+-]|)"+K+"*(\\d+)|))"+K+"*\\)|)","i"),bool:new RegExp("^(?:"+J+")$","i"),needsContext:new RegExp("^"+K+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+K+"*((?:-\\d)?\\d*)"+K+"*\\)|)(?=[^-]|$)","i")},W=/^(?:input|select|textarea|button)$/i,X=/^h\d$/i,Y=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,$=/[+~]/,_=new RegExp("\\\\([\\da-f]{1,6}"+K+"?|("+K+")|.)","ig"),aa=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:d<0?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)},ba=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ca=function(a,b){return b?"\0"===a?"\ufffd":a.slice(0,-1)+"\\"+a.charCodeAt(a.length-1).toString(16)+" ":"\\"+a},da=function(){m()},ea=ta(function(a){return a.disabled===!0&&("form"in a||"label"in a)},{dir:"parentNode",next:"legend"});try{G.apply(D=H.call(v.childNodes),v.childNodes),D[v.childNodes.length].nodeType}catch(fa){G={apply:D.length?function(a,b){F.apply(a,H.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function ga(a,b,d,e){var f,h,j,k,l,o,r,s=b&&b.ownerDocument,w=b?b.nodeType:9;if(d=d||[],"string"!=typeof a||!a||1!==w&&9!==w&&11!==w)return d;if(!e&&((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,p)){if(11!==w&&(l=Z.exec(a)))if(f=l[1]){if(9===w){if(!(j=b.getElementById(f)))return d;if(j.id===f)return d.push(j),d}else if(s&&(j=s.getElementById(f))&&t(b,j)&&j.id===f)return d.push(j),d}else{if(l[2])return G.apply(d,b.getElementsByTagName(a)),d;if((f=l[3])&&c.getElementsByClassName&&b.getElementsByClassName)return G.apply(d,b.getElementsByClassName(f)),d}if(c.qsa&&!A[a+" "]&&(!q||!q.test(a))){if(1!==w)s=b,r=a;else if("object"!==b.nodeName.toLowerCase()){(k=b.getAttribute("id"))?k=k.replace(ba,ca):b.setAttribute("id",k=u),o=g(a),h=o.length;while(h--)o[h]="#"+k+" "+sa(o[h]);r=o.join(","),s=$.test(a)&&qa(b.parentNode)||b}if(r)try{return G.apply(d,s.querySelectorAll(r)),d}catch(x){}finally{k===u&&b.removeAttribute("id")}}}return i(a.replace(P,"$1"),b,d,e)}function ha(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function ia(a){return a[u]=!0,a}function ja(a){var b=n.createElement("fieldset");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function ka(a,b){var c=a.split("|"),e=c.length;while(e--)d.attrHandle[c[e]]=b}function la(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&a.sourceIndex-b.sourceIndex;if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function ma(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function na(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function oa(a){return function(b){return"form"in b?b.parentNode&&b.disabled===!1?"label"in b?"label"in b.parentNode?b.parentNode.disabled===a:b.disabled===a:b.isDisabled===a||b.isDisabled!==!a&&ea(b)===a:b.disabled===a:"label"in b&&b.disabled===a}}function pa(a){return ia(function(b){return b=+b,ia(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function qa(a){return a&&"undefined"!=typeof a.getElementsByTagName&&a}c=ga.support={},f=ga.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return!!b&&"HTML"!==b.nodeName},m=ga.setDocument=function(a){var b,e,g=a?a.ownerDocument||a:v;return g!==n&&9===g.nodeType&&g.documentElement?(n=g,o=n.documentElement,p=!f(n),v!==n&&(e=n.defaultView)&&e.top!==e&&(e.addEventListener?e.addEventListener("unload",da,!1):e.attachEvent&&e.attachEvent("onunload",da)),c.attributes=ja(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ja(function(a){return a.appendChild(n.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=Y.test(n.getElementsByClassName),c.getById=ja(function(a){return o.appendChild(a).id=u,!n.getElementsByName||!n.getElementsByName(u).length}),c.getById?(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){return a.getAttribute("id")===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c=b.getElementById(a);return c?[c]:[]}}):(d.filter.ID=function(a){var b=a.replace(_,aa);return function(a){var c="undefined"!=typeof a.getAttributeNode&&a.getAttributeNode("id");return c&&c.value===b}},d.find.ID=function(a,b){if("undefined"!=typeof b.getElementById&&p){var c,d,e,f=b.getElementById(a);if(f){if(c=f.getAttributeNode("id"),c&&c.value===a)return[f];e=b.getElementsByName(a),d=0;while(f=e[d++])if(c=f.getAttributeNode("id"),c&&c.value===a)return[f]}return[]}}),d.find.TAG=c.getElementsByTagName?function(a,b){return"undefined"!=typeof b.getElementsByTagName?b.getElementsByTagName(a):c.qsa?b.querySelectorAll(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){if("undefined"!=typeof b.getElementsByClassName&&p)return b.getElementsByClassName(a)},r=[],q=[],(c.qsa=Y.test(n.querySelectorAll))&&(ja(function(a){o.appendChild(a).innerHTML="",a.querySelectorAll("[msallowcapture^='']").length&&q.push("[*^$]="+K+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+K+"*(?:value|"+J+")"),a.querySelectorAll("[id~="+u+"-]").length||q.push("~="),a.querySelectorAll(":checked").length||q.push(":checked"),a.querySelectorAll("a#"+u+"+*").length||q.push(".#.+[+~]")}),ja(function(a){a.innerHTML="";var b=n.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+K+"*[*^$|!~]?="),2!==a.querySelectorAll(":enabled").length&&q.push(":enabled",":disabled"),o.appendChild(a).disabled=!0,2!==a.querySelectorAll(":disabled").length&&q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=Y.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ja(function(a){c.disconnectedMatch=s.call(a,"*"),s.call(a,"[s!='']:x"),r.push("!=",N)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=Y.test(o.compareDocumentPosition),t=b||Y.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===n||a.ownerDocument===v&&t(v,a)?-1:b===n||b.ownerDocument===v&&t(v,b)?1:k?I(k,a)-I(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,e=a.parentNode,f=b.parentNode,g=[a],h=[b];if(!e||!f)return a===n?-1:b===n?1:e?-1:f?1:k?I(k,a)-I(k,b):0;if(e===f)return la(a,b);c=a;while(c=c.parentNode)g.unshift(c);c=b;while(c=c.parentNode)h.unshift(c);while(g[d]===h[d])d++;return d?la(g[d],h[d]):g[d]===v?-1:h[d]===v?1:0},n):n},ga.matches=function(a,b){return ga(a,null,null,b)},ga.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(S,"='$1']"),c.matchesSelector&&p&&!A[b+" "]&&(!r||!r.test(b))&&(!q||!q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return ga(b,n,null,[a]).length>0},ga.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},ga.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&C.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},ga.escape=function(a){return(a+"").replace(ba,ca)},ga.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},ga.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=ga.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=ga.selectors={cacheLength:50,createPseudo:ia,match:V,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(_,aa),a[3]=(a[3]||a[4]||a[5]||"").replace(_,aa),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||ga.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&ga.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return V.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&T.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(_,aa).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+K+")"+a+"("+K+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||"undefined"!=typeof a.getAttribute&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=ga.attr(d,a);return null==e?"!="===b:!b||(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e.replace(O," ")+" ").indexOf(c)>-1:"|="===b&&(e===c||e.slice(0,c.length+1)===c+"-"))}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h,t=!1;if(q){if(f){while(p){m=b;while(m=m[p])if(h?m.nodeName.toLowerCase()===r:1===m.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){m=q,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n&&j[2],m=n&&q.childNodes[n];while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if(1===m.nodeType&&++t&&m===b){k[a]=[w,n,t];break}}else if(s&&(m=b,l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),j=k[a]||[],n=j[0]===w&&j[1],t=n),t===!1)while(m=++n&&m&&m[p]||(t=n=0)||o.pop())if((h?m.nodeName.toLowerCase()===r:1===m.nodeType)&&++t&&(s&&(l=m[u]||(m[u]={}),k=l[m.uniqueID]||(l[m.uniqueID]={}),k[a]=[w,t]),m===b))break;return t-=e,t===d||t%d===0&&t/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||ga.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?ia(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=I(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:ia(function(a){var b=[],c=[],d=h(a.replace(P,"$1"));return d[u]?ia(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),b[0]=null,!c.pop()}}),has:ia(function(a){return function(b){return ga(a,b).length>0}}),contains:ia(function(a){return a=a.replace(_,aa),function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:ia(function(a){return U.test(a||"")||ga.error("unsupported lang: "+a),a=a.replace(_,aa).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:oa(!1),disabled:oa(!0),checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return X.test(a.nodeName)},input:function(a){return W.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:pa(function(){return[0]}),last:pa(function(a,b){return[b-1]}),eq:pa(function(a,b,c){return[c<0?c+b:c]}),even:pa(function(a,b){for(var c=0;c=0;)a.push(d);return a}),gt:pa(function(a,b,c){for(var d=c<0?c+b:c;++d1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function va(a,b,c){for(var d=0,e=b.length;d-1&&(f[j]=!(g[j]=l))}}else r=wa(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):G.apply(g,r)})}function ya(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=ta(function(a){return a===b},h,!0),l=ta(function(a){return I(b,a)>-1},h,!0),m=[function(a,c,d){var e=!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d));return b=null,e}];i1&&ua(m),i>1&&sa(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(P,"$1"),c,i0,e=a.length>0,f=function(f,g,h,i,k){var l,o,q,r=0,s="0",t=f&&[],u=[],v=j,x=f||e&&d.find.TAG("*",k),y=w+=null==v?1:Math.random()||.1,z=x.length;for(k&&(j=g===n||g||k);s!==z&&null!=(l=x[s]);s++){if(e&&l){o=0,g||l.ownerDocument===n||(m(l),h=!p);while(q=a[o++])if(q(l,g||n,h)){i.push(l);break}k&&(w=y)}c&&((l=!q&&l)&&r--,f&&t.push(l))}if(r+=s,c&&s!==r){o=0;while(q=b[o++])q(t,u,g,h);if(f){if(r>0)while(s--)t[s]||u[s]||(u[s]=E.call(i));u=wa(u)}G.apply(i,u),k&&!f&&u.length>0&&r+b.length>1&&ga.uniqueSort(i)}return k&&(w=y,j=v),t};return c?ia(f):f}return h=ga.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=ya(b[c]),f[u]?d.push(f):e.push(f);f=A(a,za(e,d)),f.selector=a}return f},i=ga.select=function(a,b,c,e){var f,i,j,k,l,m="function"==typeof a&&a,n=!e&&g(a=m.selector||a);if(c=c||[],1===n.length){if(i=n[0]=n[0].slice(0),i.length>2&&"ID"===(j=i[0]).type&&9===b.nodeType&&p&&d.relative[i[1].type]){if(b=(d.find.ID(j.matches[0].replace(_,aa),b)||[])[0],!b)return c;m&&(b=b.parentNode),a=a.slice(i.shift().value.length)}f=V.needsContext.test(a)?0:i.length;while(f--){if(j=i[f],d.relative[k=j.type])break;if((l=d.find[k])&&(e=l(j.matches[0].replace(_,aa),$.test(i[0].type)&&qa(b.parentNode)||b))){if(i.splice(f,1),a=e.length&&sa(i),!a)return G.apply(c,e),c;break}}}return(m||h(a,n))(e,b,!p,c,!b||$.test(a)&&qa(b.parentNode)||b),c},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ja(function(a){return 1&a.compareDocumentPosition(n.createElement("fieldset"))}),ja(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||ka("type|href|height|width",function(a,b,c){if(!c)return a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ja(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||ka("value",function(a,b,c){if(!c&&"input"===a.nodeName.toLowerCase())return a.defaultValue}),ja(function(a){return null==a.getAttribute("disabled")})||ka(J,function(a,b,c){var d;if(!c)return a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),ga}(a);r.find=x,r.expr=x.selectors,r.expr[":"]=r.expr.pseudos,r.uniqueSort=r.unique=x.uniqueSort,r.text=x.getText,r.isXMLDoc=x.isXML,r.contains=x.contains,r.escapeSelector=x.escape;var y=function(a,b,c){var d=[],e=void 0!==c;while((a=a[b])&&9!==a.nodeType)if(1===a.nodeType){if(e&&r(a).is(c))break;d.push(a)}return d},z=function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c},A=r.expr.match.needsContext;function B(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()}var C=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i,D=/^.[^:#\[\.,]*$/;function E(a,b,c){return r.isFunction(b)?r.grep(a,function(a,d){return!!b.call(a,d,a)!==c}):b.nodeType?r.grep(a,function(a){return a===b!==c}):"string"!=typeof b?r.grep(a,function(a){return i.call(b,a)>-1!==c}):D.test(b)?r.filter(b,a,c):(b=r.filter(b,a),r.grep(a,function(a){return i.call(b,a)>-1!==c&&1===a.nodeType}))}r.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?r.find.matchesSelector(d,a)?[d]:[]:r.find.matches(a,r.grep(b,function(a){return 1===a.nodeType}))},r.fn.extend({find:function(a){var b,c,d=this.length,e=this;if("string"!=typeof a)return this.pushStack(r(a).filter(function(){for(b=0;b1?r.uniqueSort(c):c},filter:function(a){return this.pushStack(E(this,a||[],!1))},not:function(a){return this.pushStack(E(this,a||[],!0))},is:function(a){return!!E(this,"string"==typeof a&&A.test(a)?r(a):a||[],!1).length}});var F,G=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,H=r.fn.init=function(a,b,c){var e,f;if(!a)return this;if(c=c||F,"string"==typeof a){if(e="<"===a[0]&&">"===a[a.length-1]&&a.length>=3?[null,a,null]:G.exec(a),!e||!e[1]&&b)return!b||b.jquery?(b||c).find(a):this.constructor(b).find(a);if(e[1]){if(b=b instanceof r?b[0]:b,r.merge(this,r.parseHTML(e[1],b&&b.nodeType?b.ownerDocument||b:d,!0)),C.test(e[1])&&r.isPlainObject(b))for(e in b)r.isFunction(this[e])?this[e](b[e]):this.attr(e,b[e]);return this}return f=d.getElementById(e[2]),f&&(this[0]=f,this.length=1),this}return a.nodeType?(this[0]=a,this.length=1,this):r.isFunction(a)?void 0!==c.ready?c.ready(a):a(r):r.makeArray(a,this)};H.prototype=r.fn,F=r(d);var I=/^(?:parents|prev(?:Until|All))/,J={children:!0,contents:!0,next:!0,prev:!0};r.fn.extend({has:function(a){var b=r(a,this),c=b.length;return this.filter(function(){for(var a=0;a-1:1===c.nodeType&&r.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?r.uniqueSort(f):f)},index:function(a){return a?"string"==typeof a?i.call(r(a),this[0]):i.call(this,a.jquery?a[0]:a):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(r.uniqueSort(r.merge(this.get(),r(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function K(a,b){while((a=a[b])&&1!==a.nodeType);return a}r.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return y(a,"parentNode")},parentsUntil:function(a,b,c){return y(a,"parentNode",c)},next:function(a){return K(a,"nextSibling")},prev:function(a){return K(a,"previousSibling")},nextAll:function(a){return y(a,"nextSibling")},prevAll:function(a){return y(a,"previousSibling")},nextUntil:function(a,b,c){return y(a,"nextSibling",c)},prevUntil:function(a,b,c){return y(a,"previousSibling",c)},siblings:function(a){return z((a.parentNode||{}).firstChild,a)},children:function(a){return z(a.firstChild)},contents:function(a){return B(a,"iframe")?a.contentDocument:(B(a,"template")&&(a=a.content||a),r.merge([],a.childNodes))}},function(a,b){r.fn[a]=function(c,d){var e=r.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=r.filter(d,e)),this.length>1&&(J[a]||r.uniqueSort(e),I.test(a)&&e.reverse()),this.pushStack(e)}});var L=/[^\x20\t\r\n\f]+/g;function M(a){var b={};return r.each(a.match(L)||[],function(a,c){b[c]=!0}),b}r.Callbacks=function(a){a="string"==typeof a?M(a):r.extend({},a);var b,c,d,e,f=[],g=[],h=-1,i=function(){for(e=e||a.once,d=b=!0;g.length;h=-1){c=g.shift();while(++h-1)f.splice(c,1),c<=h&&h--}),this},has:function(a){return a?r.inArray(a,f)>-1:f.length>0},empty:function(){return f&&(f=[]),this},disable:function(){return e=g=[],f=c="",this},disabled:function(){return!f},lock:function(){return e=g=[],c||b||(f=c=""),this},locked:function(){return!!e},fireWith:function(a,c){return e||(c=c||[],c=[a,c.slice?c.slice():c],g.push(c),b||i()),this},fire:function(){return j.fireWith(this,arguments),this},fired:function(){return!!d}};return j};function N(a){return a}function O(a){throw a}function P(a,b,c,d){var e;try{a&&r.isFunction(e=a.promise)?e.call(a).done(b).fail(c):a&&r.isFunction(e=a.then)?e.call(a,b,c):b.apply(void 0,[a].slice(d))}catch(a){c.apply(void 0,[a])}}r.extend({Deferred:function(b){var c=[["notify","progress",r.Callbacks("memory"),r.Callbacks("memory"),2],["resolve","done",r.Callbacks("once memory"),r.Callbacks("once memory"),0,"resolved"],["reject","fail",r.Callbacks("once memory"),r.Callbacks("once memory"),1,"rejected"]],d="pending",e={state:function(){return d},always:function(){return f.done(arguments).fail(arguments),this},"catch":function(a){return e.then(null,a)},pipe:function(){var a=arguments;return r.Deferred(function(b){r.each(c,function(c,d){var e=r.isFunction(a[d[4]])&&a[d[4]];f[d[1]](function(){var a=e&&e.apply(this,arguments);a&&r.isFunction(a.promise)?a.promise().progress(b.notify).done(b.resolve).fail(b.reject):b[d[0]+"With"](this,e?[a]:arguments)})}),a=null}).promise()},then:function(b,d,e){var f=0;function g(b,c,d,e){return function(){var h=this,i=arguments,j=function(){var a,j;if(!(b=f&&(d!==O&&(h=void 0,i=[a]),c.rejectWith(h,i))}};b?k():(r.Deferred.getStackHook&&(k.stackTrace=r.Deferred.getStackHook()),a.setTimeout(k))}}return r.Deferred(function(a){c[0][3].add(g(0,a,r.isFunction(e)?e:N,a.notifyWith)),c[1][3].add(g(0,a,r.isFunction(b)?b:N)),c[2][3].add(g(0,a,r.isFunction(d)?d:O))}).promise()},promise:function(a){return null!=a?r.extend(a,e):e}},f={};return r.each(c,function(a,b){var g=b[2],h=b[5];e[b[1]]=g.add,h&&g.add(function(){d=h},c[3-a][2].disable,c[0][2].lock),g.add(b[3].fire),f[b[0]]=function(){return f[b[0]+"With"](this===f?void 0:this,arguments),this},f[b[0]+"With"]=g.fireWith}),e.promise(f),b&&b.call(f,f),f},when:function(a){var b=arguments.length,c=b,d=Array(c),e=f.call(arguments),g=r.Deferred(),h=function(a){return function(c){d[a]=this,e[a]=arguments.length>1?f.call(arguments):c,--b||g.resolveWith(d,e)}};if(b<=1&&(P(a,g.done(h(c)).resolve,g.reject,!b),"pending"===g.state()||r.isFunction(e[c]&&e[c].then)))return g.then();while(c--)P(e[c],h(c),g.reject);return g.promise()}});var Q=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;r.Deferred.exceptionHook=function(b,c){a.console&&a.console.warn&&b&&Q.test(b.name)&&a.console.warn("jQuery.Deferred exception: "+b.message,b.stack,c)},r.readyException=function(b){a.setTimeout(function(){throw b})};var R=r.Deferred();r.fn.ready=function(a){return R.then(a)["catch"](function(a){r.readyException(a)}),this},r.extend({isReady:!1,readyWait:1,ready:function(a){(a===!0?--r.readyWait:r.isReady)||(r.isReady=!0,a!==!0&&--r.readyWait>0||R.resolveWith(d,[r]))}}),r.ready.then=R.then;function S(){d.removeEventListener("DOMContentLoaded",S), 3 | a.removeEventListener("load",S),r.ready()}"complete"===d.readyState||"loading"!==d.readyState&&!d.documentElement.doScroll?a.setTimeout(r.ready):(d.addEventListener("DOMContentLoaded",S),a.addEventListener("load",S));var T=function(a,b,c,d,e,f,g){var h=0,i=a.length,j=null==c;if("object"===r.type(c)){e=!0;for(h in c)T(a,b,h,c[h],!0,f,g)}else if(void 0!==d&&(e=!0,r.isFunction(d)||(g=!0),j&&(g?(b.call(a,d),b=null):(j=b,b=function(a,b,c){return j.call(r(a),c)})),b))for(;h1,null,!0)},removeData:function(a){return this.each(function(){X.remove(this,a)})}}),r.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=W.get(a,b),c&&(!d||Array.isArray(c)?d=W.access(a,b,r.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=r.queue(a,b),d=c.length,e=c.shift(),f=r._queueHooks(a,b),g=function(){r.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return W.get(a,c)||W.access(a,c,{empty:r.Callbacks("once memory").add(function(){W.remove(a,[b+"queue",c])})})}}),r.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.length\x20\t\r\n\f]+)/i,la=/^$|\/(?:java|ecma)script/i,ma={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ma.optgroup=ma.option,ma.tbody=ma.tfoot=ma.colgroup=ma.caption=ma.thead,ma.th=ma.td;function na(a,b){var c;return c="undefined"!=typeof a.getElementsByTagName?a.getElementsByTagName(b||"*"):"undefined"!=typeof a.querySelectorAll?a.querySelectorAll(b||"*"):[],void 0===b||b&&B(a,b)?r.merge([a],c):c}function oa(a,b){for(var c=0,d=a.length;c-1)e&&e.push(f);else if(j=r.contains(f.ownerDocument,f),g=na(l.appendChild(f),"script"),j&&oa(g),c){k=0;while(f=g[k++])la.test(f.type||"")&&c.push(f)}return l}!function(){var a=d.createDocumentFragment(),b=a.appendChild(d.createElement("div")),c=d.createElement("input");c.setAttribute("type","radio"),c.setAttribute("checked","checked"),c.setAttribute("name","t"),b.appendChild(c),o.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,b.innerHTML="",o.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue}();var ra=d.documentElement,sa=/^key/,ta=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ua=/^([^.]*)(?:\.(.+)|)/;function va(){return!0}function wa(){return!1}function xa(){try{return d.activeElement}catch(a){}}function ya(a,b,c,d,e,f){var g,h;if("object"==typeof b){"string"!=typeof c&&(d=d||c,c=void 0);for(h in b)ya(a,h,c,d,b[h],f);return a}if(null==d&&null==e?(e=c,d=c=void 0):null==e&&("string"==typeof c?(e=d,d=void 0):(e=d,d=c,c=void 0)),e===!1)e=wa;else if(!e)return a;return 1===f&&(g=e,e=function(a){return r().off(a),g.apply(this,arguments)},e.guid=g.guid||(g.guid=r.guid++)),a.each(function(){r.event.add(this,b,e,d,c)})}r.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.get(a);if(q){c.handler&&(f=c,c=f.handler,e=f.selector),e&&r.find.matchesSelector(ra,e),c.guid||(c.guid=r.guid++),(i=q.events)||(i=q.events={}),(g=q.handle)||(g=q.handle=function(b){return"undefined"!=typeof r&&r.event.triggered!==b.type?r.event.dispatch.apply(a,arguments):void 0}),b=(b||"").match(L)||[""],j=b.length;while(j--)h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n&&(l=r.event.special[n]||{},n=(e?l.delegateType:l.bindType)||n,l=r.event.special[n]||{},k=r.extend({type:n,origType:p,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&r.expr.match.needsContext.test(e),namespace:o.join(".")},f),(m=i[n])||(m=i[n]=[],m.delegateCount=0,l.setup&&l.setup.call(a,d,o,g)!==!1||a.addEventListener&&a.addEventListener(n,g)),l.add&&(l.add.call(a,k),k.handler.guid||(k.handler.guid=c.guid)),e?m.splice(m.delegateCount++,0,k):m.push(k),r.event.global[n]=!0)}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q=W.hasData(a)&&W.get(a);if(q&&(i=q.events)){b=(b||"").match(L)||[""],j=b.length;while(j--)if(h=ua.exec(b[j])||[],n=p=h[1],o=(h[2]||"").split(".").sort(),n){l=r.event.special[n]||{},n=(d?l.delegateType:l.bindType)||n,m=i[n]||[],h=h[2]&&new RegExp("(^|\\.)"+o.join("\\.(?:.*\\.|)")+"(\\.|$)"),g=f=m.length;while(f--)k=m[f],!e&&p!==k.origType||c&&c.guid!==k.guid||h&&!h.test(k.namespace)||d&&d!==k.selector&&("**"!==d||!k.selector)||(m.splice(f,1),k.selector&&m.delegateCount--,l.remove&&l.remove.call(a,k));g&&!m.length&&(l.teardown&&l.teardown.call(a,o,q.handle)!==!1||r.removeEvent(a,n,q.handle),delete i[n])}else for(n in i)r.event.remove(a,n+b[j],c,d,!0);r.isEmptyObject(i)&&W.remove(a,"handle events")}},dispatch:function(a){var b=r.event.fix(a),c,d,e,f,g,h,i=new Array(arguments.length),j=(W.get(this,"events")||{})[b.type]||[],k=r.event.special[b.type]||{};for(i[0]=b,c=1;c=1))for(;j!==this;j=j.parentNode||this)if(1===j.nodeType&&("click"!==a.type||j.disabled!==!0)){for(f=[],g={},c=0;c-1:r.find(e,this,null,[j]).length),g[e]&&f.push(d);f.length&&h.push({elem:j,handlers:f})}return j=this,i\x20\t\r\n\f]*)[^>]*)\/>/gi,Aa=/\s*$/g;function Ea(a,b){return B(a,"table")&&B(11!==b.nodeType?b:b.firstChild,"tr")?r(">tbody",a)[0]||a:a}function Fa(a){return a.type=(null!==a.getAttribute("type"))+"/"+a.type,a}function Ga(a){var b=Ca.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function Ha(a,b){var c,d,e,f,g,h,i,j;if(1===b.nodeType){if(W.hasData(a)&&(f=W.access(a),g=W.set(b,f),j=f.events)){delete g.handle,g.events={};for(e in j)for(c=0,d=j[e].length;c1&&"string"==typeof q&&!o.checkClone&&Ba.test(q))return a.each(function(e){var f=a.eq(e);s&&(b[0]=q.call(this,e,f.html())),Ja(f,b,c,d)});if(m&&(e=qa(b,a[0].ownerDocument,!1,a,d),f=e.firstChild,1===e.childNodes.length&&(e=f),f||d)){for(h=r.map(na(e,"script"),Fa),i=h.length;l")},clone:function(a,b,c){var d,e,f,g,h=a.cloneNode(!0),i=r.contains(a.ownerDocument,a);if(!(o.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||r.isXMLDoc(a)))for(g=na(h),f=na(a),d=0,e=f.length;d0&&oa(g,!i&&na(a,"script")),h},cleanData:function(a){for(var b,c,d,e=r.event.special,f=0;void 0!==(c=a[f]);f++)if(U(c)){if(b=c[W.expando]){if(b.events)for(d in b.events)e[d]?r.event.remove(c,d):r.removeEvent(c,d,b.handle);c[W.expando]=void 0}c[X.expando]&&(c[X.expando]=void 0)}}}),r.fn.extend({detach:function(a){return Ka(this,a,!0)},remove:function(a){return Ka(this,a)},text:function(a){return T(this,function(a){return void 0===a?r.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=a)})},null,a,arguments.length)},append:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.appendChild(a)}})},prepend:function(){return Ja(this,arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=Ea(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return Ja(this,arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},empty:function(){for(var a,b=0;null!=(a=this[b]);b++)1===a.nodeType&&(r.cleanData(na(a,!1)),a.textContent="");return this},clone:function(a,b){return a=null!=a&&a,b=null==b?a:b,this.map(function(){return r.clone(this,a,b)})},html:function(a){return T(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a&&1===b.nodeType)return b.innerHTML;if("string"==typeof a&&!Aa.test(a)&&!ma[(ka.exec(a)||["",""])[1].toLowerCase()]){a=r.htmlPrefilter(a);try{for(;c1)}});function _a(a,b,c,d,e){return new _a.prototype.init(a,b,c,d,e)}r.Tween=_a,_a.prototype={constructor:_a,init:function(a,b,c,d,e,f){this.elem=a,this.prop=c,this.easing=e||r.easing._default,this.options=b,this.start=this.now=this.cur(),this.end=d,this.unit=f||(r.cssNumber[c]?"":"px")},cur:function(){var a=_a.propHooks[this.prop];return a&&a.get?a.get(this):_a.propHooks._default.get(this)},run:function(a){var b,c=_a.propHooks[this.prop];return this.options.duration?this.pos=b=r.easing[this.easing](a,this.options.duration*a,0,1,this.options.duration):this.pos=b=a,this.now=(this.end-this.start)*b+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),c&&c.set?c.set(this):_a.propHooks._default.set(this),this}},_a.prototype.init.prototype=_a.prototype,_a.propHooks={_default:{get:function(a){var b;return 1!==a.elem.nodeType||null!=a.elem[a.prop]&&null==a.elem.style[a.prop]?a.elem[a.prop]:(b=r.css(a.elem,a.prop,""),b&&"auto"!==b?b:0)},set:function(a){r.fx.step[a.prop]?r.fx.step[a.prop](a):1!==a.elem.nodeType||null==a.elem.style[r.cssProps[a.prop]]&&!r.cssHooks[a.prop]?a.elem[a.prop]=a.now:r.style(a.elem,a.prop,a.now+a.unit)}}},_a.propHooks.scrollTop=_a.propHooks.scrollLeft={set:function(a){a.elem.nodeType&&a.elem.parentNode&&(a.elem[a.prop]=a.now)}},r.easing={linear:function(a){return a},swing:function(a){return.5-Math.cos(a*Math.PI)/2},_default:"swing"},r.fx=_a.prototype.init,r.fx.step={};var ab,bb,cb=/^(?:toggle|show|hide)$/,db=/queueHooks$/;function eb(){bb&&(d.hidden===!1&&a.requestAnimationFrame?a.requestAnimationFrame(eb):a.setTimeout(eb,r.fx.interval),r.fx.tick())}function fb(){return a.setTimeout(function(){ab=void 0}),ab=r.now()}function gb(a,b){var c,d=0,e={height:a};for(b=b?1:0;d<4;d+=2-b)c=ca[d],e["margin"+c]=e["padding"+c]=a;return b&&(e.opacity=e.width=a),e}function hb(a,b,c){for(var d,e=(kb.tweeners[b]||[]).concat(kb.tweeners["*"]),f=0,g=e.length;f1)},removeAttr:function(a){return this.each(function(){r.removeAttr(this,a)})}}),r.extend({attr:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return"undefined"==typeof a.getAttribute?r.prop(a,b,c):(1===f&&r.isXMLDoc(a)||(e=r.attrHooks[b.toLowerCase()]||(r.expr.match.bool.test(b)?lb:void 0)),void 0!==c?null===c?void r.removeAttr(a,b):e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:(a.setAttribute(b,c+""),c):e&&"get"in e&&null!==(d=e.get(a,b))?d:(d=r.find.attr(a,b), 4 | null==d?void 0:d))},attrHooks:{type:{set:function(a,b){if(!o.radioValue&&"radio"===b&&B(a,"input")){var c=a.value;return a.setAttribute("type",b),c&&(a.value=c),b}}}},removeAttr:function(a,b){var c,d=0,e=b&&b.match(L);if(e&&1===a.nodeType)while(c=e[d++])a.removeAttribute(c)}}),lb={set:function(a,b,c){return b===!1?r.removeAttr(a,c):a.setAttribute(c,c),c}},r.each(r.expr.match.bool.source.match(/\w+/g),function(a,b){var c=mb[b]||r.find.attr;mb[b]=function(a,b,d){var e,f,g=b.toLowerCase();return d||(f=mb[g],mb[g]=e,e=null!=c(a,b,d)?g:null,mb[g]=f),e}});var nb=/^(?:input|select|textarea|button)$/i,ob=/^(?:a|area)$/i;r.fn.extend({prop:function(a,b){return T(this,r.prop,a,b,arguments.length>1)},removeProp:function(a){return this.each(function(){delete this[r.propFix[a]||a]})}}),r.extend({prop:function(a,b,c){var d,e,f=a.nodeType;if(3!==f&&8!==f&&2!==f)return 1===f&&r.isXMLDoc(a)||(b=r.propFix[b]||b,e=r.propHooks[b]),void 0!==c?e&&"set"in e&&void 0!==(d=e.set(a,c,b))?d:a[b]=c:e&&"get"in e&&null!==(d=e.get(a,b))?d:a[b]},propHooks:{tabIndex:{get:function(a){var b=r.find.attr(a,"tabindex");return b?parseInt(b,10):nb.test(a.nodeName)||ob.test(a.nodeName)&&a.href?0:-1}}},propFix:{"for":"htmlFor","class":"className"}}),o.optSelected||(r.propHooks.selected={get:function(a){var b=a.parentNode;return b&&b.parentNode&&b.parentNode.selectedIndex,null},set:function(a){var b=a.parentNode;b&&(b.selectedIndex,b.parentNode&&b.parentNode.selectedIndex)}}),r.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){r.propFix[this.toLowerCase()]=this});function pb(a){var b=a.match(L)||[];return b.join(" ")}function qb(a){return a.getAttribute&&a.getAttribute("class")||""}r.fn.extend({addClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).addClass(a.call(this,b,qb(this)))});if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])d.indexOf(" "+f+" ")<0&&(d+=f+" ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},removeClass:function(a){var b,c,d,e,f,g,h,i=0;if(r.isFunction(a))return this.each(function(b){r(this).removeClass(a.call(this,b,qb(this)))});if(!arguments.length)return this.attr("class","");if("string"==typeof a&&a){b=a.match(L)||[];while(c=this[i++])if(e=qb(c),d=1===c.nodeType&&" "+pb(e)+" "){g=0;while(f=b[g++])while(d.indexOf(" "+f+" ")>-1)d=d.replace(" "+f+" "," ");h=pb(d),e!==h&&c.setAttribute("class",h)}}return this},toggleClass:function(a,b){var c=typeof a;return"boolean"==typeof b&&"string"===c?b?this.addClass(a):this.removeClass(a):r.isFunction(a)?this.each(function(c){r(this).toggleClass(a.call(this,c,qb(this),b),b)}):this.each(function(){var b,d,e,f;if("string"===c){d=0,e=r(this),f=a.match(L)||[];while(b=f[d++])e.hasClass(b)?e.removeClass(b):e.addClass(b)}else void 0!==a&&"boolean"!==c||(b=qb(this),b&&W.set(this,"__className__",b),this.setAttribute&&this.setAttribute("class",b||a===!1?"":W.get(this,"__className__")||""))})},hasClass:function(a){var b,c,d=0;b=" "+a+" ";while(c=this[d++])if(1===c.nodeType&&(" "+pb(qb(c))+" ").indexOf(b)>-1)return!0;return!1}});var rb=/\r/g;r.fn.extend({val:function(a){var b,c,d,e=this[0];{if(arguments.length)return d=r.isFunction(a),this.each(function(c){var e;1===this.nodeType&&(e=d?a.call(this,c,r(this).val()):a,null==e?e="":"number"==typeof e?e+="":Array.isArray(e)&&(e=r.map(e,function(a){return null==a?"":a+""})),b=r.valHooks[this.type]||r.valHooks[this.nodeName.toLowerCase()],b&&"set"in b&&void 0!==b.set(this,e,"value")||(this.value=e))});if(e)return b=r.valHooks[e.type]||r.valHooks[e.nodeName.toLowerCase()],b&&"get"in b&&void 0!==(c=b.get(e,"value"))?c:(c=e.value,"string"==typeof c?c.replace(rb,""):null==c?"":c)}}}),r.extend({valHooks:{option:{get:function(a){var b=r.find.attr(a,"value");return null!=b?b:pb(r.text(a))}},select:{get:function(a){var b,c,d,e=a.options,f=a.selectedIndex,g="select-one"===a.type,h=g?null:[],i=g?f+1:e.length;for(d=f<0?i:g?f:0;d-1)&&(c=!0);return c||(a.selectedIndex=-1),f}}}}),r.each(["radio","checkbox"],function(){r.valHooks[this]={set:function(a,b){if(Array.isArray(b))return a.checked=r.inArray(r(a).val(),b)>-1}},o.checkOn||(r.valHooks[this].get=function(a){return null===a.getAttribute("value")?"on":a.value})});var sb=/^(?:focusinfocus|focusoutblur)$/;r.extend(r.event,{trigger:function(b,c,e,f){var g,h,i,j,k,m,n,o=[e||d],p=l.call(b,"type")?b.type:b,q=l.call(b,"namespace")?b.namespace.split("."):[];if(h=i=e=e||d,3!==e.nodeType&&8!==e.nodeType&&!sb.test(p+r.event.triggered)&&(p.indexOf(".")>-1&&(q=p.split("."),p=q.shift(),q.sort()),k=p.indexOf(":")<0&&"on"+p,b=b[r.expando]?b:new r.Event(p,"object"==typeof b&&b),b.isTrigger=f?2:3,b.namespace=q.join("."),b.rnamespace=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=e),c=null==c?[b]:r.makeArray(c,[b]),n=r.event.special[p]||{},f||!n.trigger||n.trigger.apply(e,c)!==!1)){if(!f&&!n.noBubble&&!r.isWindow(e)){for(j=n.delegateType||p,sb.test(j+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),i=h;i===(e.ownerDocument||d)&&o.push(i.defaultView||i.parentWindow||a)}g=0;while((h=o[g++])&&!b.isPropagationStopped())b.type=g>1?j:n.bindType||p,m=(W.get(h,"events")||{})[b.type]&&W.get(h,"handle"),m&&m.apply(h,c),m=k&&h[k],m&&m.apply&&U(h)&&(b.result=m.apply(h,c),b.result===!1&&b.preventDefault());return b.type=p,f||b.isDefaultPrevented()||n._default&&n._default.apply(o.pop(),c)!==!1||!U(e)||k&&r.isFunction(e[p])&&!r.isWindow(e)&&(i=e[k],i&&(e[k]=null),r.event.triggered=p,e[p](),r.event.triggered=void 0,i&&(e[k]=i)),b.result}},simulate:function(a,b,c){var d=r.extend(new r.Event,c,{type:a,isSimulated:!0});r.event.trigger(d,null,b)}}),r.fn.extend({trigger:function(a,b){return this.each(function(){r.event.trigger(a,b,this)})},triggerHandler:function(a,b){var c=this[0];if(c)return r.event.trigger(a,b,c,!0)}}),r.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(a,b){r.fn[b]=function(a,c){return arguments.length>0?this.on(b,null,a,c):this.trigger(b)}}),r.fn.extend({hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}}),o.focusin="onfocusin"in a,o.focusin||r.each({focus:"focusin",blur:"focusout"},function(a,b){var c=function(a){r.event.simulate(b,a.target,r.event.fix(a))};r.event.special[b]={setup:function(){var d=this.ownerDocument||this,e=W.access(d,b);e||d.addEventListener(a,c,!0),W.access(d,b,(e||0)+1)},teardown:function(){var d=this.ownerDocument||this,e=W.access(d,b)-1;e?W.access(d,b,e):(d.removeEventListener(a,c,!0),W.remove(d,b))}}});var tb=a.location,ub=r.now(),vb=/\?/;r.parseXML=function(b){var c;if(!b||"string"!=typeof b)return null;try{c=(new a.DOMParser).parseFromString(b,"text/xml")}catch(d){c=void 0}return c&&!c.getElementsByTagName("parsererror").length||r.error("Invalid XML: "+b),c};var wb=/\[\]$/,xb=/\r?\n/g,yb=/^(?:submit|button|image|reset|file)$/i,zb=/^(?:input|select|textarea|keygen)/i;function Ab(a,b,c,d){var e;if(Array.isArray(b))r.each(b,function(b,e){c||wb.test(a)?d(a,e):Ab(a+"["+("object"==typeof e&&null!=e?b:"")+"]",e,c,d)});else if(c||"object"!==r.type(b))d(a,b);else for(e in b)Ab(a+"["+e+"]",b[e],c,d)}r.param=function(a,b){var c,d=[],e=function(a,b){var c=r.isFunction(b)?b():b;d[d.length]=encodeURIComponent(a)+"="+encodeURIComponent(null==c?"":c)};if(Array.isArray(a)||a.jquery&&!r.isPlainObject(a))r.each(a,function(){e(this.name,this.value)});else for(c in a)Ab(c,a[c],b,e);return d.join("&")},r.fn.extend({serialize:function(){return r.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var a=r.prop(this,"elements");return a?r.makeArray(a):this}).filter(function(){var a=this.type;return this.name&&!r(this).is(":disabled")&&zb.test(this.nodeName)&&!yb.test(a)&&(this.checked||!ja.test(a))}).map(function(a,b){var c=r(this).val();return null==c?null:Array.isArray(c)?r.map(c,function(a){return{name:b.name,value:a.replace(xb,"\r\n")}}):{name:b.name,value:c.replace(xb,"\r\n")}}).get()}});var Bb=/%20/g,Cb=/#.*$/,Db=/([?&])_=[^&]*/,Eb=/^(.*?):[ \t]*([^\r\n]*)$/gm,Fb=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Gb=/^(?:GET|HEAD)$/,Hb=/^\/\//,Ib={},Jb={},Kb="*/".concat("*"),Lb=d.createElement("a");Lb.href=tb.href;function Mb(a){return function(b,c){"string"!=typeof b&&(c=b,b="*");var d,e=0,f=b.toLowerCase().match(L)||[];if(r.isFunction(c))while(d=f[e++])"+"===d[0]?(d=d.slice(1)||"*",(a[d]=a[d]||[]).unshift(c)):(a[d]=a[d]||[]).push(c)}}function Nb(a,b,c,d){var e={},f=a===Jb;function g(h){var i;return e[h]=!0,r.each(a[h]||[],function(a,h){var j=h(b,c,d);return"string"!=typeof j||f||e[j]?f?!(i=j):void 0:(b.dataTypes.unshift(j),g(j),!1)}),i}return g(b.dataTypes[0])||!e["*"]&&g("*")}function Ob(a,b){var c,d,e=r.ajaxSettings.flatOptions||{};for(c in b)void 0!==b[c]&&((e[c]?a:d||(d={}))[c]=b[c]);return d&&r.extend(!0,a,d),a}function Pb(a,b,c){var d,e,f,g,h=a.contents,i=a.dataTypes;while("*"===i[0])i.shift(),void 0===d&&(d=a.mimeType||b.getResponseHeader("Content-Type"));if(d)for(e in h)if(h[e]&&h[e].test(d)){i.unshift(e);break}if(i[0]in c)f=i[0];else{for(e in c){if(!i[0]||a.converters[e+" "+i[0]]){f=e;break}g||(g=e)}f=f||g}if(f)return f!==i[0]&&i.unshift(f),c[f]}function Qb(a,b,c,d){var e,f,g,h,i,j={},k=a.dataTypes.slice();if(k[1])for(g in a.converters)j[g.toLowerCase()]=a.converters[g];f=k.shift();while(f)if(a.responseFields[f]&&(c[a.responseFields[f]]=b),!i&&d&&a.dataFilter&&(b=a.dataFilter(b,a.dataType)),i=f,f=k.shift())if("*"===f)f=i;else if("*"!==i&&i!==f){if(g=j[i+" "+f]||j["* "+f],!g)for(e in j)if(h=e.split(" "),h[1]===f&&(g=j[i+" "+h[0]]||j["* "+h[0]])){g===!0?g=j[e]:j[e]!==!0&&(f=h[0],k.unshift(h[1]));break}if(g!==!0)if(g&&a["throws"])b=g(b);else try{b=g(b)}catch(l){return{state:"parsererror",error:g?l:"No conversion from "+i+" to "+f}}}return{state:"success",data:b}}r.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:tb.href,type:"GET",isLocal:Fb.test(tb.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Kb,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":r.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(a,b){return b?Ob(Ob(a,r.ajaxSettings),b):Ob(r.ajaxSettings,a)},ajaxPrefilter:Mb(Ib),ajaxTransport:Mb(Jb),ajax:function(b,c){"object"==typeof b&&(c=b,b=void 0),c=c||{};var e,f,g,h,i,j,k,l,m,n,o=r.ajaxSetup({},c),p=o.context||o,q=o.context&&(p.nodeType||p.jquery)?r(p):r.event,s=r.Deferred(),t=r.Callbacks("once memory"),u=o.statusCode||{},v={},w={},x="canceled",y={readyState:0,getResponseHeader:function(a){var b;if(k){if(!h){h={};while(b=Eb.exec(g))h[b[1].toLowerCase()]=b[2]}b=h[a.toLowerCase()]}return null==b?null:b},getAllResponseHeaders:function(){return k?g:null},setRequestHeader:function(a,b){return null==k&&(a=w[a.toLowerCase()]=w[a.toLowerCase()]||a,v[a]=b),this},overrideMimeType:function(a){return null==k&&(o.mimeType=a),this},statusCode:function(a){var b;if(a)if(k)y.always(a[y.status]);else for(b in a)u[b]=[u[b],a[b]];return this},abort:function(a){var b=a||x;return e&&e.abort(b),A(0,b),this}};if(s.promise(y),o.url=((b||o.url||tb.href)+"").replace(Hb,tb.protocol+"//"),o.type=c.method||c.type||o.method||o.type,o.dataTypes=(o.dataType||"*").toLowerCase().match(L)||[""],null==o.crossDomain){j=d.createElement("a");try{j.href=o.url,j.href=j.href,o.crossDomain=Lb.protocol+"//"+Lb.host!=j.protocol+"//"+j.host}catch(z){o.crossDomain=!0}}if(o.data&&o.processData&&"string"!=typeof o.data&&(o.data=r.param(o.data,o.traditional)),Nb(Ib,o,c,y),k)return y;l=r.event&&o.global,l&&0===r.active++&&r.event.trigger("ajaxStart"),o.type=o.type.toUpperCase(),o.hasContent=!Gb.test(o.type),f=o.url.replace(Cb,""),o.hasContent?o.data&&o.processData&&0===(o.contentType||"").indexOf("application/x-www-form-urlencoded")&&(o.data=o.data.replace(Bb,"+")):(n=o.url.slice(f.length),o.data&&(f+=(vb.test(f)?"&":"?")+o.data,delete o.data),o.cache===!1&&(f=f.replace(Db,"$1"),n=(vb.test(f)?"&":"?")+"_="+ub++ +n),o.url=f+n),o.ifModified&&(r.lastModified[f]&&y.setRequestHeader("If-Modified-Since",r.lastModified[f]),r.etag[f]&&y.setRequestHeader("If-None-Match",r.etag[f])),(o.data&&o.hasContent&&o.contentType!==!1||c.contentType)&&y.setRequestHeader("Content-Type",o.contentType),y.setRequestHeader("Accept",o.dataTypes[0]&&o.accepts[o.dataTypes[0]]?o.accepts[o.dataTypes[0]]+("*"!==o.dataTypes[0]?", "+Kb+"; q=0.01":""):o.accepts["*"]);for(m in o.headers)y.setRequestHeader(m,o.headers[m]);if(o.beforeSend&&(o.beforeSend.call(p,y,o)===!1||k))return y.abort();if(x="abort",t.add(o.complete),y.done(o.success),y.fail(o.error),e=Nb(Jb,o,c,y)){if(y.readyState=1,l&&q.trigger("ajaxSend",[y,o]),k)return y;o.async&&o.timeout>0&&(i=a.setTimeout(function(){y.abort("timeout")},o.timeout));try{k=!1,e.send(v,A)}catch(z){if(k)throw z;A(-1,z)}}else A(-1,"No Transport");function A(b,c,d,h){var j,m,n,v,w,x=c;k||(k=!0,i&&a.clearTimeout(i),e=void 0,g=h||"",y.readyState=b>0?4:0,j=b>=200&&b<300||304===b,d&&(v=Pb(o,y,d)),v=Qb(o,v,y,j),j?(o.ifModified&&(w=y.getResponseHeader("Last-Modified"),w&&(r.lastModified[f]=w),w=y.getResponseHeader("etag"),w&&(r.etag[f]=w)),204===b||"HEAD"===o.type?x="nocontent":304===b?x="notmodified":(x=v.state,m=v.data,n=v.error,j=!n)):(n=x,!b&&x||(x="error",b<0&&(b=0))),y.status=b,y.statusText=(c||x)+"",j?s.resolveWith(p,[m,x,y]):s.rejectWith(p,[y,x,n]),y.statusCode(u),u=void 0,l&&q.trigger(j?"ajaxSuccess":"ajaxError",[y,o,j?m:n]),t.fireWith(p,[y,x]),l&&(q.trigger("ajaxComplete",[y,o]),--r.active||r.event.trigger("ajaxStop")))}return y},getJSON:function(a,b,c){return r.get(a,b,c,"json")},getScript:function(a,b){return r.get(a,void 0,b,"script")}}),r.each(["get","post"],function(a,b){r[b]=function(a,c,d,e){return r.isFunction(c)&&(e=e||d,d=c,c=void 0),r.ajax(r.extend({url:a,type:b,dataType:e,data:c,success:d},r.isPlainObject(a)&&a))}}),r._evalUrl=function(a){return r.ajax({url:a,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,"throws":!0})},r.fn.extend({wrapAll:function(a){var b;return this[0]&&(r.isFunction(a)&&(a=a.call(this[0])),b=r(a,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstElementChild)a=a.firstElementChild;return a}).append(this)),this},wrapInner:function(a){return r.isFunction(a)?this.each(function(b){r(this).wrapInner(a.call(this,b))}):this.each(function(){var b=r(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=r.isFunction(a);return this.each(function(c){r(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(a){return this.parent(a).not("body").each(function(){r(this).replaceWith(this.childNodes)}),this}}),r.expr.pseudos.hidden=function(a){return!r.expr.pseudos.visible(a)},r.expr.pseudos.visible=function(a){return!!(a.offsetWidth||a.offsetHeight||a.getClientRects().length)},r.ajaxSettings.xhr=function(){try{return new a.XMLHttpRequest}catch(b){}};var Rb={0:200,1223:204},Sb=r.ajaxSettings.xhr();o.cors=!!Sb&&"withCredentials"in Sb,o.ajax=Sb=!!Sb,r.ajaxTransport(function(b){var c,d;if(o.cors||Sb&&!b.crossDomain)return{send:function(e,f){var g,h=b.xhr();if(h.open(b.type,b.url,b.async,b.username,b.password),b.xhrFields)for(g in b.xhrFields)h[g]=b.xhrFields[g];b.mimeType&&h.overrideMimeType&&h.overrideMimeType(b.mimeType),b.crossDomain||e["X-Requested-With"]||(e["X-Requested-With"]="XMLHttpRequest");for(g in e)h.setRequestHeader(g,e[g]);c=function(a){return function(){c&&(c=d=h.onload=h.onerror=h.onabort=h.onreadystatechange=null,"abort"===a?h.abort():"error"===a?"number"!=typeof h.status?f(0,"error"):f(h.status,h.statusText):f(Rb[h.status]||h.status,h.statusText,"text"!==(h.responseType||"text")||"string"!=typeof h.responseText?{binary:h.response}:{text:h.responseText},h.getAllResponseHeaders()))}},h.onload=c(),d=h.onerror=c("error"),void 0!==h.onabort?h.onabort=d:h.onreadystatechange=function(){4===h.readyState&&a.setTimeout(function(){c&&d()})},c=c("abort");try{h.send(b.hasContent&&b.data||null)}catch(i){if(c)throw i}},abort:function(){c&&c()}}}),r.ajaxPrefilter(function(a){a.crossDomain&&(a.contents.script=!1)}),r.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(a){return r.globalEval(a),a}}}),r.ajaxPrefilter("script",function(a){void 0===a.cache&&(a.cache=!1),a.crossDomain&&(a.type="GET")}),r.ajaxTransport("script",function(a){if(a.crossDomain){var b,c;return{send:function(e,f){b=r(" 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 755 | 756 | 757 | 758 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | class GlslViewport { 4 | constructor(canvas, opts) { 5 | opts = opts || {}; 6 | this.canvas = canvas || document.createElement('canvas'); // canvas element 7 | this.canvas.width = opts.width || 600; // canvas width 8 | this.canvas.height = opts.height || 600; // canvas height 9 | this.tile_rendering = opts.tile_rendering || false; 10 | 11 | this.defines = [ 12 | "#define USE_CUBEMAP", 13 | "//#define USE_PROCEDURAL_SKY", 14 | "#define USE_BIASED_SAMPLING", 15 | "//#define USE_BIDIRECTIONAL" 16 | ]; 17 | 18 | this.constants = [ 19 | "const lowp int MAX_BOUNCES = 12;", 20 | "const lowp int MAX_DIFF_BOUNCES = 4;", 21 | "const lowp int MAX_SPEC_BOUNCES = 4;", 22 | "const lowp int MAX_TRANS_BOUNCES = 12;", 23 | "const lowp int MAX_SCATTERING_EVENTS = 12;", 24 | "const mediump int MARCHING_STEPS = 128;", 25 | "const lowp float FUDGE_FACTOR = 1.0;", 26 | "const bool sample_lights = true;", 27 | "const lowp int LIGHT_PATH_LENGTH = 2;" 28 | ]; 29 | 30 | this.scene = `//--------------------- EUCLIDEAN/QUADRIC PARAMS -------------------------------- 31 | 32 | const bool U_EUCLIDEAN = false; // using Euclidean meshes? 33 | const bool U_SPHERE = true; // using Euclidean sphere? 34 | const bool U_PLANE = true; // using Euclidean plane? 35 | const bool U_BOX = false; // using Non-Euclidean box? 36 | const bool U_SDF = true; // using SDF meshes? 37 | 38 | const lowp int NUM_MESHES = 0; 39 | const lowp int NUM_SDFS = 2; 40 | const lowp int NUM_MODELS = 0; 41 | 42 | const Mesh meshes[NUM_MESHES + NUM_SDFS + NUM_MODELS] = Mesh[]( 43 | Mesh(MAT_METAL, SDF, vec3(0.0, -0.49, 0.0), vec4(1.0)), 44 | Mesh(MAT_WHITE, SDF, vec3(0.0, -1.6, -0.2), vec4(1.5,0.1,1.5,0.0)) 45 | ); 46 | 47 | // light index 48 | const lowp int light_index[1] = int[](-1);`; 49 | 50 | this.sdf_meshes = [ 51 | "sdf_meshes[0] = vec2(sdBox(p-meshes[NUM_MESHES + 0].pos, meshes[NUM_MESHES + 0].joker.xyz), 0.0);", 52 | "sdf_meshes[1] = vec2(sdBox(p-meshes[NUM_MESHES + 1].pos, meshes[NUM_MESHES + 1].joker.xyz), 1.0);" 53 | ]; 54 | 55 | this.camera = { 56 | "origin": new Vector3(0.0, 0.0, 3.0), 57 | "lookat": new Vector3(0.0, 0.0, -1.0), 58 | "fov": 90.0, 59 | "aperture": 0.01, 60 | "focalLength": 10.0 61 | }; 62 | 63 | // tile opts 64 | this.tile = [0, 0]; 65 | this.tile_size = [32, 32]; 66 | this.total_tiles = [ 67 | Math.ceil(this.canvas.width / this.tile_size[0]) - 1, 68 | Math.ceil(this.canvas.height / this.tile_size[1]) - 1 69 | ]; 70 | 71 | console.log("%cInitialiazing GL...", 'color: #00b1ff'); 72 | let gl = this.create3DContext(opts); 73 | 74 | if (!gl) { 75 | return; 76 | } 77 | 78 | gl.getExtension('WEBGL_lose_context'); 79 | gl.getExtension('EXT_color_buffer_float'); 80 | gl.getExtension('OES_texture_float_linear'); 81 | // this.debug_shader = gl.getExtension('WEBGL_debug_shaders'); 82 | 83 | this.gl = gl; // GL Instance 84 | this.loadTime = performance.now(); // load time 85 | 86 | this.shaders = {}; // compiled shaders 87 | this.images = {}; // loadded iamges 88 | this.textures = { 89 | length: 0 90 | }; // loaded gl textures 91 | 92 | this.frontTarget = { 93 | framebuffer: this.createFramebuffer({ 94 | name: "front_target_tex", 95 | width: this.canvas.width, 96 | height: this.canvas.height, 97 | color: [gl.RGBA32F, gl.RGBA], 98 | type: gl.FLOAT 99 | }), 100 | uniforms: { 101 | "backbuffer": 0, 102 | "tex0": 1, 103 | "tex1": 2, 104 | "tex2": 3, 105 | "tex3": 4, 106 | "rng_tex": 5, 107 | "cubemap": 6 108 | } 109 | }; // front render target 110 | 111 | this.createTexture({ 112 | name: "back_target_tex", 113 | width: this.canvas.width, 114 | height: this.canvas.height, 115 | color: [gl.RGBA32F, gl.RGBA], 116 | type: gl.FLOAT 117 | }); 118 | 119 | this.display = {}; // display program 120 | 121 | this.paused = opts.paused || false; // is render loop paused 122 | this.passes = 0; // current passes 123 | this.max_passes = opts.max_passes || Infinity; // max passes 124 | 125 | let vertexbuffer = gl.createBuffer(); // vertex buffer 126 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexbuffer); 127 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([-1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0]), gl.STATIC_DRAW); 128 | gl.enableVertexAttribArray(0); 129 | gl.vertexAttribPointer(0, 2, gl.FLOAT, false, 0, 0); 130 | 131 | // compile & load shaders 132 | for (let i = 0; i < opts.shaders.length; i++) { 133 | opts.shaders[i].string = parseShader(opts.shaders[i].filepath, this); 134 | this.createShader(opts.shaders[i]); 135 | } 136 | 137 | //---------------------------- FRONT TARGET PROGRAM 138 | 139 | this.frontTarget.program = this.createProgram([this.shaders["vertex_shader"], this.shaders["raytracing_shader"]]); 140 | this.updateFrontTarget(); 141 | 142 | // this.genRandomTexture(); 143 | // gl.activeTexture(gl.TEXTURE0); 144 | 145 | let sandbox = this; 146 | 147 | // RGBA noise image 148 | (function () { 149 | let image = new Image(); 150 | 151 | image.onload = function () { 152 | createImageBitmap(image).then(function (bitmap) { 153 | sandbox.images["rnd_img"] = bitmap; 154 | 155 | gl.activeTexture(gl.TEXTURE0 + sandbox.frontTarget["uniforms"]["rng_tex"]); 156 | sandbox.loadTexture({ 157 | name: "rnd_tex" 158 | }, bitmap); 159 | }); 160 | }; 161 | 162 | image.src = "textures/rgba_noise/rgba_noise256.png"; 163 | })(); 164 | 165 | // Images Loader 166 | let load_images = new Promise(function (resolve, reject) { 167 | console.log("Loading Images..."); 168 | for (let i = 0; i < opts.textures.length; i++) { 169 | let image = new Image(); 170 | 171 | image.onload = function () { 172 | createImageBitmap(image).then(function (bitmap) { 173 | sandbox.images["img" + i] = bitmap; 174 | 175 | gl.activeTexture(gl.TEXTURE0 + sandbox.frontTarget["uniforms"]["tex" + i]); 176 | sandbox.loadTexture({ 177 | name: "tex" + i 178 | }, bitmap); 179 | 180 | if (i === opts.textures.length - 1) { 181 | resolve(); 182 | } 183 | }); 184 | }; 185 | 186 | image.src = opts.textures[i]; 187 | } 188 | }); 189 | 190 | 191 | 192 | let load_cubemap = new Promise(function (resolve, reject) { 193 | const targets = [gl.TEXTURE_CUBE_MAP_NEGATIVE_X, gl.TEXTURE_CUBE_MAP_NEGATIVE_Y, gl.TEXTURE_CUBE_MAP_NEGATIVE_Z, 194 | gl.TEXTURE_CUBE_MAP_POSITIVE_X, gl.TEXTURE_CUBE_MAP_POSITIVE_Y, gl.TEXTURE_CUBE_MAP_POSITIVE_Z]; 195 | 196 | let texture = gl.createTexture(); 197 | 198 | console.log("Loading Cubemap Images..."); 199 | for (let i = 0; i < opts.cubemap.length; i++) { 200 | let image = new Image(); 201 | 202 | image.onload = function () { 203 | createImageBitmap(image).then(function (bitmap) { 204 | sandbox.images["cubemap_img" + i] = bitmap; 205 | 206 | gl.activeTexture(gl.TEXTURE0 + sandbox.frontTarget["uniforms"]["cubemap"]); 207 | gl.bindTexture(gl.TEXTURE_CUBE_MAP, texture); 208 | 209 | gl.texImage2D(targets[i], 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, bitmap); 210 | 211 | if (i === opts.cubemap.length - 1) { 212 | gl.texParameteri(gl.TEXTURE_CUBE_MAP, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 213 | sandbox.pushNewTexture("cubemap", texture); 214 | resolve(); 215 | } 216 | }); 217 | }; 218 | 219 | image.src = opts.cubemap[i]; 220 | } 221 | }); 222 | 223 | load_images.then(function () { 224 | console.log("%cLoaded Images...", 'color: #27ff00'); 225 | }).then(load_cubemap).then(function () { 226 | console.log("%cLoaded Cubemap...", 'color: #27ff00'); 227 | }).then(function () { 228 | console.log("%cLoaded All Textures...", 'color: #27ff00'); 229 | document.dispatchEvent(new Event('textures_loaded')); 230 | }); 231 | 232 | //---------------------------- DISPLAY PROGRAM 233 | 234 | this.display.program = this.createProgram([this.shaders["vertex_shader"], this.shaders["display_shader"]]); 235 | gl.useProgram(this.display.program); 236 | 237 | this.display.bufferA_ID = gl.getUniformLocation(this.display.program, "u_bufferA"); // bufferA texture2D 238 | this.display.contributionID = gl.getUniformLocation(this.display.program, "u_cont"); // current frame 239 | 240 | gl.uniform1i(this.display.bufferA_ID, this.frontTarget["uniforms"]["backbuffer"]); 241 | 242 | if (this.tile_rendering) gl.viewport(0, 0, this.tile_size[0], this.tile_size[1]); 243 | 244 | //------------ EVENTS ------------ 245 | let mouse = { 246 | x: 0, 247 | y: 0 248 | }; // mouse position 249 | 250 | //------------------------------- KEYBOARD -------------------------------- 251 | // this.keyboardTexVal = new Uint8Array(256); 252 | // this.keyboardTex = this.createTexture({ 253 | // name: "keyboard_tex", 254 | // width: 256, 255 | // height: 1, 256 | // color: [gl.R8, gl.RED], 257 | // ch: 1, 258 | // pixels: this.keyboardTexVal 259 | // }); 260 | // let keyboardTexPos = this.textures["keyboard_tex"]["pos"]; 261 | // gl.uniform1i(this.frontTarget.keyboardID, keyboardTexPos); 262 | 263 | // document.addEventListener("keyup", function (evt) { 264 | // sandbox.keyboardTexVal[evt.keyCode] = 0; 265 | // 266 | // gl.activeTexture(gl.TEXTURE0 + keyboardTexPos); 267 | // gl.bindTexture(gl.TEXTURE_2D, sandbox.textures["keyboard_tex"]["tex"]); 268 | // gl.texImage2D(gl.TEXTURE_2D, 0, gl.R8, 256, 1, 0, gl.RED, gl.UNSIGNED_BYTE, sandbox.keyboardTexVal); 269 | // 270 | // }); 271 | 272 | //------------------------- SDF SAMPLED ON A GRID ------------------------- 273 | 274 | // let sdf0 = parseSDF("models/armadilo.sdf"); 275 | // console.log(sdf0); 276 | // this.sdfTex = this.createTexture({ 277 | // name: "sdf0", 278 | // width: sdf0.dimensions[0], 279 | // height: sdf0.dimensions[1], 280 | // depth: sdf0.dimensions[2], 281 | // color: [gl.R32F, gl.RED], 282 | // type: gl.FLOAT, 283 | // ch: 1, 284 | // pixels: sdf0.values 285 | // }); 286 | // gl.uniform1i(this.frontTarget.sdf0_texID, this.textures["sdf0"]["pos"]); 287 | // 288 | // // create sdf0 AABB Buffer 289 | // let sdf0_aabb = new Float32Array([ 290 | // sdf0.bb_min[0], sdf0.bb_min[1], sdf0.bb_min[2], 0.0, 291 | // sdf0.bb_max[0], sdf0.bb_max[1], sdf0.bb_max[2], 0.0, 292 | // ]); 293 | // 294 | // let sdf0_aabbBuffer = gl.createBuffer(); 295 | // gl.bindBuffer(gl.UNIFORM_BUFFER, sdf0_aabbBuffer); 296 | // gl.bufferData(gl.UNIFORM_BUFFER, sdf0_aabb, gl.STATIC_DRAW); 297 | // gl.bufferSubData(gl.UNIFORM_BUFFER, 0, sdf0_aabb); 298 | // gl.bindBuffer(gl.UNIFORM_BUFFER, null); 299 | // 300 | // gl.uniformBlockBinding(this.frontTarget.program, this.frontTarget.sdf0_aabbID, 0); 301 | // gl.bindBufferBase(gl.UNIFORM_BUFFER, 0, sdf0_aabbBuffer); 302 | 303 | //--------------------------------- MOUSE --------------------------------- 304 | 305 | // mouse events 306 | // this.canvas.onmouseup = function () { 307 | // gl.uniform1i(sandbox.frontTarget.mouse_downID, 0); 308 | // }; 309 | // this.canvas.onmousedown = function () { 310 | // gl.uniform1i(sandbox.frontTarget.mouse_downID, 1); 311 | // }; 312 | // document.addEventListener('mousemove', (e) => { 313 | // mouse.x = e.clientX || e.pageX; 314 | // mouse.y = e.clientY || e.pageY; 315 | // }, false); 316 | 317 | // on GL context lost 318 | this.canvas.addEventListener("webglcontextlost", function (e) { 319 | alert("Your gpu cant bear this buddy ;("); 320 | EXT.restoreContext(); 321 | }, false); 322 | 323 | // this.setMouse(mouse); 324 | 325 | return this; 326 | } 327 | 328 | updateFrontTarget() { 329 | let gl = this.gl; 330 | 331 | gl.useProgram(this.frontTarget.program); 332 | 333 | this.frontTarget.timeID = gl.getUniformLocation(this.frontTarget.program, "u_time"); // time elapsed 334 | this.frontTarget.frameID = gl.getUniformLocation(this.frontTarget.program, "u_frame"); 335 | this.frontTarget.resolutionID = gl.getUniformLocation(this.frontTarget.program, "u_resolution"); // canvas resolution 336 | 337 | this.frontTarget.camPosID = gl.getUniformLocation(this.frontTarget.program, "u_camPos"); // position 338 | this.frontTarget.camLookAtID = gl.getUniformLocation(this.frontTarget.program, "u_camLookAt"); // facing position 339 | this.frontTarget.camParamsID = gl.getUniformLocation(this.frontTarget.program, "u_camParams"); // parameters 340 | 341 | // this.frontTarget.mouseID = gl.getUniformLocation(this.frontTarget.program, "u_mouse"); // mouse position 342 | // this.frontTarget.mouse_downID = gl.getUniformLocation(this.frontTarget.program, "u_mouse_down"); // is LMB down 343 | // this.frontTarget.keyboardID = gl.getUniformLocation(this.frontTarget.program, "u_keyboard"); // is LMB down 344 | 345 | // this.frontTarget.sdf0_texID = gl.getUniformLocation(this.frontTarget.program, "u_sdf0"); // canvas resolution 346 | // this.frontTarget.sdf0_aabbID = gl.getUniformBlockIndex(this.frontTarget.program, "u_sdf0_aabb"); // sdf0 AABB 347 | 348 | this.frontTarget.bufferA_ID = gl.getUniformLocation(this.frontTarget.program, "u_bufferA"); // bufferA texture2D 349 | 350 | this.frontTarget.rnd_tex_ID = gl.getUniformLocation(this.frontTarget.program, "u_rnd_tex"); // random texture 351 | 352 | this.frontTarget.tex0_ID = gl.getUniformLocation(this.frontTarget.program, "u_tex0"); // tex0 353 | this.frontTarget.tex1_ID = gl.getUniformLocation(this.frontTarget.program, "u_tex1"); // tex1 354 | this.frontTarget.tex2_ID = gl.getUniformLocation(this.frontTarget.program, "u_tex2"); // tex2 355 | this.frontTarget.tex3_ID = gl.getUniformLocation(this.frontTarget.program, "u_tex3"); // tex3 356 | 357 | this.frontTarget.cubemap_ID = gl.getUniformLocation(this.frontTarget.program, "u_cubemap"); // cubemap 358 | 359 | gl.uniform2f(this.frontTarget.resolutionID, this.canvas.width, this.canvas.height); 360 | 361 | gl.uniform3f(this.frontTarget.camPosID, this.camera.origin.x, this.camera.origin.y, this.camera.origin.z); 362 | gl.uniform3f(this.frontTarget.camLookAtID, this.camera.lookat.x, this.camera.lookat.y, this.camera.lookat.z); 363 | gl.uniform3f(this.frontTarget.camParamsID, this.camera.fov, this.camera.aperture, this.camera.focalLength); 364 | 365 | gl.uniform1i(this.frontTarget.bufferA_ID, this.frontTarget["uniforms"]["backbuffer"]); 366 | gl.uniform1i(this.frontTarget.rnd_tex_ID, this.frontTarget["uniforms"]["rng_tex"]); 367 | gl.uniform1i(this.frontTarget.tex0_ID, this.frontTarget["uniforms"]["tex0"]); 368 | gl.uniform1i(this.frontTarget.tex1_ID, this.frontTarget["uniforms"]["tex1"]); 369 | gl.uniform1i(this.frontTarget.tex2_ID, this.frontTarget["uniforms"]["tex2"]); 370 | gl.uniform1i(this.frontTarget.tex3_ID, this.frontTarget["uniforms"]["tex3"]); 371 | gl.uniform1i(this.frontTarget.cubemap_ID, this.frontTarget["uniforms"]["cubemap"]); 372 | } 373 | 374 | genRandomTexture() { 375 | let gl = this.gl; 376 | let rngData = new Float32Array(this.canvas.width * this.canvas.height * 4); 377 | 378 | for (let i = 0; i < this.canvas.width * this.canvas.height; i++) { 379 | rngData[i * 4 + 0] = Math.random() * 4194167.0; 380 | rngData[i * 4 + 1] = Math.random() * 4194167.0; 381 | rngData[i * 4 + 2] = Math.random() * 4194167.0; 382 | rngData[i * 4 + 3] = Math.random() * 4194167.0; 383 | } 384 | 385 | gl.activeTexture(gl.TEXTURE0 + this.frontTarget["uniforms"]["rngData"]); 386 | this.loadTexture({ 387 | name: "rngData", 388 | width: this.canvas.width, 389 | height: this.canvas.height, 390 | color: [gl.RGBA32F, gl.RGBA], 391 | type: gl.FLOAT 392 | }, rngData); 393 | } 394 | 395 | create3DContext(optAttribs) { 396 | let context = undefined; 397 | try { 398 | context = this.canvas.getContext('webgl2', optAttribs); 399 | } catch (e) { } 400 | return context; 401 | } 402 | 403 | resize(value) { 404 | let gl = this.gl; 405 | 406 | switch (value) { 407 | case 0: 408 | this.canvas.width = this.canvas.height = 256 * window.devicePixelRatio; 409 | break; 410 | case 1: 411 | this.canvas.width = this.canvas.height = 512 * window.devicePixelRatio; 412 | break; 413 | case 2: 414 | this.canvas.width = this.canvas.height = 1024 * window.devicePixelRatio; 415 | break; 416 | case 3: 417 | this.canvas.width = this.canvas.height = 2048 * window.devicePixelRatio; 418 | break; 419 | case 4: 420 | this.canvas.width = this.canvas.height = 4096 * window.devicePixelRatio; 421 | break; 422 | case 5: 423 | this.canvas.width = this.canvas.height = 8192 * window.devicePixelRatio; 424 | break; 425 | default: 426 | break; 427 | } 428 | 429 | this.total_tiles = [ 430 | Math.ceil(this.canvas.width / this.tile_size[0]) - 1, 431 | Math.ceil(this.canvas.height / this.tile_size[1]) - 1 432 | ]; 433 | 434 | this.createTexture({ 435 | name: "front_target_tex", 436 | width: this.canvas.width, 437 | height: this.canvas.height, 438 | color: [gl.RGBA32F, gl.RGBA], 439 | type: gl.FLOAT 440 | }); 441 | 442 | this.createTexture({ 443 | name: "back_target_tex", 444 | width: this.canvas.width, 445 | height: this.canvas.height, 446 | color: [gl.RGBA32F, gl.RGBA], 447 | type: gl.FLOAT 448 | }); 449 | gl.bindTexture(gl.TEXTURE_2D, null); 450 | 451 | gl.clear(gl.COLOR_BUFFER_BIT); 452 | 453 | gl.useProgram(this.frontTarget.program); 454 | gl.uniform2f(this.frontTarget.resolutionID, this.canvas.width, this.canvas.height); 455 | 456 | gl.useProgram(this.display.program); 457 | gl.uniform2f(this.display.resolutionID, this.canvas.width, this.canvas.height); 458 | 459 | if (!this.tile_rendering) gl.viewport(0, 0, this.canvas.width, this.canvas.height); 460 | } 461 | 462 | createShader(opts) { 463 | let gl = this.gl; 464 | 465 | let shader = gl.createShader(opts.type == "vert" ? gl.VERTEX_SHADER : gl.FRAGMENT_SHADER); 466 | 467 | gl.shaderSource(shader, opts.string); 468 | gl.compileShader(shader); 469 | 470 | if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) throw ("could not compile shader: \n" + gl.getShaderInfoLog(shader)); 471 | 472 | this.shaders[opts.name] = shader; 473 | return shader; 474 | } 475 | 476 | createProgram(shaders) { 477 | let gl = this.gl; 478 | 479 | let program = gl.createProgram(); 480 | for (let i = 0; i < shaders.length; i++) 481 | gl.attachShader(program, shaders[i]); 482 | 483 | gl.linkProgram(program); 484 | 485 | if (!gl.getProgramParameter(program, gl.LINK_STATUS)) 486 | throw "Unable to initialize the shader program: \n" + gl.getProgramInfoLog(program); 487 | 488 | return program; 489 | } 490 | 491 | pushNewTexture(name, texture) { 492 | if (!this.textures[name]) { 493 | this.textures.length++; 494 | } 495 | this.textures[name] = texture; 496 | } 497 | 498 | createTexture(opts) { 499 | let gl = this.gl; 500 | opts = opts || {}; 501 | 502 | if (!(opts.width && opts.height)) throw "Can't create a texture without giving a size !"; 503 | 504 | let is3D = opts.depth != undefined; 505 | opts.depth = opts.depth || 1; 506 | 507 | // color components 508 | opts.color = opts.color || []; 509 | let color = [opts.color[0] || gl.RGBA, opts.color[1] || gl.RGBA]; 510 | 511 | // data type 512 | let type = opts.type || gl.UNSIGNED_BYTE; 513 | 514 | // color channels 515 | let ch = opts.ch || 4; 516 | 517 | let pixels = opts.pixels; 518 | if (pixels == undefined) pixels = type == gl.UNSIGNED_BYTE ? new Uint8Array(opts.width * opts.height * opts.depth * ch) : 519 | new Float32Array(opts.width * opts.height * opts.depth * ch); 520 | 521 | let texture = gl.createTexture(); 522 | if (!is3D) { 523 | gl.bindTexture(gl.TEXTURE_2D, texture); 524 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 525 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 526 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 527 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 528 | 529 | gl.texImage2D( 530 | gl.TEXTURE_2D, // target 531 | 0, // level 532 | color[0], // internal format 533 | opts.width, // width 534 | opts.height, // height 535 | 0, // border 536 | color[1], // format 537 | type, // type 538 | pixels // pixels 539 | ); 540 | 541 | if (opts.genMIP) gl.generateMipmap(gl.TEXTURE_2D); 542 | } else { 543 | gl.bindTexture(gl.TEXTURE_3D, texture); 544 | 545 | gl.texImage3D( 546 | gl.TEXTURE_3D, // target 547 | 0, // level 548 | color[0], // internal format 549 | opts.width, // width 550 | opts.height, // height 551 | opts.depth, // depth 552 | 0, // border 553 | color[1], // format 554 | type, // type 555 | pixels // pixels 556 | ); 557 | } 558 | 559 | this.pushNewTexture(opts.name || ("tex" + this.textures.length), texture) 560 | return texture; 561 | } 562 | 563 | loadTexture(opts, img) { 564 | let gl = this.gl; 565 | opts = opts || {}; 566 | 567 | let hs = opts.width && opts.height; 568 | 569 | // color components 570 | opts.color = opts.color || []; 571 | let color = [opts.color[0] || gl.RGBA, opts.color[1] || gl.RGBA]; 572 | 573 | // data type 574 | let type = opts.type || gl.UNSIGNED_BYTE; 575 | 576 | let texture = gl.createTexture(); 577 | gl.bindTexture(gl.TEXTURE_2D, texture); 578 | 579 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.REPEAT); 580 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.REPEAT); 581 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 582 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 583 | 584 | if (hs) { 585 | gl.texImage2D(gl.TEXTURE_2D, 0, color[0], opts.width, opts.height, 0, color[1], type, img); 586 | } else { 587 | gl.texImage2D(gl.TEXTURE_2D, 0, color[0], color[1], type, img); 588 | gl.generateMipmap(gl.TEXTURE_2D); 589 | } 590 | 591 | this.pushNewTexture(opts.name || ("tex" + this.textures.length), texture); 592 | } 593 | 594 | createFramebuffer(opts) { 595 | let gl = this.gl; 596 | 597 | let framebuffer = gl.createFramebuffer(); 598 | gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer); 599 | 600 | if (Array.isArray(opts)) { 601 | let drawBuffers = []; 602 | 603 | for (let i = 0; i < opts.length; i++) { 604 | drawBuffers.push(gl.COLOR_ATTACHMENT0 + i); 605 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0 + i, gl.TEXTURE_2D, this.createTexture(opts[i]), 0); 606 | } 607 | 608 | gl.drawBuffers(drawBuffers); 609 | } else { 610 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.createTexture(opts), 0); 611 | } 612 | 613 | let status = gl.checkFramebufferStatus(gl.DRAW_FRAMEBUFFER); 614 | if (status != gl.FRAMEBUFFER_COMPLETE) { 615 | console.error('fb status: ' + status.toString(16)); 616 | return; 617 | } 618 | 619 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 620 | 621 | return framebuffer; 622 | } 623 | 624 | setScene() { 625 | let gl = this.gl; 626 | 627 | let models = []; 628 | 629 | models[0] = models[1] = models[2] = newModel(JSON.parse(fetchHTTP('/models/null.json'))); 630 | 631 | models[0] = newModel(JSON.parse(fetchHTTP('/models/gem.json')), { 632 | color: [1., 1., 1.], 633 | position: [0., -.5, 0.], 634 | backface_culling: 1, 635 | type: 4 636 | }); 637 | 638 | // let mesh = parseMesh(JSON.parse(fetchHTTP('/models/gem2.json'))); 639 | // let bvh = new BVH(); 640 | // bvh.Build(mesh); 641 | // console.log(bvh); 642 | 643 | let sandbox = this; 644 | models.forEach(function (model, i) { 645 | //------------------------------------------------------------------------ 646 | //> model Texture 647 | //------------------------------------------------------------------------ 648 | let modelTexture = gl.createTexture(); 649 | gl.activeTexture(gl.TEXTURE0 + sandbox.textures.length); 650 | gl.bindTexture(gl.TEXTURE_2D_ARRAY, modelTexture); 651 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 652 | gl.texImage3D( 653 | gl.TEXTURE_2D_ARRAY, 654 | 0, 655 | gl.RGB32F, 656 | model.buffer_s, 657 | model.buffer_s, 658 | 4, 659 | 0, 660 | gl.RGB, 661 | gl.FLOAT, 662 | model.buffer); 663 | gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 664 | gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 665 | gl.texParameteri(gl.TEXTURE_2D_ARRAY, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 666 | gl.uniform1i(gl.getUniformLocation(sandbox.frontTarget.program, "u_model" + i), sandbox.textures.length); 667 | 668 | sandbox.pushNewTexture("model" + i, modelTexture); 669 | 670 | //------------------------------------------------------------------------ 671 | //> indices 672 | //------------------------------------------------------------------------ 673 | let indicesTexture = gl.createTexture(); 674 | gl.activeTexture(gl.TEXTURE0 + sandbox.textures.length); 675 | gl.bindTexture(gl.TEXTURE_2D, indicesTexture); 676 | gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, false); 677 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB32UI, model.indices_s, model.indices_s, 0, gl.RGB_INTEGER, gl.UNSIGNED_INT, model.indices); 678 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); 679 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); 680 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 681 | gl.uniform1i(gl.getUniformLocation(sandbox.frontTarget.program, "u_indices" + i), sandbox.textures.length); 682 | 683 | sandbox.pushNewTexture("indices" + i, indicesTexture); 684 | }); 685 | 686 | } 687 | 688 | setMouse(mouse) { 689 | let rect = this.canvas.getBoundingClientRect(); 690 | if (mouse && 691 | mouse.x && mouse.x >= rect.left && mouse.x <= rect.right && 692 | mouse.y && mouse.y >= rect.top && mouse.y <= rect.bottom) { 693 | 694 | this.gl.uniform2f(this.frontTarget.mouseID, mouse.x - rect.left, this.canvas.height - (mouse.y - rect.top)); 695 | } 696 | } 697 | 698 | updateTile() { 699 | let gl = this.gl; 700 | let tile_max = this.tile_size; 701 | 702 | this.passes = 0; 703 | 704 | if (this.tile[0] < this.total_tiles[0]) { 705 | this.tile[0] += 1; 706 | 707 | if (this.tile[0] == this.total_tiles[0] - 1) 708 | tile_max[0] = Math.abs(this.canvas.width - (this.total_tiles[0]) * this.tile_size[0]); 709 | } else { 710 | this.tile[0] = 0; 711 | 712 | if (this.tile[1] < this.total_tiles[1]) { 713 | this.tile[1] += 1; 714 | 715 | if (this.tile[1] == this.total_tiles[1] - 1) 716 | tile_max[1] = Math.abs(this.canvas.height - (this.total_tiles[1]) * this.tile_size[1]); 717 | } else { 718 | this.paused = true; 719 | this.tile[1] = 0; 720 | } 721 | } 722 | 723 | let tile_min = [ 724 | this.tile[0] * this.tile_size[0], 725 | this.tile[1] * this.tile_size[1] 726 | ]; 727 | 728 | gl.viewport(tile_min[0], tile_min[1], tile_max[0], tile_max[1]); 729 | } 730 | 731 | clear() { 732 | let gl = this.gl; 733 | 734 | let empty_tex = new Float32Array(this.canvas.width * this.canvas.height * 4); 735 | 736 | gl.bindTexture(gl.TEXTURE_2D, this.textures["back_target_tex"]); 737 | gl.texImage2D( 738 | gl.TEXTURE_2D, // target 739 | 0, // level 740 | gl.RGBA32F, // internal format 741 | this.canvas.width, // width 742 | this.canvas.height, // height 743 | 0, // border 744 | gl.RGBA, // format 745 | gl.FLOAT, // type 746 | empty_tex // pixels 747 | ); 748 | 749 | gl.bindTexture(gl.TEXTURE_2D, this.textures["front_target_tex"]); 750 | gl.texImage2D( 751 | gl.TEXTURE_2D, // target 752 | 0, // level 753 | gl.RGBA32F, // internal format 754 | this.canvas.width, // width 755 | this.canvas.height, // height 756 | 0, // border 757 | gl.RGBA, // format 758 | gl.FLOAT, // type 759 | empty_tex // pixels 760 | ); 761 | 762 | gl.clear(gl.COLOR_BUFFER_BIT); 763 | } 764 | 765 | // RENDER FUNCTION 766 | render() { 767 | let time = performance.now() - this.loadTime; 768 | let gl = this.gl; 769 | 770 | //------------------ CUSTOM SHADER (RAYTRACER) ---------------------- 771 | gl.useProgram(this.frontTarget.program); 772 | 773 | gl.uniform1ui(this.frontTarget.frameID, ++this.passes); 774 | gl.uniform1f(this.frontTarget.timeID, time); 775 | 776 | gl.activeTexture(gl.TEXTURE0); 777 | gl.bindTexture(gl.TEXTURE_2D, this.textures["back_target_tex"]); 778 | 779 | // Render custom shader to front buffer 780 | gl.bindFramebuffer(gl.FRAMEBUFFER, this.frontTarget.framebuffer); 781 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, this.textures["front_target_tex"], 0); 782 | gl.drawArrays(gl.TRIANGLES, 0, 6); 783 | 784 | //------------------ SCREEN SHADER (DISPLAY) ------------------------ 785 | 786 | gl.useProgram(this.display.program); 787 | 788 | gl.uniform1f(this.display.contributionID, 1.0 / this.passes); 789 | 790 | gl.activeTexture(gl.TEXTURE0); 791 | gl.bindTexture(gl.TEXTURE_2D, this.textures["front_target_tex"]); 792 | 793 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 794 | gl.drawArrays(gl.TRIANGLES, 0, 6); 795 | 796 | // texture ping pong 797 | let tmp = this.textures["back_target_tex"]; 798 | this.textures["back_target_tex"] = this.textures["front_target_tex"]; 799 | this.textures["front_target_tex"] = tmp; 800 | } 801 | } 802 | -------------------------------------------------------------------------------- /shaders/pathtracing/raytracer.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | /* 4 | *@highp - 16-bit, floating point range: -2^62 to 2^62, integer range: -2^16 to 2^16 5 | *@mediump - 10 bit, floating point range: -2^14 to 2^14, integer range: -2^10 to 2^10 6 | *@lowp - 8 bit, floating point range: -2 to 2, integer range: -2^8 to 2^8 7 | */ 8 | 9 | precision highp float; 10 | precision mediump int; 11 | precision mediump sampler3D; 12 | precision mediump sampler2DArray; 13 | precision mediump usampler2D; 14 | 15 | layout(location = 0) out highp vec4 FragColor; 16 | 17 | //------------------------------- UNIFORMS -------------------------------------- 18 | 19 | // canvas resolution 20 | uniform vec2 u_resolution; 21 | // curremt time 22 | uniform float u_time; 23 | // current frame 24 | uniform uint u_frame; 25 | // sdf meshes sample on grid 26 | /*uniform sampler3D u_sdf0;*/ 27 | // backbuffer, tex0, tex1, tex2, tex3, random tex, keyboard LUT 28 | uniform sampler2D u_bufferA, u_tex0, u_tex1, u_tex2, u_tex3, u_rnd_tex/*, u_keyboard*/; 29 | // enviroment cubemap 30 | uniform samplerCube u_cubemap; 31 | // camera attributes 32 | uniform vec3 u_camPos, u_camLookAt, u_camParams; 33 | 34 | //------------------------------- CONSTANTS ------------------------------------- 35 | 36 | #constants 37 | 38 | // epsilon & infinity 39 | const float EPSILON = 0.0001; 40 | const float INFINITY = 1e4; 41 | 42 | // Index of Refraction 43 | const lowp float ior_vacuum = 1.0; 44 | const lowp float ior_air = 1.00029; 45 | const lowp float ior_ice = 1.31; 46 | const lowp float ior_water = 1.33; 47 | const lowp float ior_coat = 1.4; 48 | const lowp float ior_glass = 1.53; 49 | const lowp float ior_sapphire = 1.77; 50 | const mediump float ior_diamond = 2.417; 51 | 52 | // PI & E consts 53 | const float PI = 3.1415926535897932384626433832795; 54 | const float ONE_OVER_PI = 1.5707963267948966192313216916398; 55 | const float TWO_PI = 6.283185307179586476925286766559; 56 | const float FOUR_PI = 12.566370614359172953850573533118; 57 | const float RAD = 0.01745329251994329576923690768489; 58 | const float E = 2.71828182845904523536028747135266; 59 | 60 | const lowp int NULL = -1; 61 | 62 | //------------------------------- 63 | 64 | const struct Ray { 65 | vec3 o, d; //origin, direction 66 | }; 67 | 68 | //------------------------------- 69 | 70 | const struct AABB{ 71 | vec3 b1; //bottom-left vertex 72 | vec3 tr; //top-right vertex 73 | }; 74 | //uniform u_sdf0_aabb{ AABB aabb; } sdf0_aabb; 75 | 76 | //------------------------------- 77 | 78 | const struct Hit{ 79 | vec3 n, pos; // hitpoint normal, position 80 | lowp int index; // index of mesh 81 | vec2 uv; // texture uv 82 | vec4 texel; // texture element 83 | }; 84 | const Hit HIT_MISS = Hit(vec3(0.0), vec3(0.0), 0, vec2(-1.0), vec4(0.0)); 85 | 86 | //=============================================================================== 87 | //----------------------------------- TEXTURES ---------------------------------- 88 | //=============================================================================== 89 | 90 | //texture types 91 | const lowp int TEXTURE0 = 0; 92 | const lowp int TEXTURE1 = 1; 93 | const lowp int TEXTURE2 = 2; 94 | const lowp int TEXTURE3 = 3; 95 | const lowp int VORONOI = 4; 96 | const lowp int GRADIENT_NOISE = 5; 97 | const lowp int VALUE_NOISE = 6; 98 | const lowp int CHECK = 7; 99 | const lowp int RIPPLE = 8; 100 | const lowp int METAL = 9; 101 | 102 | 103 | const struct Texture{ 104 | vec3 c_mask, e_mask; //color, emission/specular mask 105 | vec4 params; //generator parameters 106 | int t; //texture type 107 | }; 108 | //----- EMPTY TEXTURE ----- 109 | const Texture NULL_TEX = Texture(vec3(1.0), vec3(1.0), vec4(0.0), NULL); 110 | 111 | //----- TEXTURES 0-3 ------ 112 | const Texture TEX_0 = Texture(vec3(1.0), vec3(1.0), vec4(0.0, 0.0, 0.0, 1.0), TEXTURE0); 113 | const Texture TEX_1 = Texture(vec3(1.0), vec3(1.0), vec4(0.0, 0.0, 0.0, 1.0), TEXTURE1); 114 | const Texture TEX_2 = Texture(vec3(1.0), vec3(1.0), vec4(0.0, 0.0, 0.0, 1.0), TEXTURE2); 115 | const Texture TEX_3 = Texture(vec3(1.0), vec3(1.0), vec4(0.0, 0.0, 0.0, 1.0), TEXTURE3); 116 | 117 | //----- NOISE TEXTURES ----- 118 | const Texture TEX_VALUE_NOISE = Texture(vec3(1.0), vec3(1.0), vec4(16.0), VALUE_NOISE); 119 | const Texture TEX_CHECK = Texture(vec3(1.0), vec3(0.0), vec4(5.0, 5.0, 2.0, 0.0), CHECK); 120 | const Texture TEX_METAL = Texture(vec3(0.7,0.25,0.055), vec3(0.6, 0.2, 0.6), vec4(16.0,10.0,16.0,0.0), METAL); 121 | 122 | //=============================================================================== 123 | //-------------------------------- MATERIALS ------------------------------------ 124 | //=============================================================================== 125 | 126 | // material types 127 | const lowp int LIGHT = 0; 128 | const lowp int DIR_LIGHT = 1; 129 | const lowp int DIFF = 2; 130 | const lowp int SPEC = 3; 131 | const lowp int REFR_FRESNEL = 4; 132 | const lowp int REFR_SCHLICK = 5; 133 | const lowp int COAT = 6; 134 | 135 | 136 | const struct Material{ 137 | vec3 c, e; //color, emission/glossiness 138 | float nt; //index of refraction 139 | lowp int t; //type 140 | Texture tex; //assigned texture 141 | bvec4 opts; //color texture, emission/glossiness texture, bump texture, backface culling on/off 142 | }; 143 | //----- EMPTY MATERIAL ----- 144 | const Material NULL_MAT = Material(vec3(0.0), vec3(0.0), 0.0, -1, NULL_TEX, bvec4(false)); 145 | 146 | //----- GLASS MATERIALS ----- 147 | const Material MAT_REFR_CLEAR = Material(vec3(1.,0.5,0.), vec3(0.0), ior_glass, REFR_FRESNEL, NULL_TEX, bvec4(false)); 148 | const Material MAT_REFR_CLEAR_2 = Material(vec3(1.), vec3(0.0),ior_glass, REFR_SCHLICK, NULL_TEX, bvec4(false)); 149 | const Material MAT_REFR_SAPPHIRE = Material(vec3(1.), vec3(0.0), ior_sapphire, REFR_FRESNEL, NULL_TEX, bvec4(false)); 150 | const Material MAT_REFR_WATER = Material(vec3(0.25,0.64,0.88), vec3(0.0), ior_water, REFR_FRESNEL, NULL_TEX, bvec4(false)); 151 | 152 | const Material MAT_REFR_TEST = Material(vec3(1.0), vec3(0.0), ior_glass, REFR_FRESNEL, TEX_1, bvec4(true,false,false,false)); 153 | 154 | //----- LIGHT MATERIALS ----- 155 | const Material MAT_LIGHT_4 = Material(vec3(1.0), vec3(4.0), 0.0, LIGHT, NULL_TEX, bvec4(false)); 156 | const Material MAT_LIGHT_CANDLE_4 = Material(vec3(1.0, 0.57647058823, 0.16078431372), vec3(4.0), 0.0, LIGHT, NULL_TEX, bvec4(false)); 157 | const Material MAT_LIGHT_HALOGEN_4 = Material(vec3(1.0, 0.94509803921, 0.87843137254), vec3(4.0), 0.0, LIGHT, NULL_TEX, bvec4(false)); 158 | 159 | //----- EMISSIVE TEXTURE MATERIALS ----- 160 | const Material MAT_LIGHT_4_TEX = Material(vec3(1.0), vec3(1.0), 0.0, LIGHT, TEX_1, bvec4(true,false,false,false)); 161 | 162 | //----- SKY MATERIALS ----- 163 | const Material MAT_CLEAR_SKY = Material(vec3(0.25098039215, 0.61176470588, 1.0), vec3(1.0), 0.0, DIR_LIGHT, NULL_TEX, bvec4(false)); 164 | const Material MAT_OVERCAST_SKY = Material(vec3(0.78823529411, 0.8862745098, 1.0), vec3(1.0), 0.0, DIR_LIGHT, NULL_TEX, bvec4(false)); 165 | const Material MAT_DIRECT_SUNLIGHT = Material(vec3(1.0), vec3(1.0), 0.0, DIR_LIGHT, NULL_TEX, bvec4(false)); 166 | 167 | //----- SPECULAR MATERIALS ----- 168 | const Material MAT_MIRROR = Material(vec3(1.0), vec3(0.0), 0.0, SPEC, NULL_TEX, bvec4(false)); 169 | const Material MAT_METAL = Material(vec3(0.6), vec3(0.0), 0.0, SPEC, TEX_METAL, bvec4(false,true,false,false)); 170 | 171 | //----- DIFFUSE MATERIALS ----- 172 | const Material MAT_BLACK = Material(vec3(0.0, 0.0, 0.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 173 | const Material MAT_WHITE = Material(vec3(1.0, 1.0, 1.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 174 | const Material MAT_RED = Material(vec3(1.0, 0.0, 0.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 175 | const Material MAT_GREEN = Material(vec3(0.0, 1.0, 0.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 176 | const Material MAT_BLUE = Material(vec3(0.0, 0.0, 1.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 177 | 178 | const Material MAT_CORNELL_WHITE = Material(vec3(1.0, 1.0, 1.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 179 | const Material MAT_CORNELL_RED = Material(vec3(0.7, 0.12,0.05), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 180 | const Material MAT_CORNELL_GREEN = Material(vec3(0.2, 0.4, 0.36), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 181 | 182 | const Material MAT_YELLOW = Material(vec3(1.0, 1.0, 0.0), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 183 | const Material MAT_PURPLE = Material(vec3(0.50196078431, 0.0, 0.50196078431), vec3(0.0), 0.0, DIFF, NULL_TEX, bvec4(false)); 184 | 185 | //----- CHECKERED MATERIALS ----- 186 | const Material MAT_CHECK_WHITE = Material(vec3(0.0), vec3(0.0), 0.0, DIFF, TEX_CHECK, bvec4(true,false,false,false)); 187 | 188 | //----- COAT MATERIALS ----- 189 | const Material MAT_COAT_NAVY = Material(vec3(0.0, 0.0, 0.50196078431), vec3(1.0), ior_coat, COAT, NULL_TEX, bvec4(false)); 190 | const Material MAT_COAT_PURPLE = Material(vec3(0.50196078431, 0.0, 0.50196078431), vec3(0.0), ior_coat, COAT, NULL_TEX, bvec4(false)); 191 | const Material MAT_COAT_WAX = Material(vec3(0.9333, 0.6666, 0.6), vec3(0.005), ior_coat, COAT, NULL_TEX, bvec4(false)); 192 | 193 | //----- TEST ----- 194 | const Material MAT_TEST = Material(vec3(1.0), vec3(0.0), 0.0, DIFF, TEX_1, bvec4(true,false,false,false)); 195 | 196 | //=============================================================================== 197 | //---------------------------------- MESH --------------------------------------- 198 | //=============================================================================== 199 | 200 | // mesh types 201 | const lowp int SPHERE = 0; 202 | const lowp int PLANE = 1; 203 | const lowp int BOX = 2; 204 | const lowp int SDF = 3; 205 | const lowp int GRID_SDF = 4;//@ToDo 206 | const lowp int TRIANGLE = 5; 207 | 208 | 209 | const struct Mesh{ 210 | Material mat; //material 211 | lowp int t; //type 212 | vec3 pos; //position 213 | vec4 joker; //multi-functional data 214 | }; 215 | const Mesh empty_Mesh = Mesh(NULL_MAT, NULL, vec3(0.0), vec4(0.0)); 216 | 217 | //------------------------------ ACTIVE SCENE ----------------------------------- 218 | 219 | #scene 220 | 221 | const struct LightPathNode{ 222 | vec3 c, p, n; //color, position, normal 223 | }; 224 | const LightPathNode NULL_LightPathNode = LightPathNode(vec3(0.0), vec3(0.0), vec3(0.0)); 225 | 226 | // light path nodes 227 | //LightPathNode lpNodes[light_index.length()*LIGHT_PATH_LENGTH]; 228 | //const bool h_lights = light_index.length() >= 0; 229 | 230 | //=========================== RNG =============================== 231 | 232 | float hash(float seed){return fract(sin(seed)*43758.5453123);} 233 | vec2 hash2(vec2 seed){return fract(sin(seed)*vec2(43758.5453123,22578.1459123));} 234 | vec3 hash3(vec3 seed){return fract(sin(seed)*vec3(43758.5453123,22578.1459123,19642.3490423));} 235 | 236 | //===================== NOISE FUNCTIONS ========================= 237 | 238 | vec3 gradient_hash( vec3 p ){ 239 | p = vec3( dot(p,vec3(127.1,311.7, 74.7)), 240 | dot(p,vec3(269.5,183.3,246.1)), 241 | dot(p,vec3(113.5,271.9,124.6))); 242 | 243 | return -1.0 + 2.0*fract(sin(p)*43758.5453123); 244 | } 245 | 246 | // https://www.shadertoy.com/view/Xsl3Dl by @iq 247 | float gradient_noise( in vec3 p ){ 248 | vec3 i = floor( p ); 249 | vec3 f = fract( p ); 250 | 251 | vec3 u = f*f*(3.0-2.0*f); 252 | 253 | return mix( mix( mix( dot( gradient_hash( i + vec3(0.0,0.0,0.0) ), f - vec3(0.0,0.0,0.0) ), 254 | dot( gradient_hash( i + vec3(1.0,0.0,0.0) ), f - vec3(1.0,0.0,0.0) ), u.x), 255 | mix( dot( gradient_hash( i + vec3(0.0,1.0,0.0) ), f - vec3(0.0,1.0,0.0) ), 256 | dot( gradient_hash( i + vec3(1.0,1.0,0.0) ), f - vec3(1.0,1.0,0.0) ), u.x), u.y), 257 | mix( mix( dot( gradient_hash( i + vec3(0.0,0.0,1.0) ), f - vec3(0.0,0.0,1.0) ), 258 | dot( gradient_hash( i + vec3(1.0,0.0,1.0) ), f - vec3(1.0,0.0,1.0) ), u.x), 259 | mix( dot( gradient_hash( i + vec3(0.0,1.0,1.0) ), f - vec3(0.0,1.0,1.0) ), 260 | dot( gradient_hash( i + vec3(1.0,1.0,1.0) ), f - vec3(1.0,1.0,1.0) ), u.x), u.y), u.z ); 261 | } 262 | 263 | float value_hash(vec3 p){ 264 | p = fract( p*0.3183099+.1 ); 265 | p *= 17.0; 266 | return fract( p.x*p.y*p.z*(p.x+p.y+p.z) ); 267 | } 268 | 269 | // https://www.shadertoy.com/view/4sfGzS by @iq 270 | float value_noise( in vec3 x ){ 271 | 272 | vec3 p = floor(x); 273 | vec3 f = fract(x); 274 | f = f*f*(3.0-2.0*f); 275 | 276 | #if 1 277 | 278 | vec2 uv = (p.xy+vec2(37.0,17.0)*p.z) + f.xy; 279 | vec2 rg = textureLod( u_rnd_tex, (uv+0.5)/256.0, 0.0).yx; 280 | return mix( rg.x, rg.y, f.z ); 281 | #else 282 | 283 | return mix(mix(mix( value_hash(p+vec3(0,0,0)), 284 | value_hash(p+vec3(1,0,0)),f.x), 285 | mix( value_hash(p+vec3(0,1,0)), 286 | value_hash(p+vec3(1,1,0)),f.x),f.y), 287 | mix(mix( value_hash(p+vec3(0,0,1)), 288 | value_hash(p+vec3(1,0,1)),f.x), 289 | mix( value_hash(p+vec3(0,1,1)), 290 | value_hash(p+vec3(1,1,1)),f.x),f.y),f.z); 291 | 292 | #endif 293 | } 294 | 295 | // https://www.shadertoy.com/view/ldl3Dl by @iq 296 | vec3 voronoi( in vec3 x ){ 297 | vec3 p = floor( x ); 298 | vec3 f = fract( x ); 299 | 300 | float id = 0.0; 301 | vec2 res = vec2( 100.0 ); 302 | for( int k=-1; k<=1; ++k ) 303 | for( int j=-1; j<=1; ++j ) 304 | for( int i=-1; i<=1; ++i ) 305 | { 306 | vec3 b = vec3( float(i), float(j), float(k) ); 307 | 308 | #if 1 309 | 310 | vec3 hx = p + b ; 311 | vec3 r = vec3( b ) - f + texture( u_rnd_tex, (hx.xy+vec2(3.0,1.0)*hx.z+0.5)/256.0, -100.0 ).xyz; 312 | #else 313 | 314 | vec3 r = vec3( b ) - f + gradient_hash( p + b ); 315 | #endif 316 | 317 | float d = dot( r, r ); 318 | 319 | if( d < res.x ) 320 | { 321 | id = dot( p+b, vec3(1.0,57.0,113.0 ) ); 322 | res = vec2( d, res.x ); 323 | } 324 | else if( d < res.y ) 325 | { 326 | res.y = d; 327 | } 328 | } 329 | 330 | return vec3( sqrt( res ), abs(id) ); 331 | } 332 | 333 | lowp int DIFF_BOUNCES = 0; 334 | lowp int SPEC_BOUNCES = 0; 335 | lowp int TRANS_BOUNCES = 0; 336 | lowp int SCATTERING_EVENTS = 0; 337 | 338 | //------------------------------------------------------------------------------- 339 | 340 | mat3 scaleMatrix(float scale){ 341 | return mat3(scale,0.0,0.0, 342 | 0.0,scale,0.0, 343 | 0.0,0.0,scale); 344 | } 345 | 346 | mat3 scaleMatrix(vec3 scale){ 347 | return mat3(scale.x,0.0,0.0, 348 | 0.0,scale.y,0.0, 349 | 0.0,0.0,scale.z); 350 | } 351 | 352 | mat3 rotationMatrix(vec3 axis, float angle){ 353 | float s = sin(angle); 354 | float c = cos(angle); 355 | float oc = 1.0 - c; 356 | 357 | return mat3(oc * axis.x * axis.x + c, oc * axis.x * axis.y - axis.z * s, oc * axis.z * axis.x + axis.y * s, 358 | oc * axis.x * axis.y + axis.z * s, oc * axis.y * axis.y + c, oc * axis.y * axis.z - axis.x * s, 359 | oc * axis.z * axis.x - axis.y * s, oc * axis.y * axis.z + axis.x * s, oc * axis.z * axis.z + c ); 360 | } 361 | 362 | //------------------------------------------------------------------------------- 363 | 364 | void cartesianToSpherical( in vec3 xyz, out float rho, out float phi, out float theta ) { 365 | rho = sqrt((xyz.x * xyz.x) + (xyz.y * xyz.y) + (xyz.z * xyz.z)); 366 | phi = asin(xyz.y / rho); 367 | theta = atan( xyz.z, xyz.x ); 368 | } 369 | 370 | vec3 sphericalToCartesian( in float rho, in float phi, in float theta ) { 371 | float sinTheta = sin(theta); 372 | return vec3( sinTheta*cos(phi), sinTheta*sin(phi), cos(theta) )*rho; 373 | } 374 | 375 | //------------------------------------------------------------------------------- 376 | 377 | float schlick(Ray r, vec3 n, float nc, float nt){ 378 | float R0 = pow((nc - nt) / (nc + nt), 2.); 379 | return R0 + (1. - R0) * pow(1. + dot(n, r.d), 5.); 380 | } 381 | 382 | float fresnel(Ray r, vec3 n, float nc, float nt, vec3 refr){ 383 | float cosI = dot(r.d, n); 384 | float costT = dot(n, refr); 385 | 386 | float Rs = pow((nc * cosI - nt * costT) / (nc * cosI + nt * costT), 2.); 387 | float Rp = pow((nc * costT - nt * cosI) / (nc * costT + nt * cosI), 2.); 388 | return (Rs + Rp) * 0.5; 389 | } 390 | 391 | //=============================== SDF FUNCTIONS ================================= 392 | 393 | float sdBoxCheap( in vec3 p, in vec3 b){ 394 | vec3 d = abs(p) - b; 395 | return max(max(d.x, d.y), d.z); 396 | } 397 | 398 | float sdBox( in vec3 p, in vec3 b ){ 399 | vec3 d = abs(p) - b; 400 | return min(max(d.x,max(d.y,d.z)),0.0) + length(max(d,0.0)); 401 | } 402 | 403 | float sdSphere( in vec3 p, in float s ){ 404 | return length(p)-s; 405 | } 406 | 407 | float sdPlane( vec3 p, vec4 n ){ 408 | return dot(p,n.xyz) + n.w; 409 | } 410 | 411 | float sdCone( in vec3 p, in vec3 c ) 412 | { 413 | vec2 q = vec2( length(p.xz), p.y ); 414 | float d1 = -q.y-c.z; 415 | float d2 = max( dot(q,c.xy), q.y); 416 | return length(max(vec2(d1,d2),0.0)) + min(max(d1,d2), 0.); 417 | } 418 | 419 | float sdTriPrism( vec3 p, vec2 h ){ 420 | vec3 q = abs(p); 421 | return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5); 422 | } 423 | 424 | float sdEllipsoid( in vec3 p, in vec3 r ){ 425 | return (length( p/r ) - 1.0) * min(min(r.x,r.y),r.z); 426 | } 427 | 428 | float udRoundBox( in vec3 p, in vec3 b, in float r ){ 429 | return length(max(abs(p)-b,0.0))-r; 430 | } 431 | 432 | float sdCapsule( vec3 p, vec3 a, vec3 b, float r ){ 433 | vec3 pa = p - a, ba = b - a; 434 | float h = clamp( dot(pa,ba)/dot(ba,ba), 0.0, 1.0 ); 435 | return length( pa - ba*h ) - r; 436 | } 437 | 438 | float dot2( in vec3 v ) { return dot(v,v); } 439 | float udTriangle( vec3 p, vec3 a, vec3 b, vec3 c ){ 440 | vec3 ba = b - a; vec3 pa = p - a; 441 | vec3 cb = c - b; vec3 pb = p - b; 442 | vec3 ac = a - c; vec3 pc = p - c; 443 | vec3 nor = cross( ba, ac ); 444 | 445 | return sqrt( 446 | (sign(dot(cross(ba,nor),pa)) + 447 | sign(dot(cross(cb,nor),pb)) + 448 | sign(dot(cross(ac,nor),pc))<2.0) 449 | ? 450 | min( min( 451 | dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), 452 | dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), 453 | dot2(ac*clamp(dot(ac,pc)/dot2(ac),0.0,1.0)-pc) ) 454 | : 455 | dot(nor,pa)*dot(nor,pa)/dot2(nor) ); 456 | } 457 | 458 | float udQuad( vec3 p, vec3 a, vec3 b, vec3 c, vec3 d ){ 459 | vec3 ba = b - a; vec3 pa = p - a; 460 | vec3 cb = c - b; vec3 pb = p - b; 461 | vec3 dc = d - c; vec3 pc = p - c; 462 | vec3 ad = a - d; vec3 pd = p - d; 463 | vec3 nor = cross( ba, ad ); 464 | 465 | return sqrt( 466 | (sign(dot(cross(ba,nor),pa)) + 467 | sign(dot(cross(cb,nor),pb)) + 468 | sign(dot(cross(dc,nor),pc)) + 469 | sign(dot(cross(ad,nor),pd))<3.0) 470 | ? 471 | min( min( min( 472 | dot2(ba*clamp(dot(ba,pa)/dot2(ba),0.0,1.0)-pa), 473 | dot2(cb*clamp(dot(cb,pb)/dot2(cb),0.0,1.0)-pb) ), 474 | dot2(dc*clamp(dot(dc,pc)/dot2(dc),0.0,1.0)-pc) ), 475 | dot2(ad*clamp(dot(ad,pd)/dot2(ad),0.0,1.0)-pd) ) 476 | : 477 | dot(nor,pa)*dot(nor,pa)/dot2(nor) ); 478 | } 479 | 480 | //----------------------------------------------------- 481 | // SDF operations 482 | //----------------------------------------------------- 483 | 484 | float opS( float d1, float d2 ){ 485 | return max(-d2,d1); 486 | } 487 | 488 | float opU( float d1, float d2 ){ 489 | return min(d1,d2); 490 | } 491 | 492 | float opI( float d1, float d2 ){ 493 | return max(d1,d2); 494 | } 495 | 496 | vec3 opRep( vec3 p, vec3 c ){ 497 | return mod(p,c)-0.5*c; 498 | } 499 | 500 | vec3 opTwist( vec3 p ){ 501 | float c = cos(20.0*p.y); 502 | float s = sin(20.0*p.y); 503 | mat2 m = mat2(c,-s,s,c); 504 | return vec3(m*p.xz,p.y); 505 | } 506 | 507 | vec3 opCheapBend( vec3 p ){ 508 | float c = cos(20.0*p.y); 509 | float s = sin(20.0*p.y); 510 | mat2 m = mat2(c,-s,s,c); 511 | return vec3(m*p.xy,p.z); 512 | } 513 | 514 | float disp( vec3 p, const float phase, const float power){ 515 | return pow( 0.5 + 0.5*cos( p.x + 1.5*phase )* 516 | sin( p.y + 2.0*phase )* 517 | sin( p.z + 1.0*phase ), power); 518 | } 519 | 520 | float Snowball( vec3 p, float s ){ 521 | return sdSphere(p, s)-value_noise(p*8.0)*0.04; 522 | } 523 | 524 | float sdSeaBox( in vec3 p, in vec3 b, in float level ){ 525 | return opS(sdBox(p, b), sdPlane(p, vec4(0.,-1.,0.,level)) - disp(10.*p, 2.5, 1.0)*0.07-disp(15.*p, 4.5, 1.0)*0.03); 526 | } 527 | 528 | float sea(vec3 p, float turbulence){ 529 | return 0.0;//@ToDo 530 | } 531 | 532 | // by @iq 533 | float siggraph_obj( vec3 p ){ 534 | vec3 ax = vec3(-2.0,2.0,1.0)/3.0; 535 | vec3 ce = vec3(0.0,-0.2,-0.2); 536 | 537 | float d1 = dot(p,ax) - 0.1; 538 | float d2 = length(p) - 1.0; 539 | float d3 = length( p-ce - ax*dot(p-ce,ax)) - 1.0; 540 | 541 | return max( max( d1, d2 ), -d3 ); 542 | } 543 | 544 | // by @iq 545 | float MengerSponge( vec3 p, vec3 scale){ 546 | float d = sdBox(p, scale); 547 | 548 | float s = 1.0; 549 | for( int m=0; m<4; ++m ) 550 | { 551 | vec3 a = mod( p*s, 2.0 )-1.0; 552 | s *= 3.0; 553 | vec3 r = abs(1.0 - 3.0*abs(a)); 554 | float da = max(r.x,r.y); 555 | float db = max(r.y,r.z); 556 | float dc = max(r.z,r.x); 557 | float c = (min(da,min(db,dc))-1.0)/s; 558 | 559 | d = max(c, d); 560 | } 561 | 562 | return d; 563 | } 564 | 565 | // by @iq 566 | float Mandelbulb( vec3 p ){ 567 | vec3 w = p; 568 | float m = dot(w,w); 569 | 570 | vec4 trap = vec4(abs(w),m); 571 | float dz = 1.0; 572 | 573 | 574 | for( int i=0; i<4; ++i ) 575 | { 576 | #if 1 577 | float m2 = m*m; 578 | float m4 = m2*m2; 579 | dz = 8.0*sqrt(m4*m2*m)*dz + 1.0; 580 | 581 | float x = w.x; float x2 = x*x; float x4 = x2*x2; 582 | float y = w.y; float y2 = y*y; float y4 = y2*y2; 583 | float z = w.z; float z2 = z*z; float z4 = z2*z2; 584 | 585 | float k3 = x2 + z2; 586 | float k2 = inversesqrt( k3*k3*k3*k3*k3*k3*k3 ); 587 | float k1 = x4 + y4 + z4 - 6.0*y2*z2 - 6.0*x2*y2 + 2.0*z2*x2; 588 | float k4 = x2 - y2 + z2; 589 | 590 | w.x = p.x + 64.0*x*y*z*(x2-z2)*k4*(x4-6.0*x2*z2+z4)*k1*k2; 591 | w.y = p.y + -16.0*y2*k3*k4*k4 + k1*k1; 592 | w.z = p.z + -8.0*y*k4*(x4*x4 - 28.0*x4*x2*z2 + 70.0*x4*z4 - 28.0*x2*z2*z4 + z4*z4)*k1*k2; 593 | #else 594 | dz = 8.0*pow(m,3.5)*dz + 1.0; 595 | 596 | float r = length(w); 597 | float b = 8.0*acos( clamp(w.y/r, -1.0, 1.0)); 598 | float a = 8.0*atan( w.x, w.z ); 599 | w = p + pow(r,8.0) * vec3( sin(b)*sin(a), cos(b), sin(b)*cos(a) ); 600 | #endif 601 | 602 | trap = min( trap, vec4(abs(w),m) ); 603 | 604 | m = dot(w,w); 605 | if( m > 4.0 ) 606 | break; 607 | } 608 | trap.x = m; 609 | 610 | return 0.25*log(m)*sqrt(m)/dz; 611 | } 612 | 613 | vec2 map( vec3 p ){ 614 | vec2 sdf_meshes[max(NUM_SDFS, 1)]; 615 | 616 | // meshes[NUM_MESHES+0].data = vec4(udRoundBox(opRot(p-vec3(0.75, -0.5, -0.8), vec3(0.0, 1.0, 0.0), 35.), vec3(0.5,0.5,0.5), 0.1)); 617 | #sdf_meshes 618 | 619 | vec2 res = sdf_meshes[0]; 620 | for(int i=1; i< NUM_SDFS; ++i){ 621 | res = mix( sdf_meshes[i], res, float(res.x < sdf_meshes[i].x) ); 622 | } 623 | 624 | return res; 625 | } 626 | 627 | vec3 calcNormal( in vec3 pos ){ 628 | vec3 eps = vec3(EPSILON*2.0,0.0,0.0); 629 | 630 | return normalize( vec3( 631 | map( pos+eps.xyy ).x - map( pos-eps.xyy ).x, 632 | map( pos+eps.yxy ).x - map( pos-eps.yxy ).x, 633 | map( pos+eps.yyx ).x - map( pos-eps.yyx ).x ) ); 634 | } 635 | 636 | //------------------------------------------------------------------------------- 637 | 638 | vec4 getTexel(const Material mat, in Hit hit, in float t){ 639 | 640 | if(mat.tex.t == TEXTURE0 || mat.tex.t == TEXTURE1 || mat.tex.t == TEXTURE2 || mat.tex.t == TEXTURE3 641 | || mat.tex.t == CHECK || mat.tex.t == RIPPLE){ 642 | 643 | if(mat.tex.t == TEXTURE0 ) 644 | { 645 | return textureLod( u_tex0, hit.uv, 0. ); 646 | } 647 | else if(mat.tex.t == TEXTURE1 ) 648 | { 649 | return textureLod( u_tex1, hit.uv, 0. ); 650 | } 651 | else if(mat.tex.t == TEXTURE2 ) 652 | { 653 | return textureLod( u_tex2, hit.uv, 0. ); 654 | } 655 | else if(mat.tex.t == TEXTURE3 ) 656 | { 657 | return textureLod( u_tex3, hit.uv, 0. ); 658 | } 659 | else if(mat.tex.t == CHECK ) 660 | { 661 | return vec4( mod( floor(mat.tex.params.x*hit.uv.x) + floor(mat.tex.params.y*hit.uv.y), mat.tex.params.z) ); 662 | } 663 | else if(mat.tex.t == RIPPLE ) 664 | { 665 | return vec4( mod( ceil(distance(hit.uv, mat.tex.params.xy)*mat.tex.params.z),mat.tex.params.w) ); 666 | } 667 | } 668 | else if(mat.tex.t == VORONOI ) 669 | { 670 | return vec4( voronoi( mat.tex.params.xyz*hit.pos ), 0. ); 671 | } 672 | else if(mat.tex.t == GRADIENT_NOISE ) 673 | { 674 | float f = gradient_noise( mat.tex.params.xyz*hit.pos ); 675 | return vec4(smoothstep( -0.7, 0.7, f )); 676 | } 677 | else if(mat.tex.t == VALUE_NOISE ) 678 | { 679 | return vec4( value_noise( mat.tex.params.xyz*hit.pos ) ); 680 | } 681 | else if(mat.tex.t == METAL) 682 | { 683 | const vec3 m = vec3(-1.2,1.99,-1.6); 684 | 685 | vec3 q = mat.tex.params.xyz*hit.pos; 686 | float f = 0.5000*value_noise( q ); q = m*q*2.01; 687 | f += 0.2500*value_noise( q ); q = m*q*2.02; 688 | f += 0.1250*value_noise( q ); q = m*q*2.03; 689 | f += 0.0625*value_noise( q ); q = m*q*2.01; 690 | 691 | return vec4(f); 692 | } 693 | else 694 | { 695 | return vec4(0.0); 696 | } 697 | 698 | } 699 | 700 | //----------------------------------------------------- 701 | // Intersection functions 702 | //----------------------------------------------------- 703 | 704 | //> quadric equation solver 705 | bool solveQuadratic(float A, float B, float C, out float t0, out float t1) { 706 | float discrim = B*B-4.0*A*C; 707 | 708 | if ( discrim <= 0.0 ) return false; 709 | 710 | float rootDiscrim = sqrt( discrim ); 711 | 712 | float t_0 = (-B-rootDiscrim)/(2.0*A); 713 | float t_1 = (-B+rootDiscrim)/(2.0*A); 714 | 715 | t0 = min( t_0, t_1 ); 716 | t1 = max( t_0, t_1 ); 717 | 718 | return true; 719 | } 720 | 721 | //> aabb intersection 722 | bool iAABB(in AABB aabb, in Ray r){ 723 | vec3 tx1 = (aabb.b1 - r.o)*(1.0 / r.d); 724 | vec3 tx2 = (aabb.tr - r.o)*(1.0 / r.d); 725 | 726 | float tmin = min(tx1.x, tx2.x); 727 | tmin = min(tmin, min(tx1.y, tx2.y)); 728 | tmin = min(tmin, min(tx1.z, tx2.z)); 729 | 730 | float tmax = max(tx1.x, tx2.x); 731 | tmax = max(tmax, max(tx1.y, tx2.y)); 732 | tmax = max(tmax, max(tx1.z, tx2.z)); 733 | 734 | return tmax >= tmin; 735 | } 736 | 737 | //> plane intersection 738 | bool iPlane(const Mesh plane, in Ray r, in float tmin, out float t){ 739 | t = (-plane.joker.x - dot(plane.pos, r.o)) / dot(plane.pos, r.d); 740 | return (t > EPSILON) && (t < tmin); 741 | } 742 | 743 | //> sphere intersection 744 | bool iSphere(const Mesh sphere, in Ray r, in float tmin, out float t){ 745 | vec3 op = sphere.pos - r.o; 746 | float b = dot(op, r.d); 747 | float det = b * b - dot(op, op) + sphere.joker.x * sphere.joker.x; 748 | 749 | if(det < 0.0) return false; 750 | 751 | det = sqrt(det); 752 | 753 | t = b - det; 754 | if(t > EPSILON && t < tmin) return true; 755 | 756 | t = b + det; 757 | if(t > EPSILON && t < tmin) return true; 758 | 759 | return false; 760 | } 761 | 762 | //> box intersection 763 | bool iBox(const Mesh box, in Ray r, in float tmin, out float t, out vec3 n){ 764 | vec3 minCorner = box.pos - box.joker.x * 0.5; 765 | vec3 maxCorner = box.pos + box.joker.x * 0.5; 766 | 767 | vec3 invDir = 1.0 / r.d; 768 | vec3 dmin = (minCorner - r.o) * invDir; 769 | vec3 dmax = (maxCorner - r.o) * invDir; 770 | 771 | vec3 real_min = min(dmin, dmax); 772 | vec3 real_max = max(dmin, dmax); 773 | 774 | float minmax = min( min(real_max.x, real_max.y), real_max.z); 775 | float maxmin = max( max(real_min.x, real_min.y), real_min.z); 776 | 777 | if (minmax > maxmin) 778 | { 779 | 780 | if (maxmin > 0.0) // if we are outside the box 781 | { 782 | n = -sign(r.d) * step(real_min.yzx, real_min) * step(real_min.zxy, real_min); 783 | t = maxmin; 784 | } 785 | else if (minmax > 0.0) // else if we are inside the box 786 | { 787 | n = -sign(r.d) * step(real_max, real_max.yzx) * step(real_max, real_max.zxy); 788 | t = minmax; 789 | } 790 | 791 | } 792 | 793 | return (t > EPSILON) && (t < tmin); 794 | } 795 | 796 | /*//----------------------------------------------------------------------------------------- 797 | 798 | //> triangle intersection 799 | bool iTriangle(in vec3 v1, in vec3 v2, in vec3 v3, in Mesh mesh, in Ray r, in float tmin, in bool forShadowTest, out float t, out Hit hit){ 800 | vec3 e0 = v2 - v1; 801 | vec3 e1 = v3 - v1; 802 | 803 | vec3 h = cross(r.d, e1); 804 | float a = dot(e0, h); 805 | 806 | if(mesh.mat.opts[3] && a < EPSILON) // backface culling on 807 | return false; 808 | else if(!mesh.mat.opts[3] && a > -EPSILON && a < EPSILON) // backface culling off 809 | return false; 810 | 811 | float f = 1.0 / a; 812 | 813 | vec3 s = r.o - v1; 814 | float u = f * dot(s, h); 815 | 816 | if(u < 0.0 || u > 1.0) return false; 817 | 818 | vec3 q = cross(s, e0); 819 | float v = f * dot(r.d, q); 820 | 821 | if(v < 0.0 || u + v > 1.0) 822 | return false; 823 | 824 | t = f * dot(e1, q); 825 | 826 | return (t > EPSILON) && (t < tmin); 827 | } 828 | 829 | //> cylinder intersection 830 | bool iCylinder(in Ray r, in Mesh mesh, in bool forShadowTest, out float t, out Hit hit){ 831 | 832 | // Compute quadratic cylinder coefficients 833 | float a = r.d.x*r.d.x + r.d.y*r.d.y; 834 | float b = 2.0 * (r.d.x*r.o.x + r.d.y*r.o.y); 835 | float c = r.o.x*r.o.x + r.o.y*r.o.y - mesh.joker.x*mesh.joker.x; 836 | 837 | // Solve quadratic equation for _t_ values 838 | float t0, t1; 839 | if (!solveQuadratic( a, b, c, t0, t1)) 840 | return false; 841 | 842 | if ( t1 < 0.0 ) 843 | return false; 844 | 845 | t = t0; 846 | 847 | if (t0 < 0.0) 848 | t = t1; 849 | 850 | // Compute cylinder hit point and $\phi$ 851 | vec3 phit = r.o + r.d*t; 852 | float phi = atan(phit.y,phit.x) + PI; 853 | 854 | if (phi < 0.0) 855 | phi += TWO_PI; 856 | 857 | // Test cylinder intersection against clipping parameters 858 | if ( (phit.z < mesh.joker.y) || (phit.z > mesh.joker.z) || (phi > mesh.joker.w) ) { 859 | if (t == t1) 860 | return false; 861 | 862 | t = t1; 863 | // Compute cylinder hit point and $\phi$ 864 | phit = r.o + r.d*t; 865 | phi = atan(phit.y,phit.x); 866 | phi += PI; 867 | 868 | if ( (phit.z < mesh.joker.y) || (phit.z > mesh.joker.z) || (phi > mesh.joker.w) ) 869 | return false; 870 | } 871 | 872 | if( !forShadowTest ) { 873 | hit.pos = phit; 874 | hit.uv.x = (phit.z - mesh.joker.y)/(mesh.joker.z - mesh.joker.y); 875 | hit.uv.y = phi/mesh.joker.w; 876 | hit.n = normalize( vec3( phit.xy, 0.0 ) ); 877 | //hit.tangent = vec3( 0.0, 0.0, 1.0 ); 878 | } 879 | 880 | return true; 881 | } 882 | 883 | //> GRID SDF intersection 884 | bool iGRID_SDF(in Ray r, in float tmin, out float t, out Mesh mesh){ 885 | t = EPSILON*10.; 886 | 887 | if(!iAABB(sdf0_aabb.aabb, r)) return false; 888 | 889 | for(int i=0; itmin ) return false; 898 | } 899 | 900 | // assign normal 901 | mesh.mat.n = calcNormal(r.o+r.d*t); 902 | 903 | return true; 904 | } 905 | 906 | //-----------------------------------------------------------------------------------------*/ 907 | 908 | //> SDF intersection 909 | bool iSDF(in Ray r, in float tmin, out float t, out vec3 n, out int index){ 910 | t = EPSILON*10.; 911 | 912 | vec2 res; 913 | 914 | for(int i=0; itmin ) break; 918 | t += h*FUDGE_FACTOR; 919 | } 920 | 921 | if( t>tmin ) return false; 922 | 923 | // assign normal 924 | n = calcNormal(r.o+r.d*t); 925 | index = NUM_MESHES + int(res.y); 926 | 927 | return true; 928 | } 929 | 930 | //--------------- Main Intersection function ---------------// 931 | 932 | float intersection(in Ray r, out Hit hit){ 933 | hit = HIT_MISS; 934 | 935 | int type = NULL; 936 | float tt = INFINITY; 937 | float tmin = INFINITY; 938 | 939 | //-------- Mesh Intersection -------- 940 | if(U_EUCLIDEAN){ 941 | for(int i = 0; i < NUM_MESHES; ++i) 942 | { 943 | if(meshes[i].joker.x == 0.0) continue;// continue if not def 944 | 945 | if(U_SPHERE && meshes[i].t == SPHERE){ 946 | if(iSphere(meshes[i], r, tmin, tt)) 947 | { 948 | tmin = tt; 949 | type = SPHERE; 950 | hit.index = i; 951 | } 952 | } else if(U_PLANE && meshes[i].t == PLANE){ 953 | if(iPlane(meshes[i], r, tmin, tt)) 954 | { 955 | tmin = tt; 956 | type = PLANE; 957 | hit.index = i; 958 | } 959 | } else if(U_BOX && meshes[i].t == BOX){ 960 | if(iBox(meshes[i], r, tmin, tt, hit.n)) 961 | { 962 | tmin = tt; 963 | type = BOX; 964 | hit.index = i; 965 | } 966 | } 967 | } 968 | } 969 | 970 | 971 | //-------- SDF Intersection -------- 972 | if(U_SDF){ 973 | if(iSDF(r, tmin, tt, hit.n, hit.index)) 974 | { 975 | tmin = tt; 976 | type = SDF; 977 | } 978 | } 979 | 980 | //----------------------- DATA PARSER ----------------------- 981 | if(bool(type+1)) 982 | { 983 | hit.pos = r.d * tmin + r.o; 984 | 985 | if(U_EUCLIDEAN && ( U_SPHERE && type == SPHERE || U_PLANE && type == PLANE || U_BOX && type == BOX )){// Sphere 986 | 987 | if(U_SPHERE && type == SPHERE) { 988 | float rho, phi, theta; 989 | cartesianToSpherical( hit.pos, rho, phi, theta ); 990 | 991 | hit.uv = vec2(phi/PI, theta/TWO_PI); 992 | hit.n = normalize(hit.pos - meshes[hit.index].pos); 993 | // hit.n = (x - spheres[ss[1]].data.xyz)/spheres[ss[1]].data.w; 994 | } 995 | else if(U_PLANE && type == PLANE) { 996 | hit.n = normalize(meshes[hit.index].pos); 997 | } 998 | 999 | } 1000 | 1001 | if(!bool(hit.uv.x+1.0)){ 1002 | vec3 nl = abs(hit.n); 1003 | 1004 | hit.uv = nl.x > nl.y && nl.x > nl.z ? -hit.pos.zy: 1005 | nl.y > nl.x && nl.y > nl.z ? hit.pos.xz: 1006 | vec2(hit.pos.x, -hit.pos.y); 1007 | } 1008 | 1009 | if(meshes[hit.index].mat.tex.t != NULL) hit.texel = getTexel(meshes[hit.index].mat, hit, tmin); 1010 | } 1011 | 1012 | return tmin; 1013 | } 1014 | 1015 | /* 1016 | SOURCE: 1017 | "Building an Orthonormal Basis from a 3D Unit Vector Without Normalization" 1018 | http://orbit.dtu.dk/files/126824972/onb_frisvad_jgt2012_v2.pdf 1019 | 1020 | "Building an Orthonormal Basis, Revisited" 1021 | http://jcgt.org/published/0006/01/01/ 1022 | */ 1023 | void calc_binormals(vec3 n, out vec3 ox, out vec3 oz){ 1024 | float sig = n.z < 0.0 ? 1.0 : -1.0; 1025 | 1026 | float a = 1.0 / (n.z - sig); 1027 | float b = n.x * n.y * a; 1028 | 1029 | ox = vec3(1.0 + sig * n.x * n.x * a, sig * b, sig * n.x); 1030 | oz = vec3(b, sig + n.y * n.y * a, n.y); 1031 | } 1032 | 1033 | vec3 getSampleBiased(vec3 w, float power, float seed){ 1034 | vec3 u, v; 1035 | calc_binormals(w, u ,v); 1036 | 1037 | // Convert to spherical coords aligned to dir 1038 | vec2 r = hash2(vec2(seed)); 1039 | r.x = r.x * TWO_PI; 1040 | r.y = pow(r.y, 1.0 / (power + 1.0)); 1041 | 1042 | float oneminus = sqrt(1.0 - r.y * r.y); 1043 | return normalize(cos(r.x) * oneminus * u + sin(r.x) * oneminus * v + r.y * w); 1044 | } 1045 | 1046 | vec3 getConeSample(vec3 w, float extent, float seed){ 1047 | vec3 u, v; 1048 | calc_binormals(w, u ,v); 1049 | 1050 | // Convert to spherical coords aligned to dir 1051 | vec2 r = hash2(vec2(seed)); 1052 | r.x = r.x * TWO_PI; 1053 | r.y = 1.0 - r.y * extent; 1054 | 1055 | float oneminus = sqrt(1.0 - r.y * r.y); 1056 | return normalize(cos(r.x) * oneminus * u + sin(r.x) * oneminus * v + r.y * w); 1057 | } 1058 | 1059 | vec3 getRandomDirection(vec3 n, float seed){ 1060 | #ifdef USE_BIASED_SAMPLING 1061 | return getSampleBiased(n, 1.0, seed); 1062 | #else 1063 | return getConeSample(n, 1.0, seed); 1064 | #endif 1065 | } 1066 | 1067 | vec3 randomSphereDirection(float seed){ 1068 | vec2 r = hash2(vec2(seed))*TWO_PI; 1069 | return vec3(sin(r.x)*vec2(sin(r.y),cos(r.y)),cos(r.x)); 1070 | } 1071 | 1072 | vec3 randomHemisphereDirection(vec3 n, float seed){ 1073 | vec3 dr = randomSphereDirection(seed); 1074 | return dot(dr,n) * dr; 1075 | } 1076 | 1077 | vec3 calcDirectLighting(const Mesh light, vec3 x, vec3 nl, float seed){ 1078 | 1079 | Hit hit; 1080 | vec3 dirLight = vec3(0.0); 1081 | 1082 | // Light source with geometry 1083 | if(light.mat.t == LIGHT){ 1084 | 1085 | if(U_SPHERE && light.t == SPHERE){ 1086 | 1087 | vec3 ld = light.pos + (randomSphereDirection(seed + 23.1656) * light.joker.x); 1088 | vec3 srDir = normalize(ld - x); 1089 | 1090 | // cast shadow ray from intersection point 1091 | float t = intersection(Ray(x, srDir), hit); 1092 | Mesh mesh = meshes[hit.index]; 1093 | 1094 | if( mesh.mat.t == LIGHT ){ 1095 | float r2 = mesh.joker.x * mesh.joker.x; 1096 | vec3 d = mesh.pos - x; 1097 | float d2 = dot(d,d); 1098 | float cos_a_max = sqrt(1. - clamp( r2 / d2, 0., 1.)); 1099 | float weight = 2. * (1. - cos_a_max); 1100 | dirLight += max(mix(mesh.mat.c, hit.texel.rgb, hit.texel.a), 0.001) * mesh.mat.e * weight * max(0.001, dot(srDir, nl)); 1101 | } 1102 | } else if(U_SDF && light.t == SDF){ 1103 | 1104 | vec3 ld = light.pos + (randomSphereDirection(seed + 78.2358) * light.joker.xyz); 1105 | vec3 srDir = normalize(ld - x); 1106 | 1107 | // cast shadow ray from intersection point 1108 | float t = intersection(Ray(x, srDir), hit); 1109 | Mesh mesh = meshes[hit.index]; 1110 | 1111 | if( mesh.mat.t == LIGHT ){ 1112 | dirLight += max(mix(mesh.mat.c, hit.texel.rgb, hit.texel.a), 0.001) * mesh.mat.e * max(0.001, dot(srDir, nl)); 1113 | } 1114 | } 1115 | } 1116 | // Directional Light source 1117 | else if(light.mat.t == DIR_LIGHT){ 1118 | 1119 | float t = intersection(Ray(x, light.pos), hit); 1120 | 1121 | if(t == INFINITY){ 1122 | dirLight += light.mat.c * light.mat.e * max(0.001, dot(light.pos, nl)); 1123 | } 1124 | } 1125 | 1126 | return dirLight; 1127 | } 1128 | 1129 | void brdf(in Hit hit, in vec3 f, in vec3 e, in float t, in float inside, inout Ray r, inout vec3 mask, inout vec3 acc, inout bool bounceIsSpecular, in float seed, in float bounce){ 1130 | 1131 | vec3 x = hit.pos; 1132 | vec3 n = hit.n; 1133 | vec3 nl = hit.n * inside; 1134 | 1135 | vec3 _randomDir = getRandomDirection(nl, seed + 7.1*float(u_frame) + 5681.123 + bounce*92.13); 1136 | // vec3 _randomSphereDirection = randomSphereDirection(seed + 12.456*u_time + bounce*136.045); 1137 | 1138 | // material e is also used as a glossiness factor 1139 | vec3 _roughness = e * _randomDir; 1140 | // vec3 _reflDirection = normalize(_roughness + reflect(r.d, nl)); 1141 | 1142 | float nc = ior_air; // IOR of air 1143 | float nt = meshes[hit.index].mat.nt; // IOR of mesh 1144 | 1145 | if(meshes[hit.index].mat.t == DIFF){ // DIFFUSE 1146 | r = Ray(x + nl*EPSILON, _randomDir); 1147 | mask *= f; 1148 | 1149 | ++DIFF_BOUNCES; 1150 | bounceIsSpecular = false; 1151 | } else if(meshes[hit.index].mat.t == SPEC){ 1152 | r = Ray(x + nl*EPSILON, normalize(_roughness + reflect(r.d, nl))); 1153 | ++SPEC_BOUNCES; 1154 | bounceIsSpecular = true; 1155 | } else if(meshes[hit.index].mat.t == REFR_FRESNEL || meshes[hit.index].mat.t == REFR_SCHLICK){ // REFRACTIVE 1156 | float nnt = inside < 0. ? nt/nc : nc/nt; 1157 | vec3 tdir = normalize(_roughness + refract(r.d, nl, nnt)); 1158 | 1159 | r.o = x; 1160 | 1161 | // total internal reflection 1162 | if(dot(tdir, tdir) == 0.0) { 1163 | r.o += nl*EPSILON; 1164 | r.d = normalize(_roughness + reflect(r.d, nl)); 1165 | ++SPEC_BOUNCES; 1166 | return; 1167 | } 1168 | 1169 | // select either schlick or fresnel approximation 1170 | float Re = mix(schlick(r, nl, nc, nt), fresnel(r, nl, nc, nt, tdir), meshes[hit.index].mat.t == REFR_FRESNEL); 1171 | 1172 | if( hash(seed) < Re){ 1173 | r.o += nl*EPSILON; 1174 | r.d = normalize(_roughness + reflect(r.d, nl)); 1175 | ++SPEC_BOUNCES; 1176 | } else { 1177 | r.o -= nl*EPSILON; 1178 | mask *= f; 1179 | r.d = tdir; 1180 | ++SCATTERING_EVENTS; 1181 | } 1182 | bounceIsSpecular = true; 1183 | } else if(meshes[hit.index].mat.t == COAT){ // COAT 1184 | r.o = x + nl*EPSILON; 1185 | 1186 | // choose either specular reflection or diffuse 1187 | if( hash(seed) < schlick(r, nl, nc, nt) ){ 1188 | r.d = normalize(_roughness + reflect(r.d, nl)); 1189 | ++SPEC_BOUNCES; 1190 | bounceIsSpecular = true; 1191 | } else { 1192 | r.d =_randomDir; 1193 | mask *= f; 1194 | 1195 | ++DIFF_BOUNCES; 1196 | bounceIsSpecular = false; 1197 | } 1198 | } 1199 | 1200 | if(!bounceIsSpecular){ 1201 | 1202 | #ifdef USE_CUBEMAP 1203 | vec3 srDir = getRandomDirection(nl, seed + bounce*965.325); 1204 | 1205 | float t = intersection(Ray(x, srDir), hit); 1206 | if(t == INFINITY){ 1207 | acc += mask * texture(u_cubemap, srDir).rgb * max(0.001, dot(srDir, nl)); 1208 | } 1209 | #endif 1210 | 1211 | if(sample_lights){ 1212 | for(int i = 0; i < light_index.length(); ++i){ 1213 | acc += calcDirectLighting(meshes[light_index[i]], x, nl, seed + 8652.1*float(u_frame) + 5681.123 + bounce*7895.13)*mask; 1214 | } 1215 | } 1216 | } 1217 | 1218 | // mask *= max(0.001, dot(r.d, n)); // cosine weighted importance sampling 1219 | } 1220 | 1221 | //----------------------------------------------------- 1222 | // Radiance 1223 | //----------------------------------------------------- 1224 | 1225 | vec3 radiance(Ray r, float seed){ 1226 | 1227 | vec3 acc = vec3(0.); 1228 | vec3 mask = vec3(1.); 1229 | 1230 | bool bounceIsSpecular = true; 1231 | 1232 | for (int depth = 0; depth < MAX_BOUNCES; ++depth){ 1233 | 1234 | Hit hit; 1235 | float t = intersection(r, hit); 1236 | 1237 | if(t == INFINITY){ 1238 | 1239 | if(!bounceIsSpecular && sample_lights) break; 1240 | 1241 | #ifdef USE_CUBEMAP 1242 | acc += mask * texture(u_cubemap, r.d).rgb; 1243 | #elif defined (USE_PROCEDURAL_SKY) 1244 | acc += mask * vec3(0.5) + vec3(0.5) * cos(TWO_PI * (vec3(0.525, 0.408, 0.409) + vec3(0.9, 0.97, 0.8) * clamp(r.d.y * 0.6 + 0.5, 0.3, 1.0))); 1245 | #endif 1246 | 1247 | break; 1248 | } 1249 | 1250 | Mesh mesh = meshes[hit.index]; 1251 | 1252 | // color 1253 | vec3 c = max(mix(mesh.mat.c, hit.texel.rgb * mesh.mat.tex.c_mask, float(mesh.mat.opts[0])*hit.texel.a), 0.001); 1254 | 1255 | // 1 if outside 1256 | float inside = -sign(dot(r.d, hit.n)); 1257 | 1258 | // emission 1259 | vec3 e = max(mix(mesh.mat.e, hit.texel.rgb * mesh.mat.tex.e_mask, float(mesh.mat.opts[1])*hit.texel.a), 0.001); 1260 | 1261 | if(mesh.mat.t == LIGHT){ 1262 | if(bounceIsSpecular || !sample_lights){ 1263 | mask *= c; 1264 | acc += mask*e; 1265 | } 1266 | 1267 | break; 1268 | } 1269 | 1270 | brdf(hit, c, e, t, inside, r, mask, acc, bounceIsSpecular, seed, float(depth)); 1271 | 1272 | // terminate if necessary 1273 | if( DIFF_BOUNCES >= MAX_DIFF_BOUNCES || SPEC_BOUNCES >= MAX_SPEC_BOUNCES || 1274 | TRANS_BOUNCES >= MAX_TRANS_BOUNCES || SCATTERING_EVENTS >= MAX_SCATTERING_EVENTS ) break; 1275 | } 1276 | 1277 | return acc; 1278 | } 1279 | 1280 | //----------------------------------------------------- 1281 | // main 1282 | //----------------------------------------------------- 1283 | 1284 | void main(void){ 1285 | vec2 st = 2.0 * gl_FragCoord.xy / u_resolution - 1.; 1286 | float aspect = u_resolution.x/u_resolution.y; 1287 | 1288 | float seed = hash(dot( gl_FragCoord.xy, vec2(12.9898, 78.233) ) + 1113.1*float(u_frame)); 1289 | 1290 | //camera setup 1291 | float theta = u_camParams.x*RAD; 1292 | float uVLen = tan(theta/2.); 1293 | float uULen = aspect * uVLen; 1294 | 1295 | vec3 w = normalize(u_camLookAt); 1296 | vec3 u = normalize(cross(w,vec3(0.0, 1.0, 0.0))); 1297 | vec3 v = cross(u, w); 1298 | 1299 | float r1 = 2.0 * hash(seed+13.271); 1300 | float r2 = 2.0 * hash(seed+63.216); 1301 | 1302 | // AA offset 1303 | vec2 d = vec2( 1304 | r1 < 1.0 ? sqrt(r1) - 1.0 : 1.0 - sqrt(2.0 - r1), 1305 | r2 < 1.0 ? sqrt(r2) - 1.0 : 1.0 - sqrt(2.0 - r2) 1306 | ); 1307 | d /= (u_resolution * 0.5); 1308 | d += st; 1309 | 1310 | vec3 focalPoint = normalize(d.x * u * uULen + d.y * v * uVLen + w) * u_camParams.z; 1311 | 1312 | // random point on aperture 1313 | float randomAngle = hash(seed+496.4562) * TWO_PI; 1314 | float randomRadius = hash(seed+249.1686) * u_camParams.y; 1315 | vec3 randomAperturePos = ( cos(randomAngle) * u + sin(randomAngle) * v ) * randomRadius; 1316 | 1317 | // primary ray 1318 | Ray r = Ray(u_camPos + randomAperturePos, normalize(focalPoint - randomAperturePos)); 1319 | FragColor.rgb = texelFetch(u_bufferA, ivec2(gl_FragCoord.xy), 0).rgb + radiance(r, seed); 1320 | } 1321 | -------------------------------------------------------------------------------- /shaders/tonemapper.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision mediump float; 4 | 5 | layout(location = 0) out highp vec4 FragColor; 6 | 7 | uniform sampler2D u_bufferA; 8 | uniform float u_cont; 9 | 10 | // gamma correction 11 | const vec3 gamma = vec3(1./2.2); 12 | // image exposure 13 | const float exposure = 1.5; 14 | // tonemapping whitepoint 15 | const float whitepoint = exposure*(gamma.x * 2.0); 16 | 17 | vec3 ACESFilm( vec3 x ) 18 | { 19 | const float a = 2.51; 20 | const float b = 0.03; 21 | const float c = 2.43; 22 | const float d = 0.59; 23 | const float e = 0.14; 24 | 25 | return (x*(a*x+b))/(x*(c*x+d)+e); 26 | } 27 | 28 | void main(){ 29 | 30 | vec3 col = texelFetch( u_bufferA, ivec2(gl_FragCoord.xy), 0 ).rgb * u_cont; 31 | 32 | #if 0 33 | 34 | col *= exposure; 35 | float sum = col.x + col.y + col.z; 36 | float x = col.x / sum; 37 | float y = col.y / sum; 38 | 39 | // compute Reinhard tonemapping scale factor 40 | float scale = (1.0 + col.y/(whitepoint*whitepoint)) / (1.0 + col.y); 41 | col.y *= scale; 42 | col.x = x * col.y / y; 43 | col.z = (1.0 - x - y) * (col.y / y); 44 | 45 | #endif 46 | 47 | FragColor = vec4(pow(col, gamma), 1.0); 48 | } 49 | -------------------------------------------------------------------------------- /shaders/vert.glsl: -------------------------------------------------------------------------------- 1 | #version 300 es 2 | 3 | precision highp float; 4 | 5 | layout(location = 0) in vec2 a_position; 6 | 7 | void main(void) { 8 | gl_Position = vec4(a_position, 0.0, 1.0); 9 | } 10 | -------------------------------------------------------------------------------- /textures/rgba_noise/rgba_noise1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/rgba_noise/rgba_noise1024.png -------------------------------------------------------------------------------- /textures/rgba_noise/rgba_noise2048.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/rgba_noise/rgba_noise2048.png -------------------------------------------------------------------------------- /textures/rgba_noise/rgba_noise256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/rgba_noise/rgba_noise256.png -------------------------------------------------------------------------------- /textures/rgba_noise/rgba_noise512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/rgba_noise/rgba_noise512.png -------------------------------------------------------------------------------- /textures/tex0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/tex0.png -------------------------------------------------------------------------------- /textures/tex1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/tex1.png -------------------------------------------------------------------------------- /textures/tex2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/tex2.png -------------------------------------------------------------------------------- /textures/tex3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Mourtz/raytracer-0/dfe4b9561a87a9db3852eb1eb72168aeea0efa4a/textures/tex3.png -------------------------------------------------------------------------------- /tools.js: -------------------------------------------------------------------------------- 1 | "use strict"; 2 | 3 | function fillArray(array, type, num) { 4 | for (let i = 0; i < num; i++) { 5 | array[i] = new type(); 6 | } 7 | return array; 8 | } 9 | 10 | Number.prototype.clamp = function (min, max) { 11 | return Math.min(Math.max(this, min), max); 12 | }; 13 | 14 | function vec4array_to_f32Array(array) { 15 | let i = 0; 16 | let j = 0; 17 | let f32 = new Float32Array(array.length * 4); 18 | while (i < array.length) { 19 | let v = array[i]; 20 | f32[j] = v.x; 21 | f32[j + 1] = v.y; 22 | f32[j + 2] = v.z; 23 | f32[j + 3] = v.w; 24 | i = i + 1; 25 | j = j + 4; 26 | } 27 | return f32; 28 | } 29 | 30 | function vec3array_to_f32Array(array) { 31 | let i = 0; 32 | let j = 0; 33 | let f32 = new Float32Array(array.length * 3); 34 | while (i < array.length) { 35 | let v = array[i]; 36 | f32[j] = v.x; 37 | f32[j + 1] = v.y; 38 | f32[j + 2] = v.z; 39 | i = i + 1; 40 | j = j + 3; 41 | } 42 | return f32; 43 | } 44 | 45 | function vec2array_to_f32Array(array) { 46 | let i = 0; 47 | let j = 0; 48 | let f32 = new Float32Array(array.length * 2); 49 | while (i < array.length) { 50 | let v = array[i]; 51 | f32[j] = v.x; 52 | f32[j + 1] = v.y; 53 | i = i + 1; 54 | j = j + 2; 55 | } 56 | return f32; 57 | } 58 | 59 | function sgn(v) { 60 | if (v > 0.0) { 61 | return 1.0; 62 | } else { 63 | return -1.0; 64 | } 65 | } 66 | 67 | function fetchHTTP(url) { 68 | let request = new XMLHttpRequest(); 69 | let response; 70 | 71 | request.onreadystatechange = function () { 72 | if (request.readyState === 4 && request.status === 200) { 73 | response = request.responseText; 74 | } 75 | } 76 | request.open('GET', url, false); 77 | request.overrideMimeType("text/plain"); 78 | request.send(null); 79 | return response; 80 | } 81 | 82 | function parseShader(filepath, sandbox) { 83 | let constants = sandbox.constants; 84 | let defines = sandbox.defines; 85 | let scene = sandbox.scene; 86 | let sdf_meshes = sandbox.sdf_meshes; 87 | 88 | if (!filepath) return console.error("You got to give a filepath"); 89 | 90 | let string = fetchHTTP(filepath); 91 | let line = string.split("\n"); 92 | 93 | for (let i = 0; i < line.length; i++) { 94 | if (line[i].charAt(0) == "#") { 95 | if (line[i].slice(0, 8) == "#include") { 96 | let index = line[i].indexOf("'") 97 | if (index == -1) index = line[i].indexOf('"'); 98 | if (index == -1) return console.error(); 99 | filepath = line[i].slice(index + 1); 100 | 101 | index = filepath.indexOf("'") 102 | if (index == -1) index = filepath.indexOf('"'); 103 | if (index == -1) return console.error(); 104 | filepath = filepath.slice(0, index); 105 | 106 | line[i] = parseShader(filepath); 107 | } else if (defines && constants && line[i].slice(0, 10) == "#constants") { 108 | line[i] = defines.concat(constants).join("\n"); 109 | } else if (scene && line[i].slice(0, 6) == "#scene") { 110 | line[i] = scene; 111 | } else if (sdf_meshes && line[i].slice(0, 11) == "#sdf_meshes") { 112 | line[i] = sdf_meshes.join("\n"); 113 | } else { 114 | console.error(); 115 | } 116 | } 117 | } 118 | 119 | let result = line.join("\n"); 120 | return result; 121 | } 122 | 123 | function parseSDF(filepath) { 124 | if (!filepath) return console.error("You got to give a filepath"); 125 | 126 | let string = fetchHTTP(filepath); 127 | let line = string.split("\n"); 128 | 129 | let dimensions = line[0].split(" "); 130 | let bb_min = line[1].split(" "); 131 | let bb_max = line[2].split(" "); 132 | 133 | if (true) { 134 | return { 135 | "dimensions": dimensions, 136 | "bb_min": bb_min, 137 | "bb_max": bb_max, 138 | "values": new Float32Array(line.slice(3)) 139 | }; 140 | } else { 141 | return new Float32Array(dimensions.concat(bb_min).concat(bb_max).concat(line.slice(3))); 142 | } 143 | } 144 | 145 | function elementInViewport(el) { 146 | let top = el.offsetTop; 147 | let left = el.offsetLeft; 148 | let width = el.offsetWidth; 149 | let height = el.offsetHeight; 150 | while (el.offsetParent) { 151 | el = el.offsetParent; 152 | top += el.offsetTop; 153 | left += el.offsetLeft; 154 | } 155 | return (top < (window.pageYOffset + window.innerHeight) && left < (window.pageXOffset + window.innerWidth) && (top + height) > window.pageYOffset && (left + width) > window.pageXOffset); 156 | } 157 | 158 | window.requestAnimFrame = window.requestAnimationFrame || window.webkitRequestAnimationFrame || window.mozRequestAnimationFrame || window.oRequestAnimationFrame || window.msRequestAnimationFrame || function (callback) { 159 | window.setTimeout(callback, 1e3 / 60); 160 | }; 161 | 162 | window.cancelAnimFrame = window.cancelAnimationFrame || window.mozCancelAnimationFrame; 163 | -------------------------------------------------------------------------------- /vector.js: -------------------------------------------------------------------------------- 1 | let Vector2 = /** @class */ (function () { 2 | function Vector2(x, y) { 3 | if (x === void 0) { 4 | x = 0.0; 5 | } 6 | if (y === void 0) { 7 | y = 0.0; 8 | } 9 | this.x = x; 10 | this.y = y; 11 | } 12 | 13 | Vector2.prototype.get = function (axis) { 14 | switch (axis) { 15 | case 1: 16 | return this.x; 17 | case 2: 18 | return this.y; 19 | } 20 | }; 21 | 22 | Object.defineProperty(Vector2.prototype, "glData", { 23 | get: function () { 24 | return new Float32Array([this.x, this.y]); 25 | }, 26 | enumerable: true, 27 | configurable: true 28 | }); 29 | 30 | return Vector2; 31 | }()); 32 | 33 | let Vector4 = /** @class */ (function () { 34 | function Vector4(x, y, z, w) { 35 | if (x === void 0) { 36 | x = 0.0; 37 | } 38 | if (y === void 0) { 39 | y = 0.0; 40 | } 41 | if (z === void 0) { 42 | z = 0.0; 43 | } 44 | if (w === void 0) { 45 | w = 0.0; 46 | } 47 | this.x = x; 48 | this.y = y; 49 | this.z = z; 50 | this.w = w; 51 | } 52 | 53 | Vector4.prototype.get = function (axis) { 54 | switch (axis) { 55 | case 1: 56 | return this.x; 57 | case 2: 58 | return this.y; 59 | case 3: 60 | return this.z; 61 | case 4: 62 | return this.w; 63 | } 64 | }; 65 | 66 | Object.defineProperty(Vector4.prototype, "glData", { 67 | get: function () { 68 | return new Float32Array([this.x, this.y, this.z, this.w]); 69 | }, 70 | enumerable: true, 71 | configurable: true 72 | }); 73 | 74 | return Vector4; 75 | }()); 76 | 77 | let Vector3 = (function () { 78 | function Vector3(x, y, z) { 79 | if (x === void 0) { 80 | x = 0.0; 81 | } 82 | if (y === void 0) { 83 | y = 0.0; 84 | } 85 | if (z === void 0) { 86 | z = 0.0; 87 | } 88 | this.x = x; 89 | this.y = y; 90 | this.z = z; 91 | } 92 | 93 | Object.defineProperty(Vector3.prototype, "glData", { 94 | get: function () { 95 | return new Float32Array([this.x, this.y, this.z]); 96 | }, 97 | enumerable: true, 98 | configurable: true 99 | }); 100 | 101 | Vector3.prototype.add = function (b) { 102 | return new Vector3(this.x + b.x, this.y + b.y, this.z + b.z); 103 | }; 104 | 105 | Vector3.prototype.addScalar = function (f) { 106 | return new Vector3(this.x + f, this.y + f, this.z + f); 107 | }; 108 | 109 | Vector3.prototype.sub = function (b) { 110 | return new Vector3(this.x - b.x, this.y - b.y, this.z - b.z); 111 | }; 112 | 113 | Vector3.prototype.subScalar = function (f) { 114 | return new Vector3(this.x - f, this.y - f, this.z - f); 115 | }; 116 | 117 | Vector3.prototype.mul = function (b) { 118 | return new Vector3(this.x * b.x, this.y * b.y, this.z * b.z); 119 | }; 120 | 121 | Vector3.prototype.mulScalar = function (f) { 122 | return new Vector3(this.x * f, this.y * f, this.z * f); 123 | }; 124 | 125 | Vector3.prototype.div = function (b) { 126 | return new Vector3(this.x / b.x, this.y / b.y, this.z / b.z); 127 | }; 128 | 129 | Vector3.prototype.divScalar = function (f) { 130 | return new Vector3(this.x / f, this.y / f, this.z / f); 131 | }; 132 | 133 | Vector3.prototype.dot = function (b) { 134 | return (this.x * b.x) + (this.y * b.y) + (this.z * b.z); 135 | }; 136 | 137 | Vector3.prototype.length = function () { 138 | return Math.sqrt((this.x * this.x) + (this.y * this.y) + (this.z * this.z)); 139 | }; 140 | 141 | Vector3.prototype.normalize = function () { 142 | let d = this.length(); 143 | return new Vector3(this.x / d, this.y / d, this.z / d); 144 | }; 145 | 146 | Vector3.prototype.op_remainder = function (b) { 147 | return new Vector3(this.y * b.z - this.z * b.y, this.z * b.x - this.x * b.z, this.x * b.y - this.y * b.x); 148 | }; 149 | 150 | Vector3.prototype.maximize = function (b) { 151 | return new Vector3(this.x > b.x ? this.x : b.x, this.y > b.y ? this.y : b.y, this.z > b.z ? this.z : b.z); 152 | }; 153 | 154 | Vector3.prototype.minimize = function (b) { 155 | return new Vector3(this.x < b.x ? this.x : b.x, this.y < b.y ? this.y : b.y, this.z < b.z ? this.z : b.z); 156 | }; 157 | 158 | Vector3.prototype.get = function (axis) { 159 | switch (axis) { 160 | case 1: 161 | return this.x; 162 | case 2: 163 | return this.y; 164 | case 3: 165 | return this.z; 166 | } 167 | }; 168 | 169 | return Vector3; 170 | }()); 171 | 172 | //let tes = new Vector3(1,2,3); 173 | //let tes1 = new Vector3(4,5,6); 174 | --------------------------------------------------------------------------------