├── 1matmul.txt ├── Final Presentation.pdf ├── Links.txt ├── Pics ├── CUDA PathTracer 5000 iterations.bmp ├── DebugIntersectMatColor.bmp ├── DebugIntersectMatEmit.bmp ├── DebugIntersectNormal.bmp ├── DebugIntersectPos.bmp ├── DebugRayDir.bmp ├── FinalResultFromWebGL.bmp ├── SSAA comparison.bmp ├── cover.jpg ├── performance_firefox.JPG └── subsurface+ssaa.bmp ├── README.md ├── WebGL_Path_Tracer.html ├── js ├── WebGL_Path_Tracer.js ├── lib │ ├── dat.gui.min.js │ ├── gl-matrix.js │ └── stats.min.js └── webGLUtility.js ├── left-arrow.png └── right-arrow.png /1matmul.txt: -------------------------------------------------------------------------------- 1 | vec3 randomDirectionInSphere(float seed) { 2 | float u = getrandom(vec3(12.9898, 78.233, 151.7182), seed); 3 | float v = getrandom(vec3(63.7264, 10.873, 623.6736), seed); 4 | 5 | float up = u * 2.0 - 1.0; // cos(theta) 6 | float over = sqrt(1.0 - up * up); // sin(theta) 7 | float around = v * 3.14; 8 | 9 | return vec3( up, cos(around) * over, sin(around) * over ); 10 | } 11 | 12 | 13 | mat4 multiply(in mat4 mat, in mat4 mato) { 14 | mat4 dest; 15 | 16 | // Cache the matrix values (makes for huge speed increases!) 17 | float a00 = mat[0][0], a01 = mat[0][1], a02 = mat[0][2], a03 = mat[0][3]; 18 | float a10 = mat[1][0], a11 = mat[1][1], a12 = mat[1][2], a13 = mat[1][3]; 19 | float a20 = mat[2][0], a21 = mat[2][1], a22 = mat[2][2], a23 = mat[2][3]; 20 | float a30 = mat[3][0], a31 = mat[3][1], a32 = mat[3][2], a33 = mat[3][3]; 21 | 22 | // Cache only the current line of the second matrix 23 | float b0 = mato[0][0], b1 = mato[0][1], b2 = mato[0][2], b3 = mato[0][3]; 24 | dest[0][0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 25 | dest[0][1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 26 | dest[0][2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 27 | dest[0][3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 28 | 29 | 30 | b0 = mato[1][0]; 31 | b1 = mato[1][1]; 32 | b2 = mato[1][2]; 33 | b3 = mato[1][3]; 34 | dest[1][0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 35 | dest[1][1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 36 | dest[1][2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 37 | dest[1][3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 38 | 39 | b0 = mato[2][0]; 40 | b1 = mato[2][1]; 41 | b2 = mato[2][2]; 42 | b3 = mato[2][3]; 43 | dest[2][0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 44 | dest[2][1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 45 | dest[2][2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 46 | dest[2][3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 47 | 48 | b0 = mato[3][0]; 49 | b1 = mato[3][1]; 50 | b2 = mato[3][2]; 51 | b3 = mato[3][3]; 52 | dest[3][0] = b0*a00 + b1*a10 + b2*a20 + b3*a30; 53 | dest[3][1] = b0*a01 + b1*a11 + b2*a21 + b3*a31; 54 | dest[3][2] = b0*a02 + b1*a12 + b2*a22 + b3*a32; 55 | dest[3][3] = b0*a03 + b1*a13 + b2*a23 + b3*a33; 56 | 57 | return dest; 58 | } 59 | 60 | 61 | 62 | vec3 getRandomPointOnSphere(in GEOM sphere, in float seed){ 63 | 64 | float radius = 0.5; 65 | 66 | vec3 point; 67 | 68 | float theta, phi; 69 | theta = 180.0 * getrandom(vec3(12.9898, 78.233, 151.7182), seed); //0-180 70 | phi = 360.0 * getrandom(vec3(78.233, 12.9898, 151.7182), seed+1.0); //0-360 71 | 72 | float degToRad = PI / 180.0; 73 | 74 | point.x = radius * sin(theta * degToRad) * cos(phi * degToRad); 75 | point.y = radius * sin(theta * degToRad) * sin(phi * degToRad); 76 | point.z = radius * cos(theta * degToRad); 77 | 78 | vec3 randPoint = (sphere.model * vec4(point, 1.0)).xyz; 79 | 80 | return randPoint; 81 | } 82 | 83 | vec3 getRandomPointOnCube(in GEOM cube, in float seed){ 84 | 85 | vec3 origin = (cube.model * vec4(0.0,0.0,0.0,1.0)).xyz; 86 | vec3 xmax = (cube.model * vec4(0.5,0.0,0.0,1.0)).xyz; 87 | vec3 ymax = (cube.model * vec4(0.0,0.5,0.0,1.0)).xyz; 88 | vec3 zmax = (cube.model * vec4(0.0,0.0,0.5,1.0)).xyz; 89 | float xradius = distance(origin, xmax); 90 | float yradius = distance(origin, ymax); 91 | float zradius = distance(origin, zmax); 92 | 93 | //get surface areas of sides 94 | vec3 radii = vec3(xradius,yradius,zradius); 95 | float side1 = radii.x * radii.y * 4.0; //x-y face 96 | float side2 = radii.z * radii.y * 4.0; //y-z face 97 | float side3 = radii.x * radii.z * 4.0; //x-z face 98 | float totalarea = 2.0 * (side1+side2+side3); 99 | 100 | //pick random face, weighted by surface area 101 | float russianRoulette = getrandom(vec3(12.9898, 78.233, 151.7182), seed); //0-1 102 | 103 | float rand1 = getrandom(vec3(78.233, 12.9898, 151.7182), seed + 1.0) - 0.5; //-0.5-0.5 104 | float rand2 = getrandom(vec3(151.7182, 12.9898, 78.233), seed + 2.0) - 0.5; //-0.5-0.5 105 | 106 | vec3 point = vec3(0.5,0.5,0.5); 107 | 108 | if(russianRoulette<(side1/totalarea)) 109 | point = vec3(rand1, rand2, 0.5); 110 | else if(russianRoulette<((side1*2.0)/totalarea)) 111 | point = vec3(rand1, rand2, -0.5); 112 | else if(russianRoulette<(((side1*2.0)+(side2))/totalarea)) 113 | point = vec3(0.5, rand1, rand2); 114 | else if(russianRoulette<(((side1*2.0)+(side2*2.0))/totalarea)) 115 | point = vec3(-0.5, rand1, rand2); 116 | else if(russianRoulette<(((side1*2.0)+(side2*2.0)+(side3))/totalarea)) 117 | point = vec3( rand1, 0.5, rand2); 118 | else 119 | point = vec3( rand1, -0.5, rand2); 120 | 121 | 122 | vec3 randPoint = (cube.model * vec4(point,1.0)).xyz; 123 | 124 | return randPoint; 125 | 126 | } 127 | 128 | -------------------------------------------------------------------------------- /Final Presentation.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Final Presentation.pdf -------------------------------------------------------------------------------- /Links.txt: -------------------------------------------------------------------------------- 1 | http://www.opengl-tutorial.org/beginners-tutorials/tutorial-3-matrices/#The_Model__View_and_Projection_matrices -------------------------------------------------------------------------------- /Pics/CUDA PathTracer 5000 iterations.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/CUDA PathTracer 5000 iterations.bmp -------------------------------------------------------------------------------- /Pics/DebugIntersectMatColor.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/DebugIntersectMatColor.bmp -------------------------------------------------------------------------------- /Pics/DebugIntersectMatEmit.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/DebugIntersectMatEmit.bmp -------------------------------------------------------------------------------- /Pics/DebugIntersectNormal.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/DebugIntersectNormal.bmp -------------------------------------------------------------------------------- /Pics/DebugIntersectPos.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/DebugIntersectPos.bmp -------------------------------------------------------------------------------- /Pics/DebugRayDir.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/DebugRayDir.bmp -------------------------------------------------------------------------------- /Pics/FinalResultFromWebGL.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/FinalResultFromWebGL.bmp -------------------------------------------------------------------------------- /Pics/SSAA comparison.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/SSAA comparison.bmp -------------------------------------------------------------------------------- /Pics/cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/cover.jpg -------------------------------------------------------------------------------- /Pics/performance_firefox.JPG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/performance_firefox.JPG -------------------------------------------------------------------------------- /Pics/subsurface+ssaa.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/Pics/subsurface+ssaa.bmp -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | WebGL PathTracer 3 | ================ 4 | 5 | Members 6 | ------------------- 7 | [Bo Zhang](https://www.linkedin.com/pub/bo-zhang/7b/767/815) , [Ying Li](https://www.linkedin.com/in/liying3)
8 | 9 | Introduction 10 | ------------------- 11 | In this project, we implement a WebGL version path-tracer. Most of computation of path tracer are written in the shader and we also add UI on the webpage which enables users to make their own scene. 12 | 13 | ####Features implemented: 14 | - Basic path tracer 15 | - Diffuse surfaces 16 | - Diffuse reflection 17 | - Fresnel Based Reflection & Refraction 18 | - Camera interactivity 19 | - Subsurface scattering (Fake) 20 | - Super-Sample Anti alias 21 | - Realtime Add new primitives 22 | 23 | Demo 24 | ------------------- 25 | Website: [WebGL PathTracer](http://wulinjiansheng.github.io/WebGL_PathTracer/)
26 | Video: \ 27 | [![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/cover.jpg)](http://youtu.be/Hm6VyPIbKP0) 28 | \ 29 | Slide: https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Final%20Presentation.pdf 30 | 31 | Screenshots 32 | ------------------- 33 | #### Final Result(5000 iterations): 34 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/FinalResultFromWebGL.bmp) 35 |
36 | #### Debug views: 37 | - Initray Direction Test
38 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/DebugRayDir.bmp) 39 |

40 | - Intersection Normal Test
41 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/DebugIntersectNormal.bmp) 42 |

43 | - Intersection Position Test
44 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/DebugIntersectPos.bmp) 45 |

46 | - Intersection Geometry Color Test
47 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/DebugIntersectMatColor.bmp) 48 |

49 | - Intersection Geometry Emittance Test
50 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/DebugIntersectMatEmit.bmp) 51 |

52 | 53 | 54 | Implementation Details: 55 | ------------------------ 56 | ####1. WebGL framework 57 | - Ping-pong textures 58 | We use Ping-pong technique to mix each iteration's image with previous result. That we store the previous iteration's image in texture0 and after path tracer computation we mix texture0's color with the new computed result color and store this new iteration's image in texture1. Then we exchange texture0 and texture1 and run the next iteration, so on and so forth. 59 | 60 | - Texture parameters 61 | We store the objects' information in a texture and in the shader we read objects' parameters from this texture. More specifically, every 7 pixels of the texture image store one object's information. This enables us to pass only one uniform to pass all the objects' information, which enables users to add as many objects as they want in the scene. (We set the max number of objects as 30.) 62 |

63 | **Store Pattern:**
64 | #### 65 | |Pixel | Object's Parameter 66 | |---------|---------------------- 67 | |0 | `Color` 68 | |1 | `Objtype,Texturetype` 69 | |2 | `Refelective,Refractive` 70 | |3 | `IOR,Subsurface Scattering,Emittance` 71 | |4 | `Translation` 72 | |5 | `Rotation` 73 | |6 | `Scale` 74 | 75 | 76 | ###2. Path tracer 77 | - Fresnel Based Reflection & Refraction
78 | **Reference**: http://en.wikipedia.org/wiki/Fresnel_equations
79 | We add fresnel reflection and refraction. And it enables us to add transparent objects in the scene. To do this, we just use the fresnel equations to compute the reflective and refractive coefficients whenever the ray hits a refractive object, and get the reflect ray and refract ray. Then we generate a random number to decide which ray to return, based on the reflective and refractive coefficients. 80 | 81 | - Super sample anti-alisasing
82 | **Reference**: http://en.wikipedia.org/wiki/Supersampling
83 | We add super sample anti-alisasing, which makes my render result smoother. To do this, just jitter the initial rays randomly in each iteration.
84 | (Right image is with SSAA; 1500 iterations)
85 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/SSAA%20comparison.bmp) 86 |

87 | 88 | - Subsurface scattering (Fake)
89 | **Reference**: https://machinesdontcare.wordpress.com/tag/subsurface/
90 | We use a fakery way to implement subsurface scattering.
91 | We can see that light is scattered by interacting with the transparent sphere. But the result is still not very realistic.
92 | (Right image is with subsurface scattering; 2500 iterations with SSAA)
93 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/subsurface%2Bssaa.bmp) 94 |

95 | 96 | - Utility functions
97 | **Reference**: https://github.com/toji/gl-matrix
98 | We also write some mat4 utility functions in the shader, including mat translate,rotate,scale,inverse and transpose. 99 | 100 | ###3. UI 101 | - We use dat.gui.js to provide UI for the path tracer scene. Users can resize the size of rendered image, add new objects, currently including cube and sphere, to the current scene; and change the attribute of the objects. Once the configuration of the scene is changed, the image will be clear and rendered again. 102 | 103 | - We also provide mouse interaction to translate, rotate and zoom in/ out the scene. We use plane to represent the wall, so the wall will be cur off if not faced to camera. 104 | 105 | Performance Evaluation 106 | ------------------------------------------------------------------------------- 107 | ###1. Cuda-Path tracer vs Webgl-Path tracer 108 | Both test on default scene(Same objects parameters and same trace depth) and run for 5000 iterations. 109 | 110 | - Final result on cuda (800X800):
111 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/CUDA%20PathTracer%205000%20iterations.bmp) 112 | - Final result on WebGL (800X800):
113 | ![Alt text](https://github.com/wulinjiansheng/WebGL_PathTracer/blob/master/Pics/FinalResultFromWebGL.bmp) 114 |

115 | **FPS Comparison:**
116 | 117 | |Version | Average FPS 118 | |---------|---------------------- 119 | |CUDA | `6.47` 120 | |WebGL | `12` 121 |
122 | From the result we can see that the WebGl version has a better performace. 123 | 124 | ###2. Webgl (Timing of each Procedure) 125 | |Procedure| Timing (ms) 126 | |---------|---------------------- 127 | |Initialize WebGL | `7` 128 | |Initialize Shader | `45` 129 | |Load Scene | `42` 130 | |Draw Scene (Avg.) | `42` 131 |
132 | ###3. Number of objects
133 | Scene size: 800 X 800 134 | 135 | |Number of Objects| Average FPS 136 | |---------|---------------------- 137 | |Default(14) | `12` 138 | |20 | `9` 139 | |Max(30) | `6` 140 |
141 | 142 | Thrid Party Code 143 | ------------------------------------------------------------------------------- 144 | * stas.js:
145 | It's a library to visualize realize fps and timing.
146 | https://github.com/mrdoob/stats.js/ 147 | * dat.gui.js:
148 | A lightweight graphical user interface for changing variables in JavaScript.
149 | https://code.google.com/p/dat-gui/ 150 | * gl-matrix.js:
151 | Javascript Matrix and Vector library for High Performance WebGL apps.
152 | https://github.com/toji/gl-matrix

153 | 154 | 155 | Install and Build Instructions 156 | ------------------------------------------------------------------------------- 157 | Run well on Windows Chrome and Firefox browser. 158 | 159 | Future Work 160 | ------------------------------------------------------------------------------- 161 | - Phyisically based Subsurface Scattering 162 | - Texturemap, bumpmap 163 | - Make the project more robustic 164 | 165 | -------------------------------------------------------------------------------- /WebGL_Path_Tracer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | WebGL Path Tracing 4 | 5 | 6 | 7 | 25 | 26 | 27 |
28 |

WebGL Path Tracing

29 | Made by Bo Zhang and Ying Li 30 |
31 |
32 | 33 |
34 | 35 |
36 |
37 |
38 |
39 |
40 | 41 | 42 |
43 |
44 |
45 |
46 | 47 | 48 |
49 |
50 |
51 | Path tracing is a method of rendering images faithful to reality. The core algorithm of this WebGL Path Tracer is implemented in GLSL fragment shader. Each ray bounces in the scene around five times.

A Demo is uploaded in YouTube and code is provided in GitHub. 52 |

Features:

53 | 60 |

Interaction:

61 | 68 |
69 |
70 |
71 | 91 | 842 | 856 | 868 | 869 | 870 | 871 | 872 | 873 | 874 | 875 | -------------------------------------------------------------------------------- /js/WebGL_Path_Tracer.js: -------------------------------------------------------------------------------- 1 | window.onload = runGL; 2 | 3 | "use strict"; 4 | 5 | var gl; 6 | var canvas; 7 | var message; 8 | 9 | var shaderProgram; 10 | var angleX = 0; 11 | var angleY = 0; 12 | var zoomZ = 15.5; 13 | 14 | var eye = { x: 0.0, y: 0.0, z: 0.0 }; 15 | var center = { x: 0.0, y: 0.0, z: 0.0 }; 16 | var up = { x: 0.0, y: 1.0, z: 0.0 }; 17 | var FOVY = 45.0; 18 | 19 | eye.x = zoomZ * Math.sin(angleY) * Math.cos(angleX); 20 | eye.y = zoomZ * Math.sin(angleX); 21 | eye.z = zoomZ * Math.cos(angleY) * Math.cos(angleX); 22 | 23 | //Texture 24 | var textures; 25 | var objattrtex; 26 | 27 | //Vertex Shader 28 | var VertexLocation; 29 | var u_veyeLocation; 30 | var u_vInvMPLocation; 31 | 32 | //Fragment Shader 33 | var u_numsLocation; 34 | var u_eyeLocation; 35 | var u_timeLocation; 36 | var u_itrLocation; 37 | var u_textureLocation; 38 | var u_attrtextureLocation; 39 | var u_texsizeLocation; 40 | var u_attrtexsizeLocation; 41 | var u_SSAALocation; 42 | var u_texLocations = []; 43 | 44 | //Added for attrtexture 45 | //width and height must be pow(2,n) 46 | var attw = 1024; //width 47 | var atth = 2; //height 48 | var attributes = new Uint8Array(attw * atth * 4); 49 | //bool for SSAA 50 | var SSAA = 0; 51 | 52 | //render shader 53 | var renderProgram; 54 | var renderVertexAttribute; 55 | var vertexPositionBuffer; 56 | var frameBuffer; 57 | var u_textureLocationc; 58 | 59 | var time = 0; 60 | var iterations = 0; 61 | 62 | var Datas = []; 63 | var DefaultDatas = []; 64 | var defaultSize = 6; 65 | 66 | //Added 67 | var stats = initStats(); 68 | 69 | /////////////////////////////////////////////////////////////////////////// 70 | 71 | function runGL() { 72 | var begin = Date.now(); 73 | initGL(); 74 | var end = Date.now(); 75 | document.getElementById("time").innerHTML += "Initialize WebGL: " + (end-begin).toString() + " ms
"; 76 | 77 | begin = end; 78 | initializeShader(); 79 | initBuffers(); 80 | 81 | end = Date.now(); 82 | document.getElementById("time").innerHTML += "Initialize Shader: " + (end-begin).toString() + " ms
"; 83 | 84 | initGUI(); 85 | 86 | begin = end; 87 | initDfaultScene(); 88 | end = Date.now(); 89 | document.getElementById("time").innerHTML += "Load Scene: " + (end-begin).toString() + " ms"; 90 | 91 | animate(); 92 | 93 | //register 94 | canvas.onmousedown = handleMouseDown; 95 | canvas.oncontextmenu = function (ev) { return false; }; 96 | document.onmouseup = handleMouseUp; 97 | document.onmousemove = handleMouseMove; 98 | document.onkeydown = handleKeyDown; 99 | } 100 | 101 | /////////////////////////////////////////////////////////////////////////// 102 | 103 | function initGL(){ 104 | message = document.getElementById("message"); 105 | canvas = document.getElementById("canvas"); 106 | gl = createWebGLContext(canvas, message); 107 | 108 | if (!gl) { 109 | alert("Could not initialise WebGL, sorry :-("); 110 | return; 111 | } 112 | gl.viewport(0, 0, canvas.width, canvas.height); 113 | gl.clearColor(0.0, 0.0, 0.0, 1.0); 114 | gl.enable(gl.DEPTH_TEST); 115 | } 116 | 117 | function initBuffers() { 118 | vertexPositionBuffer = gl.createBuffer(); 119 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); 120 | var vertices = [ 121 | 1.0, 1.0, 122 | -1.0, 1.0, 123 | 1.0, -1.0, 124 | -1.0, -1.0, 125 | ]; 126 | 127 | gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); 128 | gl.vertexAttribPointer(VertexLocation, 2, gl.FLOAT, false, 0, 0); 129 | 130 | 131 | frameBuffer = gl.createFramebuffer(); 132 | var type = gl.getExtension('OES_texture_float') ? gl.FLOAT : gl.UNSIGNED_BYTE; 133 | 134 | textures = []; 135 | for (var i = 0; i < 2; i++) { 136 | textures.push(gl.createTexture()); 137 | gl.bindTexture(gl.TEXTURE_2D, textures[i]); 138 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 139 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 140 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, type, null); 141 | } 142 | gl.bindTexture(gl.TEXTURE_2D, null); 143 | 144 | objattrtex = gl.createTexture(); 145 | gl.bindTexture(gl.TEXTURE_2D, objattrtex); 146 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 147 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 148 | gl.bindTexture(gl.TEXTURE_2D, null); 149 | } 150 | 151 | 152 | function initializeShader() { 153 | //create render shader 154 | var renderVs = getShaderSource(document.getElementById("vs_render")); 155 | var renderFs = getShaderSource(document.getElementById("fs_render")); 156 | 157 | renderProgram = createProgram(gl, renderVs, renderFs, message); 158 | renderVertexAttribute = gl.getAttribLocation(renderProgram, 'aVertex'); 159 | gl.enableVertexAttribArray(renderVertexAttribute); 160 | 161 | u_textureLocationc = gl.getUniformLocation(renderProgram, "texture"); 162 | 163 | //create path tracer shader 164 | var vs = getShaderSource(document.getElementById("vs_pathTracer")); 165 | var fs = getShaderSource(document.getElementById("fs_pathTracer")); 166 | 167 | shaderProgram = createProgram(gl, vs, fs, message); 168 | 169 | //Vertex Shader 170 | VertexLocation = gl.getAttribLocation(shaderProgram, "aVertex"); 171 | gl.enableVertexAttribArray(VertexLocation); 172 | 173 | u_veyeLocation = gl.getUniformLocation(shaderProgram, "vcameraPos"); 174 | u_vInvMPLocation = gl.getUniformLocation(shaderProgram, "u_vInvMP"); 175 | 176 | //Fragment Shader 177 | u_timeLocation = gl.getUniformLocation(shaderProgram, "time"); 178 | u_itrLocation = gl.getUniformLocation(shaderProgram, "u_iterations"); 179 | //Don't k why this line doesn't work 180 | u_numsLocation = gl.getUniformLocation(shaderProgram, "objnums"); 181 | u_eyeLocation = gl.getUniformLocation(shaderProgram, "cameraPos"); 182 | 183 | 184 | u_textureLocation = gl.getUniformLocation(shaderProgram, "texture"); 185 | u_attrtextureLocation = gl.getUniformLocation(shaderProgram, "attrtexture"); 186 | u_texsizeLocation = gl.getUniformLocation(shaderProgram, "texsize"); 187 | u_attrtexsizeLocation = gl.getUniformLocation(shaderProgram, "attrtexsize"); 188 | u_SSAALocation = gl.getUniformLocation(shaderProgram, "SSAA"); 189 | } 190 | 191 | function animate() { 192 | 193 | if (stats) 194 | stats.update(); 195 | 196 | message.innerHTML = "Iterations: " + (iterations).toString(); 197 | 198 | if (!pause || iterations == 0) 199 | { 200 | /////////////////////////////////////////////////////////////////////////// 201 | // Render 202 | gl.useProgram(shaderProgram); 203 | 204 | gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); 205 | 206 | var modelview = mat4.create(); 207 | mat4.lookAt([eye.x, eye.y, eye.z], [center.x, center.y, center.z], [up.x, up.y, up.z], modelview); 208 | 209 | var projection = mat4.create(); 210 | mat4.perspective(FOVY, canvas.width / canvas.height, 0.1, 100.0, projection); 211 | 212 | var modelviewprojection = mat4.create(); 213 | mat4.multiply(projection, modelview, modelviewprojection); 214 | 215 | var inversemp = mat4.create(); 216 | mat4.inverse(modelviewprojection, inversemp); 217 | 218 | gl.uniformMatrix4fv(u_vInvMPLocation, false, inversemp); 219 | gl.uniform3f(u_veyeLocation, eye.x, eye.y, eye.z); 220 | gl.uniform3f(u_eyeLocation, eye.x, eye.y, eye.z); 221 | gl.uniform1f(u_timeLocation, time); 222 | gl.uniform1f(u_itrLocation, iterations); 223 | gl.uniform1i(u_numsLocation, Datas.length); 224 | gl.uniform1i(u_SSAALocation, SSAA); 225 | //Added for texture size 226 | gl.uniform2f(u_texsizeLocation, canvas.width,canvas.height); 227 | gl.uniform2f(u_attrtexsizeLocation, attw, atth); 228 | 229 | gl.activeTexture(gl.TEXTURE0); 230 | gl.bindTexture(gl.TEXTURE_2D, textures[0]); 231 | gl.uniform1i(u_textureLocation, 0); 232 | 233 | 234 | gl.activeTexture(gl.TEXTURE1); //attributes for objects 235 | gl.bindTexture(gl.TEXTURE_2D, objattrtex); 236 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, attw, atth, 0, gl.RGBA, gl.UNSIGNED_BYTE, attributes); 237 | gl.uniform1i(u_attrtextureLocation, 1); 238 | 239 | 240 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); 241 | gl.bindFramebuffer(gl.FRAMEBUFFER, frameBuffer); 242 | gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); 243 | gl.vertexAttribPointer(VertexLocation, 2, gl.FLOAT, false, 0, 0); 244 | 245 | 246 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 247 | gl.bindFramebuffer(gl.FRAMEBUFFER, null); 248 | 249 | textures.reverse(); 250 | 251 | gl.useProgram(renderProgram); 252 | gl.activeTexture(gl.TEXTURE0); 253 | gl.bindTexture(gl.TEXTURE_2D, textures[0]); 254 | gl.uniform1i(u_textureLocationc, 0); 255 | 256 | gl.bindBuffer(gl.ARRAY_BUFFER, vertexPositionBuffer); 257 | gl.vertexAttribPointer(renderVertexAttribute, 2, gl.FLOAT, false, 0, 0); 258 | gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); 259 | 260 | iterations++; 261 | time += 1.0; 262 | 263 | } 264 | 265 | window.requestAnimFrame(animate); 266 | } 267 | 268 | /////////////////////////////////////////////////////////////////////////// 269 | 270 | 271 | function AddObjsAttr(i) { 272 | gl.useProgram(shaderProgram); 273 | //color:No need for map 274 | attributes[28 * i + 0] = 255.0 * Datas[i].obj_color[0]; attributes[28 * i + 1] = 255.0 * Datas[i].obj_color[1]; attributes[28 * i + 2] = 255.0 * Datas[i].obj_color[2]; attributes[28 * i + 3] = 255.0; 275 | //objtype:[0.0,5.0] to [0,255] texturetype:[0.0,5.0] to [0,255] 276 | attributes[28 * i + 4] = 255.0 * Datas[i].obj_type / 5.0; attributes[28 * i + 5] = 255.0 * Datas[i].obj_textureType / 5.0; attributes[28 * i + 6] = 255.0; attributes[28 * i + 7] = 255.0; 277 | //mat1:No need for map 278 | attributes[28 * i + 8] = 255.0 * Datas[i].obj_reflective; attributes[28 * i + 9] = 255.0 * Datas[i].obj_refractive; attributes[28 * i + 10] = 255.0 * Datas[i].obj_reflectivity; attributes[28 * i + 11] = 255.0; 279 | //mat2:IOR[0,3] to [0,255] emittance [0,25] to [0,255] 280 | attributes[28 * i + 12] = 255.0/3.0 * Datas[i].obj_indexOfRefraction; attributes[28 * i + 13] = 255.0 * Datas[i].obj_subsurfaceScatter; attributes[28 * i + 14] = 255.0 * Datas[i].obj_emittance/25.0; attributes[28 * i + 15] = 255.0; 281 | //pos:[-10.0,10.0] to [0,255] 282 | var mind = -10.0; 283 | var maxd = 10.0; 284 | attributes[28 * i + 16] = 255.0 * (Datas[i].obj_pos[0] - mind) / (maxd - mind); attributes[28 * i + 17] = 255.0 * (Datas[i].obj_pos[1] - mind) / (maxd - mind); 285 | attributes[28 * i + 18] = 255.0 * (Datas[i].obj_pos[2] - mind) / (maxd - mind); attributes[28 * i + 19] = 255.0; 286 | //rot:[0.0,360.0] to [0,255] 287 | attributes[28 * i + 20] = 255.0 * Datas[i].obj_rotation[0] / 360.0; attributes[28 * i + 21] = 255.0 * Datas[i].obj_rotation[1] / 360.0; attributes[28 * i + 22] = 255.0 * Datas[i].obj_rotation[2]/360.0; attributes[28 * i + 23] = 255.0; 288 | //scale:[0.0,10.0] to [0,255] 289 | attributes[28 * i + 24] = 255.0 * Datas[i].obj_scale[0] / 10.0; attributes[28 * i + 25] = 255.0 * Datas[i].obj_scale[1] / 10.0; attributes[28 * i + 26] = 255.0 * Datas[i].obj_scale[2] / 10.0; attributes[28 * i + 27] = 255.0; 290 | 291 | } 292 | 293 | var cubeNum = 0; 294 | function addCube() { 295 | if (Datas.length == 31) 296 | return; 297 | Datas.push({ 298 | obj_pos: [Math.random()*10-5, Math.random()*10-5, Math.random()*10-5], 299 | obj_scale: [1.0, 1.0, 1.0], 300 | obj_rotation: [Math.random()*360, Math.random()*360, Math.random()*360], 301 | obj_color: [Math.random(), Math.random(), Math.random()], 302 | obj_type: 2, 303 | obj_textureType: 0, 304 | obj_reflective: 0, 305 | obj_refractive: 0, 306 | obj_reflectivity: 1.0, 307 | obj_indexOfRefraction: 1.0, 308 | obj_emittance: 0, 309 | obj_subsurfaceScatter: 0 310 | }); 311 | 312 | AddObjsAttr(Datas.length - 1); 313 | 314 | GUIAddObj("Cube " + ++cubeNum, Datas.length - 1); 315 | 316 | iterations = 0; 317 | } 318 | 319 | var sphereNum = 3; 320 | 321 | function addSphere() { 322 | if (Datas.length == 31) 323 | return; 324 | Datas.push({ 325 | obj_pos: [Math.random()*10-5, Math.random()*10-5, Math.random()*10-5], 326 | obj_scale: [1.0, 1.0, 1.0], 327 | obj_rotation: [Math.random()*360, Math.random()*360, Math.random()*360], 328 | obj_color: [Math.random(), Math.random(), Math.random()], 329 | obj_type: 0, 330 | obj_textureType: 0, 331 | obj_reflective: 0, 332 | obj_refractive: 0, 333 | obj_reflectivity: 1.0, 334 | obj_indexOfRefraction: 1.0, 335 | obj_emittance: 0, 336 | obj_subsurfaceScatter: 0 337 | }); 338 | 339 | AddObjsAttr(Datas.length - 1); 340 | 341 | GUIAddObj("Sphere " + ++sphereNum, Datas.length - 1); 342 | 343 | iterations = 0; 344 | } 345 | 346 | function initDfaultScene() { 347 | 348 | //Light For Subsurface Scattering,only one light and always at first 349 | DefaultDatas.push({ 350 | obj_pos: [0.0, 4.95, 0.0], 351 | obj_scale: [3.8, 0.1, 3.8], 352 | obj_rotation: [0.0, 0.0, 0.0], 353 | obj_color: [1.0, 1.0, 1.0], 354 | obj_type: 2, 355 | obj_textureType: 0, 356 | obj_reflective: 0, 357 | obj_refractive: 0, 358 | obj_reflectivity: 1.0, 359 | obj_indexOfRefraction: 1.0, 360 | obj_emittance: 25, 361 | obj_subsurfaceScatter: 0 362 | }); 363 | 364 | //Walls 365 | var WallScale = 10.0; 366 | var WallTrans = 5.0; 367 | 368 | DefaultDatas.push({ 369 | obj_pos: [0.0, 0.0, -WallTrans+0.1], 370 | obj_scale: [WallScale, 1.0, WallScale], 371 | obj_rotation: [91.0, 0.0, 0.0], 372 | obj_color: [1.0, 1.0, 1.0], 373 | obj_type: 1, 374 | obj_textureType: 0, 375 | obj_reflective: 0, 376 | obj_refractive: 0, 377 | obj_reflectivity: 1.0, 378 | obj_indexOfRefraction: 1.0, 379 | obj_emittance: 0, 380 | obj_subsurfaceScatter: 0 381 | }); 382 | 383 | DefaultDatas.push({ 384 | obj_pos: [-WallTrans+0.1, 0.0, 0.0], 385 | obj_scale: [ WallScale,1.0, WallScale], 386 | obj_rotation: [0.0, 0.0, 271.0], 387 | obj_color: [0.75, 0.25, 0.25], 388 | obj_type: 1, 389 | obj_textureType: 0, 390 | obj_reflective: 0, 391 | obj_refractive: 0, 392 | obj_reflectivity: 1.0, 393 | obj_indexOfRefraction: 1.0, 394 | obj_emittance: 0, 395 | obj_subsurfaceScatter: 0 396 | }); 397 | 398 | DefaultDatas.push({ 399 | obj_pos: [WallTrans-0.1, 0.0, 0.0], 400 | obj_scale: [WallScale,1.0, WallScale], 401 | obj_rotation: [0.0, 0.0, 91.0], 402 | obj_color: [0.25, 0.25, 0.75], 403 | obj_type: 1, 404 | obj_textureType: 0, 405 | obj_reflective: 0, 406 | obj_refractive: 0, 407 | obj_reflectivity: 1.0, 408 | obj_indexOfRefraction: 1.0, 409 | obj_emittance: 0, 410 | obj_subsurfaceScatter: 0 411 | }); 412 | 413 | DefaultDatas.push({ 414 | obj_pos: [0.0, WallTrans, 0.0], 415 | obj_scale: [WallScale, 1.0, WallScale], 416 | obj_rotation: [180.0, 0.0, 0.0], 417 | obj_color: [0.75, 0.75, 0.75], 418 | obj_type: 1, 419 | obj_textureType: 0, 420 | obj_reflective: 0, 421 | obj_refractive: 0, 422 | obj_reflectivity: 1.0, 423 | obj_indexOfRefraction: 1.0, 424 | obj_emittance: 0, 425 | obj_subsurfaceScatter: 0 426 | }); 427 | 428 | DefaultDatas.push({ 429 | obj_pos: [0.0, -WallTrans, 0.0], 430 | obj_scale: [WallScale, 1.0, WallScale], 431 | obj_rotation: [0.0, 0.0, 0.0], 432 | obj_color: [0.75, 0.75, 0.75], 433 | obj_type: 1, 434 | obj_textureType: 0, 435 | obj_reflective: 0, 436 | obj_refractive: 0, 437 | obj_reflectivity: 1.0, 438 | obj_indexOfRefraction: 1.0, 439 | obj_emittance: 0, 440 | obj_subsurfaceScatter: 0 441 | }); 442 | 443 | 444 | //Sphere1 445 | DefaultDatas.push({ 446 | obj_pos: [-2.0, 0.0, 0.0], 447 | obj_scale: [1.8, 1.8, 1.8], 448 | obj_rotation: [30.0, 4.0, 0.0], 449 | obj_color: [1.0, 0.0, 0.0], 450 | obj_type: 0, 451 | obj_textureType: 0, 452 | obj_reflective: 0, 453 | obj_refractive: 0, 454 | obj_reflectivity: 1.0, 455 | obj_indexOfRefraction: 1.0, 456 | obj_emittance: 0, 457 | obj_subsurfaceScatter: 0 458 | }); 459 | 460 | //Sphere2 461 | DefaultDatas.push({ 462 | obj_pos: [0.0, 0.0, 0.0], 463 | obj_scale: [1.8, 1.8, 1.8], 464 | obj_rotation: [30.0, 4.0, 0.0], 465 | obj_color: [0.95, 0.5, 0.4], 466 | obj_type: 0, 467 | obj_textureType: 0, 468 | obj_reflective: 1, 469 | obj_refractive: 0, 470 | obj_reflectivity: 1.0, 471 | obj_indexOfRefraction: 1.0, 472 | obj_emittance: 0, 473 | obj_subsurfaceScatter: 1 474 | }); 475 | 476 | 477 | //Sphere3 478 | DefaultDatas.push({ 479 | obj_pos: [2.0, 0.0, 0.0], 480 | obj_scale: [1.8, 1.8, 1.8], 481 | obj_rotation: [30.0, 4.0, 0.0], 482 | obj_color: [1.0, 1.0, 1.0], 483 | obj_type: 0, 484 | obj_textureType: 0, 485 | obj_reflective: 1, 486 | obj_refractive: 1, 487 | obj_reflectivity: 1.0, 488 | obj_indexOfRefraction: 3.0, 489 | obj_emittance: 0, 490 | obj_subsurfaceScatter: 1 491 | }); 492 | 493 | //Box 494 | DefaultDatas.push({ 495 | obj_pos: [0.0, -1.5, 0.0], 496 | obj_scale: [6.8, 0.2, 4.8], 497 | obj_rotation: [0.0, 0.0, 0.0], 498 | obj_color: [0.8, 0.8, 0.8], 499 | obj_type: 2, 500 | obj_textureType: 0, 501 | obj_reflective: 0, 502 | obj_refractive: 0, 503 | obj_reflectivity: 1.0, 504 | obj_indexOfRefraction: 1.0, 505 | obj_emittance: 0, 506 | obj_subsurfaceScatter: 0 507 | }); 508 | 509 | //Legs 510 | var legpos1 = 3.0, legpos2 = 1.8; 511 | DefaultDatas.push({ 512 | obj_pos: [legpos1, -3.5, legpos2], 513 | obj_scale: [0.3, 4.0, 0.3], 514 | obj_rotation: [0.0, 0.0, 0.0], 515 | obj_color: [0.9, 0.4, 0.0], 516 | obj_type: 2, 517 | obj_textureType: 0, 518 | obj_reflective: 0, 519 | obj_refractive: 0, 520 | obj_reflectivity: 1.0, 521 | obj_indexOfRefraction: 1.0, 522 | obj_emittance: 0, 523 | obj_subsurfaceScatter: 0 524 | }); 525 | 526 | DefaultDatas.push({ 527 | obj_pos: [-legpos1, -3.5, legpos2], 528 | obj_scale: [0.3, 4.0, 0.3], 529 | obj_rotation: [0.0, 0.0, 0.0], 530 | obj_color: [0.9, 0.4, 0.0], 531 | obj_type: 2, 532 | obj_textureType: 0, 533 | obj_reflective: 0, 534 | obj_refractive: 0, 535 | obj_reflectivity: 1.0, 536 | obj_indexOfRefraction: 1.0, 537 | obj_emittance: 0, 538 | obj_subsurfaceScatter: 0 539 | }); 540 | 541 | DefaultDatas.push({ 542 | obj_pos: [legpos1, -3.5, -legpos2], 543 | obj_scale: [0.3, 4.0, 0.3], 544 | obj_rotation: [0.0, 0.0, 0.0], 545 | obj_color: [0.9, 0.4, 0.0], 546 | obj_type: 2, 547 | obj_textureType: 0, 548 | obj_reflective: 0, 549 | obj_refractive: 0, 550 | obj_reflectivity: 1.0, 551 | obj_indexOfRefraction: 1.0, 552 | obj_emittance: 0, 553 | obj_subsurfaceScatter: 0 554 | }); 555 | 556 | DefaultDatas.push({ 557 | obj_pos: [-legpos1, -3.5, -legpos2], 558 | obj_scale: [0.3, 4.0, 0.3], 559 | obj_rotation: [0.0, 0.0, 0.0], 560 | obj_color: [0.9, 0.4, 0.0], 561 | obj_type: 2, 562 | obj_textureType: 0, 563 | obj_reflective: 0, 564 | obj_refractive: 0, 565 | obj_reflectivity: 1.0, 566 | obj_indexOfRefraction: 1.0, 567 | obj_emittance: 0, 568 | obj_subsurfaceScatter: 0 569 | }); 570 | defaultScene(); 571 | } 572 | 573 | function defaultScene() { 574 | Datas.length = 0; 575 | 576 | for (var i = 0; i < DefaultDatas.length; i++) { 577 | Datas[i] = DefaultDatas[i]; 578 | AddObjsAttr(i); 579 | } 580 | 581 | iterations = 0; 582 | 583 | 584 | var node = document.getElementById("gui2"); 585 | if (node != null) 586 | node.parentNode.removeChild(node); 587 | 588 | GUIDefaultScene(); 589 | } 590 | 591 | function resize() { 592 | canvas.width = width; 593 | canvas.height = height; 594 | 595 | gl.viewport(0, 0, canvas.width, canvas.height); 596 | 597 | var type = gl.getExtension('OES_texture_float') ? gl.FLOAT : gl.UNSIGNED_BYTE; 598 | 599 | gl.activeTexture(gl.TEXTURE0); 600 | gl.bindTexture(gl.TEXTURE_2D, textures[0]); 601 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 602 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 603 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 604 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 605 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, type, null); 606 | 607 | gl.bindTexture(gl.TEXTURE_2D, textures[1]); 608 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); 609 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); 610 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); 611 | gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); 612 | gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, canvas.width, canvas.height, 0, gl.RGB, type, null); 613 | 614 | gl.bindTexture(gl.TEXTURE_2D, null); 615 | 616 | iterations = 0; 617 | } 618 | 619 | 620 | 621 | ///////////////////////////////////////////////////////////////////////////// 622 | 623 | /*************************** performance ***********************************/ 624 | function initStats() { 625 | stats = new Stats(); 626 | stats.setMode(0); // 0: fps, 1: ms 627 | 628 | // Align top-left 629 | stats.domElement.style.position = 'absolute'; 630 | stats.domElement.style.left ='200px'; 631 | stats.domElement.style.top = '0px'; 632 | 633 | document.body.appendChild(stats.domElement); 634 | 635 | 636 | return stats; 637 | } 638 | 639 | /////////////////////////////////////////////////////////////////////////// 640 | 641 | /************************* interaction ********************************/ 642 | var mouseLeftDown = false; 643 | var mouseRightDown = false; 644 | var mouseMidDown = false; 645 | var lastMouseX = null; 646 | var lastMouseY = null; 647 | 648 | var pause = false; 649 | 650 | function handleMouseDown(event) { 651 | if (event.button == 2) { 652 | mouseLeftDown = false; 653 | mouseRightDown = true; 654 | mouseMidDown = false; 655 | } 656 | else if (event.button == 0) { 657 | mouseLeftDown = true; 658 | mouseRightDown = false; 659 | mouseMidDown = false; 660 | } 661 | else if (event.button == 1) { 662 | mouseLeftDown = false; 663 | mouseRightDown = false; 664 | mouseMidDown = true; 665 | } 666 | lastMouseX = event.clientX; 667 | lastMouseY = event.clientY; 668 | } 669 | 670 | function handleMouseUp(event) { 671 | mouseLeftDown = false; 672 | mouseRightDown = false; 673 | mouseMidDown = false; 674 | } 675 | 676 | function handleMouseMove(event) { 677 | if (!(mouseLeftDown || mouseRightDown || mouseMidDown)) { 678 | return; 679 | } 680 | var newX = event.clientX; 681 | var newY = event.clientY; 682 | 683 | var deltaX = newX - lastMouseX; 684 | var deltaY = newY - lastMouseY; 685 | 686 | if (mouseLeftDown) { 687 | // update the angles based on how far we moved since last time 688 | angleY -= deltaX * 0.01; 689 | angleX += deltaY * 0.01; 690 | 691 | // don't go upside down 692 | angleX = Math.max(angleX, -Math.PI / 2 + 0.01); 693 | angleX = Math.min(angleX, Math.PI / 2 - 0.01); 694 | 695 | eye.x = zoomZ * Math.sin(angleY) * Math.cos(angleX); 696 | eye.y = zoomZ * Math.sin(angleX); 697 | eye.z = zoomZ * Math.cos(angleY) * Math.cos(angleX); 698 | } 699 | else if (mouseRightDown) { 700 | zoomZ += 0.01 * deltaY; 701 | zoomZ = Math.min(Math.max(zoomZ, 4.0), 20.0); 702 | 703 | eye.x = zoomZ * Math.sin(angleY) * Math.cos(angleX); 704 | eye.y = zoomZ * Math.sin(angleX); 705 | eye.z = zoomZ * Math.cos(angleY) * Math.cos(angleX); 706 | } 707 | else if (mouseMidDown) { 708 | center.x -= 0.01 * deltaX; 709 | center.y += 0.01 * deltaY; 710 | eye.x -= 0.01 * deltaX; 711 | eye.y += 0.01 * deltaY; 712 | } 713 | 714 | lastMouseX = newX; 715 | lastMouseY = newY; 716 | 717 | iterations = 0; 718 | } 719 | 720 | function handleKeyDown(event){ 721 | if (event.keyCode == 32) 722 | pause = !pause; 723 | } 724 | 725 | var toHide = true; 726 | function toggleContorller(){ 727 | if (toHide) 728 | { 729 | document.getElementById("icon").style.background = 'url("left-arrow.png")'; 730 | document.getElementById("gui-left").style.display = "none"; 731 | document.getElementById("gui-right").style.display = "none"; 732 | } 733 | else 734 | { 735 | document.getElementById("icon").style.background = 'url("right-arrow.png")'; 736 | document.getElementById("gui-left").style.display = "block"; 737 | document.getElementById("gui-right").style.display = "block"; 738 | } 739 | toHide = !toHide; 740 | } 741 | 742 | 743 | ///////////////////////////////////////////////////////////////////////// 744 | /*******************************GUI*************************************/ 745 | //gui 746 | var gui1; 747 | var guiConfig; 748 | 749 | var gui2; 750 | var guiObjs = []; 751 | 752 | var width; 753 | var height; 754 | 755 | function initGUI() { 756 | width = canvas.width; 757 | height = canvas.height; 758 | 759 | //gui 760 | gui1 = new dat.GUI({ autoPlace: false }); 761 | var container = document.getElementById('gui-right'); 762 | container.appendChild(gui1.domElement); 763 | 764 | guiConfig = new GUIConfig(); 765 | 766 | gui1.add(guiConfig, 'width').onChange(function () { 767 | width = guiConfig.width; 768 | }); 769 | gui1.add(guiConfig, 'height').onChange(function () { 770 | height = guiConfig.height; 771 | }); 772 | 773 | gui1.add(guiConfig, 'antiAliasing').onChange(function () { 774 | SSAA = (guiConfig.antiAliasing == true) ? 1 : 0; 775 | iterations = 0; 776 | }); 777 | } 778 | 779 | function GUIConfig() { 780 | this.width = width; 781 | this.height = height; 782 | 783 | this.antiAliasing = (SSAA == 1) ? true : false; 784 | } 785 | 786 | function GUIDefaultScene(){ 787 | gui2 = new dat.GUI({ autoPlace: false }); 788 | gui2.domElement.id = 'gui2'; 789 | var container = document.getElementById('gui-left'); 790 | container.appendChild(gui2.domElement); 791 | 792 | GUIAddObj("Light", 0); 793 | GUIAddObj("Sphere 1", defaultSize); 794 | GUIAddObj("Sphere 2", defaultSize+1); 795 | GUIAddObj("Sphere 3", defaultSize+2); 796 | } 797 | 798 | function GUIObj(id) { 799 | this.translateX = Datas[id].obj_pos[0]; 800 | this.translateY = Datas[id].obj_pos[1]; 801 | this.translateZ = Datas[id].obj_pos[2]; 802 | this.scaleX = Datas[id].obj_scale[0]; 803 | this.scaleY = Datas[id].obj_scale[1]; 804 | this.scaleZ = Datas[id].obj_scale[2]; 805 | this.rotateX = Datas[id].obj_rotation[0]; 806 | this.rotateY = Datas[id].obj_rotation[1]; 807 | this.rotateZ = Datas[id].obj_rotation[2]; 808 | this.color = [Datas[id].obj_color[0] * 255.0, Datas[id].obj_color[1] * 255.0, Datas[id].obj_color[2] * 255.0]; 809 | this.reflect = (Datas[id].obj_reflective == 1) ? true : false ; 810 | this.refract = (Datas[id].obj_refractive == 1) ? true : false ; 811 | this.IOR = Datas[id].obj_indexOfRefraction; 812 | this.emittance = Datas[id].obj_emittance; 813 | this.subsurfaceScatter = (Datas[id].obj_subsurfaceScatter == 1) ? true : false ; 814 | }; 815 | 816 | function GUIAddObj(name, id) { 817 | var i = guiObjs.length; 818 | if (Datas.length == 31) 819 | return; 820 | 821 | guiObjs.push( new GUIObj(id)); 822 | 823 | var folder = gui2.addFolder(name); 824 | 825 | folder.add(guiObjs[i], 'translateX').min(-5).max(5).onChange(function () { 826 | Datas[id].obj_pos[0] = guiObjs[i].translateX; 827 | AddObjsAttr(id); 828 | iterations = 0; 829 | }); 830 | folder.add(guiObjs[i], 'translateY').min(-5).max(5).onChange(function () { 831 | Datas[id].obj_pos[1] = guiObjs[i].translateY; 832 | AddObjsAttr(id); 833 | iterations = 0; 834 | }); 835 | folder.add(guiObjs[i], 'translateZ').min(-5).max(5).onChange(function () { 836 | Datas[id].obj_pos[2] = guiObjs[i].translateZ; 837 | AddObjsAttr(id); 838 | iterations = 0; 839 | }); 840 | folder.add(guiObjs[i], 'scaleX').onChange(function () { 841 | Datas[id].obj_scale[0] = guiObjs[i].scaleX; 842 | AddObjsAttr(id); 843 | iterations = 0; 844 | }); 845 | folder.add(guiObjs[i], 'scaleY').onChange(function () { 846 | Datas[id].obj_scale[1] = guiObjs[i].scaleY; 847 | AddObjsAttr(id); 848 | iterations = 0; 849 | }); 850 | folder.add(guiObjs[i], 'scaleZ').onChange(function () { 851 | Datas[id].obj_scale[2] = guiObjs[i].scaleZ; 852 | AddObjsAttr(id); 853 | iterations = 0; 854 | }); 855 | folder.add(guiObjs[i], 'rotateX').onChange(function () { 856 | Datas[id].obj_rotation[0] = guiObjs[i].rotateX; 857 | AddObjsAttr(id); 858 | iterations = 0; 859 | }); 860 | folder.add(guiObjs[i], 'rotateY').onChange(function () { 861 | Datas[id].obj_rotation[1] = guiObjs[i].rotateY; 862 | AddObjsAttr(id); 863 | iterations = 0; 864 | }); 865 | folder.add(guiObjs[i], 'rotateZ').onChange(function () { 866 | Datas[id].obj_rotation[2] = guiObjs[i].rotateZ; 867 | AddObjsAttr(id); 868 | iterations = 0; 869 | }); 870 | folder.addColor(guiObjs[i], 'color').onChange(function () { 871 | Datas[id].obj_color = [guiObjs[i].color[0] / 255.0, guiObjs[i].color[1] / 255.0, guiObjs[i].color[2] / 255.0]; 872 | AddObjsAttr(id); 873 | iterations = 0; 874 | }); 875 | folder.add(guiObjs[i], 'reflect').onChange(function () { 876 | Datas[id].obj_reflective = guiObjs[i].reflect; 877 | AddObjsAttr(id); 878 | iterations = 0; 879 | }); 880 | folder.add(guiObjs[i], 'refract').onChange(function () { 881 | Datas[id].obj_refractive = guiObjs[i].refract; 882 | AddObjsAttr(id); 883 | iterations = 0; 884 | }); 885 | folder.add(guiObjs[i], 'IOR').min(1).onChange(function () { 886 | Datas[id].obj_indexOfRefraction = guiObjs[i].IOR; 887 | AddObjsAttr(id); 888 | iterations = 0; 889 | }); 890 | folder.add(guiObjs[i], 'emittance').onChange(function () { 891 | Datas[id].obj_emittance = guiObjs[i].emittance; 892 | AddObjsAttr(id); 893 | iterations = 0; 894 | }); 895 | folder.add(guiObjs[i], 'subsurfaceScatter').onChange(function () { 896 | Datas[id].obj_subsurfaceScatter = guiObjs[i].subsurfaceScatter; 897 | AddObjsAttr(id); 898 | iterations = 0; 899 | }); 900 | } -------------------------------------------------------------------------------- /js/lib/dat.gui.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * dat-gui JavaScript Controller Library 3 | * http://code.google.com/p/dat-gui 4 | * 5 | * Copyright 2011 Data Arts Team, Google Creative Lab 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | */ 13 | var dat=dat||{};dat.gui=dat.gui||{};dat.utils=dat.utils||{};dat.controllers=dat.controllers||{};dat.dom=dat.dom||{};dat.color=dat.color||{};dat.utils.css=function(){return{load:function(e,a){var a=a||document,c=a.createElement("link");c.type="text/css";c.rel="stylesheet";c.href=e;a.getElementsByTagName("head")[0].appendChild(c)},inject:function(e,a){var a=a||document,c=document.createElement("style");c.type="text/css";c.innerHTML=e;a.getElementsByTagName("head")[0].appendChild(c)}}}(); 14 | dat.utils.common=function(){var e=Array.prototype.forEach,a=Array.prototype.slice;return{BREAK:{},extend:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(a[f])||(c[f]=a[f])},this);return c},defaults:function(c){this.each(a.call(arguments,1),function(a){for(var f in a)this.isUndefined(c[f])&&(c[f]=a[f])},this);return c},compose:function(){var c=a.call(arguments);return function(){for(var d=a.call(arguments),f=c.length-1;f>=0;f--)d=[c[f].apply(this,d)];return d[0]}}, 15 | each:function(a,d,f){if(e&&a.forEach===e)a.forEach(d,f);else if(a.length===a.length+0)for(var b=0,n=a.length;b-1?d.length-d.indexOf(".")-1:0};c.superclass=e;a.extend(c.prototype,e.prototype,{setValue:function(a){if(this.__min!==void 0&&athis.__max)a=this.__max;this.__step!==void 0&&a%this.__step!=0&&(a=Math.round(a/this.__step)*this.__step);return c.superclass.prototype.setValue.call(this,a)},min:function(a){this.__min=a;return this},max:function(a){this.__max=a;return this},step:function(a){this.__step=a;return this}});return c}(dat.controllers.Controller,dat.utils.common); 29 | dat.controllers.NumberControllerBox=function(e,a,c){var d=function(f,b,e){function h(){var a=parseFloat(l.__input.value);c.isNaN(a)||l.setValue(a)}function j(a){var b=o-a.clientY;l.setValue(l.getValue()+b*l.__impliedStep);o=a.clientY}function m(){a.unbind(window,"mousemove",j);a.unbind(window,"mouseup",m)}this.__truncationSuspended=false;d.superclass.call(this,f,b,e);var l=this,o;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"change",h); 30 | a.bind(this.__input,"blur",function(){h();l.__onFinishChange&&l.__onFinishChange.call(l,l.getValue())});a.bind(this.__input,"mousedown",function(b){a.bind(window,"mousemove",j);a.bind(window,"mouseup",m);o=b.clientY});a.bind(this.__input,"keydown",function(a){if(a.keyCode===13)l.__truncationSuspended=true,this.blur(),l.__truncationSuspended=false});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype,e.prototype,{updateDisplay:function(){var a=this.__input, 31 | b;if(this.__truncationSuspended)b=this.getValue();else{b=this.getValue();var c=Math.pow(10,this.__precision);b=Math.round(b*c)/c}a.value=b;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.NumberController,dat.dom.dom,dat.utils.common); 32 | dat.controllers.NumberControllerSlider=function(e,a,c,d,f){var b=function(d,c,f,e,l){function o(b){b.preventDefault();var d=a.getOffset(g.__background),c=a.getWidth(g.__background);g.setValue(g.__min+(g.__max-g.__min)*((b.clientX-d.left)/(d.left+c-d.left)));return false}function y(){a.unbind(window,"mousemove",o);a.unbind(window,"mouseup",y);g.__onFinishChange&&g.__onFinishChange.call(g,g.getValue())}b.superclass.call(this,d,c,{min:f,max:e,step:l});var g=this;this.__background=document.createElement("div"); 33 | this.__foreground=document.createElement("div");a.bind(this.__background,"mousedown",function(b){a.bind(window,"mousemove",o);a.bind(window,"mouseup",y);o(b)});a.addClass(this.__background,"slider");a.addClass(this.__foreground,"slider-fg");this.updateDisplay();this.__background.appendChild(this.__foreground);this.domElement.appendChild(this.__background)};b.superclass=e;b.useDefaultStyles=function(){c.inject(f)};d.extend(b.prototype,e.prototype,{updateDisplay:function(){this.__foreground.style.width= 34 | (this.getValue()-this.__min)/(this.__max-this.__min)*100+"%";return b.superclass.prototype.updateDisplay.call(this)}});return b}(dat.controllers.NumberController,dat.dom.dom,dat.utils.css,dat.utils.common,".slider {\n box-shadow: inset 0 2px 4px rgba(0,0,0,0.15);\n height: 1em;\n border-radius: 1em;\n background-color: #eee;\n padding: 0 0.5em;\n overflow: hidden;\n}\n\n.slider-fg {\n padding: 1px 0 2px 0;\n background-color: #aaa;\n height: 1em;\n margin-left: -0.5em;\n padding-right: 0.5em;\n border-radius: 1em 0 0 1em;\n}\n\n.slider-fg:after {\n display: inline-block;\n border-radius: 1em;\n background-color: #fff;\n border: 1px solid #aaa;\n content: '';\n float: right;\n margin-right: -1em;\n margin-top: -1px;\n height: 0.9em;\n width: 0.9em;\n}"); 35 | dat.controllers.FunctionController=function(e,a,c){var d=function(c,b,e){d.superclass.call(this,c,b);var h=this;this.__button=document.createElement("div");this.__button.innerHTML=e===void 0?"Fire":e;a.bind(this.__button,"click",function(a){a.preventDefault();h.fire();return false});a.addClass(this.__button,"button");this.domElement.appendChild(this.__button)};d.superclass=e;c.extend(d.prototype,e.prototype,{fire:function(){this.__onChange&&this.__onChange.call(this);this.__onFinishChange&&this.__onFinishChange.call(this, 36 | this.getValue());this.getValue().call(this.object)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 37 | dat.controllers.BooleanController=function(e,a,c){var d=function(c,b){d.superclass.call(this,c,b);var e=this;this.__prev=this.getValue();this.__checkbox=document.createElement("input");this.__checkbox.setAttribute("type","checkbox");a.bind(this.__checkbox,"change",function(){e.setValue(!e.__prev)},false);this.domElement.appendChild(this.__checkbox);this.updateDisplay()};d.superclass=e;c.extend(d.prototype,e.prototype,{setValue:function(a){a=d.superclass.prototype.setValue.call(this,a);this.__onFinishChange&& 38 | this.__onFinishChange.call(this,this.getValue());this.__prev=this.getValue();return a},updateDisplay:function(){this.getValue()===true?(this.__checkbox.setAttribute("checked","checked"),this.__checkbox.checked=true):this.__checkbox.checked=false;return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common); 39 | dat.color.toString=function(e){return function(a){if(a.a==1||e.isUndefined(a.a)){for(a=a.hex.toString(16);a.length<6;)a="0"+a;return"#"+a}else return"rgba("+Math.round(a.r)+","+Math.round(a.g)+","+Math.round(a.b)+","+a.a+")"}}(dat.utils.common); 40 | dat.color.interpret=function(e,a){var c,d,f=[{litmus:a.isString,conversions:{THREE_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9])([A-F0-9])([A-F0-9])$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString()+a[1].toString()+a[2].toString()+a[2].toString()+a[3].toString()+a[3].toString())}},write:e},SIX_CHAR_HEX:{read:function(a){a=a.match(/^#([A-F0-9]{6})$/i);return a===null?false:{space:"HEX",hex:parseInt("0x"+a[1].toString())}},write:e},CSS_RGB:{read:function(a){a=a.match(/^rgb\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\)/); 41 | return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3])}},write:e},CSS_RGBA:{read:function(a){a=a.match(/^rgba\(\s*(.+)\s*,\s*(.+)\s*,\s*(.+)\s*\,\s*(.+)\s*\)/);return a===null?false:{space:"RGB",r:parseFloat(a[1]),g:parseFloat(a[2]),b:parseFloat(a[3]),a:parseFloat(a[4])}},write:e}}},{litmus:a.isNumber,conversions:{HEX:{read:function(a){return{space:"HEX",hex:a,conversionName:"HEX"}},write:function(a){return a.hex}}}},{litmus:a.isArray,conversions:{RGB_ARRAY:{read:function(a){return a.length!= 42 | 3?false:{space:"RGB",r:a[0],g:a[1],b:a[2]}},write:function(a){return[a.r,a.g,a.b]}},RGBA_ARRAY:{read:function(a){return a.length!=4?false:{space:"RGB",r:a[0],g:a[1],b:a[2],a:a[3]}},write:function(a){return[a.r,a.g,a.b,a.a]}}}},{litmus:a.isObject,conversions:{RGBA_OBJ:{read:function(b){return a.isNumber(b.r)&&a.isNumber(b.g)&&a.isNumber(b.b)&&a.isNumber(b.a)?{space:"RGB",r:b.r,g:b.g,b:b.b,a:b.a}:false},write:function(a){return{r:a.r,g:a.g,b:a.b,a:a.a}}},RGB_OBJ:{read:function(b){return a.isNumber(b.r)&& 43 | a.isNumber(b.g)&&a.isNumber(b.b)?{space:"RGB",r:b.r,g:b.g,b:b.b}:false},write:function(a){return{r:a.r,g:a.g,b:a.b}}},HSVA_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)&&a.isNumber(b.a)?{space:"HSV",h:b.h,s:b.s,v:b.v,a:b.a}:false},write:function(a){return{h:a.h,s:a.s,v:a.v,a:a.a}}},HSV_OBJ:{read:function(b){return a.isNumber(b.h)&&a.isNumber(b.s)&&a.isNumber(b.v)?{space:"HSV",h:b.h,s:b.s,v:b.v}:false},write:function(a){return{h:a.h,s:a.s,v:a.v}}}}}];return function(){d= 44 | false;var b=arguments.length>1?a.toArray(arguments):arguments[0];a.each(f,function(e){if(e.litmus(b))return a.each(e.conversions,function(e,f){c=e.read(b);if(d===false&&c!==false)return d=c,c.conversionName=f,c.conversion=e,a.BREAK}),a.BREAK});return d}}(dat.color.toString,dat.utils.common); 45 | dat.GUI=dat.gui.GUI=function(e,a,c,d,f,b,n,h,j,m,l,o,y,g,i){function q(a,b,r,c){if(b[r]===void 0)throw Error("Object "+b+' has no property "'+r+'"');c.color?b=new l(b,r):(b=[b,r].concat(c.factoryArgs),b=d.apply(a,b));if(c.before instanceof f)c.before=c.before.__li;t(a,b);g.addClass(b.domElement,"c");r=document.createElement("span");g.addClass(r,"property-name");r.innerHTML=b.property;var e=document.createElement("div");e.appendChild(r);e.appendChild(b.domElement);c=s(a,e,c.before);g.addClass(c,k.CLASS_CONTROLLER_ROW); 46 | g.addClass(c,typeof b.getValue());p(a,c,b);a.__controllers.push(b);return b}function s(a,b,d){var c=document.createElement("li");b&&c.appendChild(b);d?a.__ul.insertBefore(c,params.before):a.__ul.appendChild(c);a.onResize();return c}function p(a,d,c){c.__li=d;c.__gui=a;i.extend(c,{options:function(b){if(arguments.length>1)return c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[i.toArray(arguments)]});if(i.isArray(b)||i.isObject(b))return c.remove(),q(a,c.object,c.property, 47 | {before:c.__li.nextElementSibling,factoryArgs:[b]})},name:function(a){c.__li.firstElementChild.firstElementChild.innerHTML=a;return c},listen:function(){c.__gui.listen(c);return c},remove:function(){c.__gui.remove(c);return c}});if(c instanceof j){var e=new h(c.object,c.property,{min:c.__min,max:c.__max,step:c.__step});i.each(["updateDisplay","onChange","onFinishChange"],function(a){var b=c[a],H=e[a];c[a]=e[a]=function(){var a=Array.prototype.slice.call(arguments);b.apply(c,a);return H.apply(e,a)}}); 48 | g.addClass(d,"has-slider");c.domElement.insertBefore(e.domElement,c.domElement.firstElementChild)}else if(c instanceof h){var f=function(b){return i.isNumber(c.__min)&&i.isNumber(c.__max)?(c.remove(),q(a,c.object,c.property,{before:c.__li.nextElementSibling,factoryArgs:[c.__min,c.__max,c.__step]})):b};c.min=i.compose(f,c.min);c.max=i.compose(f,c.max)}else if(c instanceof b)g.bind(d,"click",function(){g.fakeEvent(c.__checkbox,"click")}),g.bind(c.__checkbox,"click",function(a){a.stopPropagation()}); 49 | else if(c instanceof n)g.bind(d,"click",function(){g.fakeEvent(c.__button,"click")}),g.bind(d,"mouseover",function(){g.addClass(c.__button,"hover")}),g.bind(d,"mouseout",function(){g.removeClass(c.__button,"hover")});else if(c instanceof l)g.addClass(d,"color"),c.updateDisplay=i.compose(function(a){d.style.borderLeftColor=c.__color.toString();return a},c.updateDisplay),c.updateDisplay();c.setValue=i.compose(function(b){a.getRoot().__preset_select&&c.isModified()&&B(a.getRoot(),true);return b},c.setValue)} 50 | function t(a,b){var c=a.getRoot(),d=c.__rememberedObjects.indexOf(b.object);if(d!=-1){var e=c.__rememberedObjectIndecesToControllers[d];e===void 0&&(e={},c.__rememberedObjectIndecesToControllers[d]=e);e[b.property]=b;if(c.load&&c.load.remembered){c=c.load.remembered;if(c[a.preset])c=c[a.preset];else if(c[w])c=c[w];else return;if(c[d]&&c[d][b.property]!==void 0)d=c[d][b.property],b.initialValue=d,b.setValue(d)}}}function I(a){var b=a.__save_row=document.createElement("li");g.addClass(a.domElement, 51 | "has-save");a.__ul.insertBefore(b,a.__ul.firstChild);g.addClass(b,"save-row");var c=document.createElement("span");c.innerHTML=" ";g.addClass(c,"button gears");var d=document.createElement("span");d.innerHTML="Save";g.addClass(d,"button");g.addClass(d,"save");var e=document.createElement("span");e.innerHTML="New";g.addClass(e,"button");g.addClass(e,"save-as");var f=document.createElement("span");f.innerHTML="Revert";g.addClass(f,"button");g.addClass(f,"revert");var m=a.__preset_select=document.createElement("select"); 52 | a.load&&a.load.remembered?i.each(a.load.remembered,function(b,c){C(a,c,c==a.preset)}):C(a,w,false);g.bind(m,"change",function(){for(var b=0;b0){a.preset=this.preset;if(!a.remembered)a.remembered={};a.remembered[this.preset]=z(this)}a.folders={};i.each(this.__folders,function(b, 69 | c){a.folders[c]=b.getSaveObject()});return a},save:function(){if(!this.load.remembered)this.load.remembered={};this.load.remembered[this.preset]=z(this);B(this,false)},saveAs:function(a){if(!this.load.remembered)this.load.remembered={},this.load.remembered[w]=z(this,true);this.load.remembered[a]=z(this);this.preset=a;C(this,a,true)},revert:function(a){i.each(this.__controllers,function(b){this.getRoot().load.remembered?t(a||this.getRoot(),b):b.setValue(b.initialValue)},this);i.each(this.__folders, 70 | function(a){a.revert(a)});a||B(this.getRoot(),false)},listen:function(a){var b=this.__listening.length==0;this.__listening.push(a);b&&E(this.__listening)}});return k}(dat.utils.css,'
\n\n Here\'s the new load parameter for your GUI\'s constructor:\n\n \n\n
\n\n Automatically save\n values to localStorage on exit.\n\n
The values saved to localStorage will\n override those passed to dat.GUI\'s constructor. This makes it\n easier to work incrementally, but localStorage is fragile,\n and your friends may not see the same values you do.\n \n
\n \n
\n\n
', 71 | ".dg ul{list-style:none;margin:0;padding:0;width:100%;clear:both}.dg.ac{position:fixed;top:0;left:0;right:0;height:0;z-index:0}.dg:not(.ac) .main{overflow:hidden}.dg.main{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear}.dg.main.taller-than-window{overflow-y:auto}.dg.main.taller-than-window .close-button{opacity:1;margin-top:-1px;border-top:1px solid #2c2c2c}.dg.main ul.closed .close-button{opacity:1 !important}.dg.main:hover .close-button,.dg.main .close-button.drag{opacity:1}.dg.main .close-button{-webkit-transition:opacity 0.1s linear;-o-transition:opacity 0.1s linear;-moz-transition:opacity 0.1s linear;transition:opacity 0.1s linear;border:0;position:absolute;line-height:19px;height:20px;cursor:pointer;text-align:center;background-color:#000}.dg.main .close-button:hover{background-color:#111}.dg.a{float:right;margin-right:15px;overflow-x:hidden}.dg.a.has-save ul{margin-top:27px}.dg.a.has-save ul.closed{margin-top:0}.dg.a .save-row{position:fixed;top:0;z-index:1002}.dg li{-webkit-transition:height 0.1s ease-out;-o-transition:height 0.1s ease-out;-moz-transition:height 0.1s ease-out;transition:height 0.1s ease-out}.dg li:not(.folder){cursor:auto;height:27px;line-height:27px;overflow:hidden;padding:0 4px 0 5px}.dg li.folder{padding:0;border-left:4px solid rgba(0,0,0,0)}.dg li.title{cursor:pointer;margin-left:-4px}.dg .closed li:not(.title),.dg .closed ul li,.dg .closed ul li > *{height:0;overflow:hidden;border:0}.dg .cr{clear:both;padding-left:3px;height:27px}.dg .property-name{cursor:default;float:left;clear:left;width:40%;overflow:hidden;text-overflow:ellipsis}.dg .c{float:left;width:60%}.dg .c input[type=text]{border:0;margin-top:4px;padding:3px;width:100%;float:right}.dg .has-slider input[type=text]{width:30%;margin-left:0}.dg .slider{float:left;width:66%;margin-left:-5px;margin-right:0;height:19px;margin-top:4px}.dg .slider-fg{height:100%}.dg .c input[type=checkbox]{margin-top:9px}.dg .c select{margin-top:5px}.dg .cr.function,.dg .cr.function .property-name,.dg .cr.function *,.dg .cr.boolean,.dg .cr.boolean *{cursor:pointer}.dg .selector{display:none;position:absolute;margin-left:-9px;margin-top:23px;z-index:10}.dg .c:hover .selector,.dg .selector.drag{display:block}.dg li.save-row{padding:0}.dg li.save-row .button{display:inline-block;padding:0px 6px}.dg.dialogue{background-color:#222;width:460px;padding:15px;font-size:13px;line-height:15px}#dg-new-constructor{padding:10px;color:#222;font-family:Monaco, monospace;font-size:10px;border:0;resize:none;box-shadow:inset 1px 1px 1px #888;word-wrap:break-word;margin:12px 0;display:block;width:440px;overflow-y:scroll;height:100px;position:relative}#dg-local-explain{display:none;font-size:11px;line-height:17px;border-radius:3px;background-color:#333;padding:8px;margin-top:10px}#dg-local-explain code{font-size:10px}#dat-gui-save-locally{display:none}.dg{color:#eee;font:11px 'Lucida Grande', sans-serif;text-shadow:0 -1px 0 #111}.dg.main::-webkit-scrollbar{width:5px;background:#1a1a1a}.dg.main::-webkit-scrollbar-corner{height:0;display:none}.dg.main::-webkit-scrollbar-thumb{border-radius:5px;background:#676767}.dg li:not(.folder){background:#1a1a1a;border-bottom:1px solid #2c2c2c}.dg li.save-row{line-height:25px;background:#dad5cb;border:0}.dg li.save-row select{margin-left:5px;width:108px}.dg li.save-row .button{margin-left:5px;margin-top:1px;border-radius:2px;font-size:9px;line-height:7px;padding:4px 4px 5px 4px;background:#c5bdad;color:#fff;text-shadow:0 1px 0 #b0a58f;box-shadow:0 -1px 0 #b0a58f;cursor:pointer}.dg li.save-row .button.gears{background:#c5bdad url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAsAAAANCAYAAAB/9ZQ7AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAQJJREFUeNpiYKAU/P//PwGIC/ApCABiBSAW+I8AClAcgKxQ4T9hoMAEUrxx2QSGN6+egDX+/vWT4e7N82AMYoPAx/evwWoYoSYbACX2s7KxCxzcsezDh3evFoDEBYTEEqycggWAzA9AuUSQQgeYPa9fPv6/YWm/Acx5IPb7ty/fw+QZblw67vDs8R0YHyQhgObx+yAJkBqmG5dPPDh1aPOGR/eugW0G4vlIoTIfyFcA+QekhhHJhPdQxbiAIguMBTQZrPD7108M6roWYDFQiIAAv6Aow/1bFwXgis+f2LUAynwoIaNcz8XNx3Dl7MEJUDGQpx9gtQ8YCueB+D26OECAAQDadt7e46D42QAAAABJRU5ErkJggg==) 2px 1px no-repeat;height:7px;width:8px}.dg li.save-row .button:hover{background-color:#bab19e;box-shadow:0 -1px 0 #b0a58f}.dg li.folder{border-bottom:0}.dg li.title{padding-left:16px;background:#000 url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlI+hKgFxoCgAOw==) 6px 10px no-repeat;cursor:pointer;border-bottom:1px solid rgba(255,255,255,0.2)}.dg .closed li.title{background-image:url(data:image/gif;base64,R0lGODlhBQAFAJEAAP////Pz8////////yH5BAEAAAIALAAAAAAFAAUAAAIIlGIWqMCbWAEAOw==)}.dg .cr.boolean{border-left:3px solid #806787}.dg .cr.function{border-left:3px solid #e61d5f}.dg .cr.number{border-left:3px solid #2fa1d6}.dg .cr.number input[type=text]{color:#2fa1d6}.dg .cr.string{border-left:3px solid #1ed36f}.dg .cr.string input[type=text]{color:#1ed36f}.dg .cr.function:hover,.dg .cr.boolean:hover{background:#111}.dg .c input[type=text]{background:#303030;outline:none}.dg .c input[type=text]:hover{background:#3c3c3c}.dg .c input[type=text]:focus{background:#494949;color:#fff}.dg .c .slider{background:#303030;cursor:ew-resize}.dg .c .slider-fg{background:#2fa1d6}.dg .c .slider:hover{background:#3c3c3c}.dg .c .slider:hover .slider-fg{background:#44abda}\n", 72 | dat.controllers.factory=function(e,a,c,d,f,b,n){return function(h,j,m,l){var o=h[j];if(n.isArray(m)||n.isObject(m))return new e(h,j,m);if(n.isNumber(o))return n.isNumber(m)&&n.isNumber(l)?new c(h,j,m,l):new a(h,j,{min:m,max:l});if(n.isString(o))return new d(h,j);if(n.isFunction(o))return new f(h,j,"");if(n.isBoolean(o))return new b(h,j)}}(dat.controllers.OptionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.StringController=function(e,a,c){var d= 73 | function(c,b){function e(){h.setValue(h.__input.value)}d.superclass.call(this,c,b);var h=this;this.__input=document.createElement("input");this.__input.setAttribute("type","text");a.bind(this.__input,"keyup",e);a.bind(this.__input,"change",e);a.bind(this.__input,"blur",function(){h.__onFinishChange&&h.__onFinishChange.call(h,h.getValue())});a.bind(this.__input,"keydown",function(a){a.keyCode===13&&this.blur()});this.updateDisplay();this.domElement.appendChild(this.__input)};d.superclass=e;c.extend(d.prototype, 74 | e.prototype,{updateDisplay:function(){if(!a.isActive(this.__input))this.__input.value=this.getValue();return d.superclass.prototype.updateDisplay.call(this)}});return d}(dat.controllers.Controller,dat.dom.dom,dat.utils.common),dat.controllers.FunctionController,dat.controllers.BooleanController,dat.utils.common),dat.controllers.Controller,dat.controllers.BooleanController,dat.controllers.FunctionController,dat.controllers.NumberControllerBox,dat.controllers.NumberControllerSlider,dat.controllers.OptionController, 75 | dat.controllers.ColorController=function(e,a,c,d,f){function b(a,b,c,d){a.style.background="";f.each(j,function(e){a.style.cssText+="background: "+e+"linear-gradient("+b+", "+c+" 0%, "+d+" 100%); "})}function n(a){a.style.background="";a.style.cssText+="background: -moz-linear-gradient(top, #ff0000 0%, #ff00ff 17%, #0000ff 34%, #00ffff 50%, #00ff00 67%, #ffff00 84%, #ff0000 100%);";a.style.cssText+="background: -webkit-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"; 76 | a.style.cssText+="background: -o-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: -ms-linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);";a.style.cssText+="background: linear-gradient(top, #ff0000 0%,#ff00ff 17%,#0000ff 34%,#00ffff 50%,#00ff00 67%,#ffff00 84%,#ff0000 100%);"}var h=function(e,l){function o(b){q(b);a.bind(window,"mousemove",q);a.bind(window, 77 | "mouseup",j)}function j(){a.unbind(window,"mousemove",q);a.unbind(window,"mouseup",j)}function g(){var a=d(this.value);a!==false?(p.__color.__state=a,p.setValue(p.__color.toOriginal())):this.value=p.__color.toString()}function i(){a.unbind(window,"mousemove",s);a.unbind(window,"mouseup",i)}function q(b){b.preventDefault();var c=a.getWidth(p.__saturation_field),d=a.getOffset(p.__saturation_field),e=(b.clientX-d.left+document.body.scrollLeft)/c,b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b= 78 | 1:b<0&&(b=0);e>1?e=1:e<0&&(e=0);p.__color.v=b;p.__color.s=e;p.setValue(p.__color.toOriginal());return false}function s(b){b.preventDefault();var c=a.getHeight(p.__hue_field),d=a.getOffset(p.__hue_field),b=1-(b.clientY-d.top+document.body.scrollTop)/c;b>1?b=1:b<0&&(b=0);p.__color.h=b*360;p.setValue(p.__color.toOriginal());return false}h.superclass.call(this,e,l);this.__color=new c(this.getValue());this.__temp=new c(0);var p=this;this.domElement=document.createElement("div");a.makeSelectable(this.domElement, 79 | false);this.__selector=document.createElement("div");this.__selector.className="selector";this.__saturation_field=document.createElement("div");this.__saturation_field.className="saturation-field";this.__field_knob=document.createElement("div");this.__field_knob.className="field-knob";this.__field_knob_border="2px solid ";this.__hue_knob=document.createElement("div");this.__hue_knob.className="hue-knob";this.__hue_field=document.createElement("div");this.__hue_field.className="hue-field";this.__input= 80 | document.createElement("input");this.__input.type="text";this.__input_textShadow="0 1px 1px ";a.bind(this.__input,"keydown",function(a){a.keyCode===13&&g.call(this)});a.bind(this.__input,"blur",g);a.bind(this.__selector,"mousedown",function(){a.addClass(this,"drag").bind(window,"mouseup",function(){a.removeClass(p.__selector,"drag")})});var t=document.createElement("div");f.extend(this.__selector.style,{width:"122px",height:"102px",padding:"3px",backgroundColor:"#222",boxShadow:"0px 1px 3px rgba(0,0,0,0.3)"}); 81 | f.extend(this.__field_knob.style,{position:"absolute",width:"12px",height:"12px",border:this.__field_knob_border+(this.__color.v<0.5?"#fff":"#000"),boxShadow:"0px 1px 3px rgba(0,0,0,0.5)",borderRadius:"12px",zIndex:1});f.extend(this.__hue_knob.style,{position:"absolute",width:"15px",height:"2px",borderRight:"4px solid #fff",zIndex:1});f.extend(this.__saturation_field.style,{width:"100px",height:"100px",border:"1px solid #555",marginRight:"3px",display:"inline-block",cursor:"pointer"});f.extend(t.style, 82 | {width:"100%",height:"100%",background:"none"});b(t,"top","rgba(0,0,0,0)","#000");f.extend(this.__hue_field.style,{width:"15px",height:"100px",display:"inline-block",border:"1px solid #555",cursor:"ns-resize"});n(this.__hue_field);f.extend(this.__input.style,{outline:"none",textAlign:"center",color:"#fff",border:0,fontWeight:"bold",textShadow:this.__input_textShadow+"rgba(0,0,0,0.7)"});a.bind(this.__saturation_field,"mousedown",o);a.bind(this.__field_knob,"mousedown",o);a.bind(this.__hue_field,"mousedown", 83 | function(b){s(b);a.bind(window,"mousemove",s);a.bind(window,"mouseup",i)});this.__saturation_field.appendChild(t);this.__selector.appendChild(this.__field_knob);this.__selector.appendChild(this.__saturation_field);this.__selector.appendChild(this.__hue_field);this.__hue_field.appendChild(this.__hue_knob);this.domElement.appendChild(this.__input);this.domElement.appendChild(this.__selector);this.updateDisplay()};h.superclass=e;f.extend(h.prototype,e.prototype,{updateDisplay:function(){var a=d(this.getValue()); 84 | if(a!==false){var e=false;f.each(c.COMPONENTS,function(b){if(!f.isUndefined(a[b])&&!f.isUndefined(this.__color.__state[b])&&a[b]!==this.__color.__state[b])return e=true,{}},this);e&&f.extend(this.__color.__state,a)}f.extend(this.__temp.__state,this.__color.__state);this.__temp.a=1;var h=this.__color.v<0.5||this.__color.s>0.5?255:0,j=255-h;f.extend(this.__field_knob.style,{marginLeft:100*this.__color.s-7+"px",marginTop:100*(1-this.__color.v)-7+"px",backgroundColor:this.__temp.toString(),border:this.__field_knob_border+ 85 | "rgb("+h+","+h+","+h+")"});this.__hue_knob.style.marginTop=(1-this.__color.h/360)*100+"px";this.__temp.s=1;this.__temp.v=1;b(this.__saturation_field,"left","#fff",this.__temp.toString());f.extend(this.__input.style,{backgroundColor:this.__input.value=this.__color.toString(),color:"rgb("+h+","+h+","+h+")",textShadow:this.__input_textShadow+"rgba("+j+","+j+","+j+",.7)"})}});var j=["-moz-","-o-","-webkit-","-ms-",""];return h}(dat.controllers.Controller,dat.dom.dom,dat.color.Color=function(e,a,c,d){function f(a, 86 | b,c){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="RGB")return this.__state[b];n(this,b,c);return this.__state[b]},set:function(a){if(this.__state.space!=="RGB")n(this,b,c),this.__state.space="RGB";this.__state[b]=a}})}function b(a,b){Object.defineProperty(a,b,{get:function(){if(this.__state.space==="HSV")return this.__state[b];h(this);return this.__state[b]},set:function(a){if(this.__state.space!=="HSV")h(this),this.__state.space="HSV";this.__state[b]=a}})}function n(b,c,e){if(b.__state.space=== 87 | "HEX")b.__state[c]=a.component_from_hex(b.__state.hex,e);else if(b.__state.space==="HSV")d.extend(b.__state,a.hsv_to_rgb(b.__state.h,b.__state.s,b.__state.v));else throw"Corrupted color state";}function h(b){var c=a.rgb_to_hsv(b.r,b.g,b.b);d.extend(b.__state,{s:c.s,v:c.v});if(d.isNaN(c.h)){if(d.isUndefined(b.__state.h))b.__state.h=0}else b.__state.h=c.h}var j=function(){this.__state=e.apply(this,arguments);if(this.__state===false)throw"Failed to interpret color arguments";this.__state.a=this.__state.a|| 88 | 1};j.COMPONENTS="r,g,b,h,s,v,hex,a".split(",");d.extend(j.prototype,{toString:function(){return c(this)},toOriginal:function(){return this.__state.conversion.write(this)}});f(j.prototype,"r",2);f(j.prototype,"g",1);f(j.prototype,"b",0);b(j.prototype,"h");b(j.prototype,"s");b(j.prototype,"v");Object.defineProperty(j.prototype,"a",{get:function(){return this.__state.a},set:function(a){this.__state.a=a}});Object.defineProperty(j.prototype,"hex",{get:function(){if(!this.__state.space!=="HEX")this.__state.hex= 89 | a.rgb_to_hex(this.r,this.g,this.b);return this.__state.hex},set:function(a){this.__state.space="HEX";this.__state.hex=a}});return j}(dat.color.interpret,dat.color.math=function(){var e;return{hsv_to_rgb:function(a,c,d){var e=a/60-Math.floor(a/60),b=d*(1-c),n=d*(1-e*c),c=d*(1-(1-e)*c),a=[[d,c,b],[n,d,b],[b,d,c],[b,n,d],[c,b,d],[d,b,n]][Math.floor(a/60)%6];return{r:a[0]*255,g:a[1]*255,b:a[2]*255}},rgb_to_hsv:function(a,c,d){var e=Math.min(a,c,d),b=Math.max(a,c,d),e=b-e;if(b==0)return{h:NaN,s:0,v:0}; 90 | a=a==b?(c-d)/e:c==b?2+(d-a)/e:4+(a-c)/e;a/=6;a<0&&(a+=1);return{h:a*360,s:e/b,v:b/255}},rgb_to_hex:function(a,c,d){a=this.hex_with_component(0,2,a);a=this.hex_with_component(a,1,c);return a=this.hex_with_component(a,0,d)},component_from_hex:function(a,c){return a>>c*8&255},hex_with_component:function(a,c,d){return d<<(e=c*8)|a&~(255<c.children.length;){var j=document.createElement("span");j.style.cssText="width:1px;height:30px;float:left;background-color:#113";c.appendChild(j)}var d=document.createElement("div");d.id="ms";d.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#020;display:none";f.appendChild(d);var k=document.createElement("div"); 4 | k.id="msText";k.style.cssText="color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px";k.innerHTML="MS";d.appendChild(k);var e=document.createElement("div");e.id="msGraph";e.style.cssText="position:relative;width:74px;height:30px;background-color:#0f0";for(d.appendChild(e);74>e.children.length;)j=document.createElement("span"),j.style.cssText="width:1px;height:30px;float:left;background-color:#131",e.appendChild(j);var t=function(b){s=b;switch(s){case 0:a.style.display= 5 | "block";d.style.display="none";break;case 1:a.style.display="none",d.style.display="block"}};return{REVISION:12,domElement:f,setMode:t,begin:function(){l=Date.now()},end:function(){var b=Date.now();g=b-l;n=Math.min(n,g);o=Math.max(o,g);k.textContent=g+" MS ("+n+"-"+o+")";var a=Math.min(30,30-30*(g/200));e.appendChild(e.firstChild).style.height=a+"px";r++;b>m+1E3&&(h=Math.round(1E3*r/(b-m)),p=Math.min(p,h),q=Math.max(q,h),i.textContent=h+" FPS ("+p+"-"+q+")",a=Math.min(30,30-30*(h/100)),c.appendChild(c.firstChild).style.height= 6 | a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats); 7 | -------------------------------------------------------------------------------- /js/webGLUtility.js: -------------------------------------------------------------------------------- 1 | // WebGL Utility 2 | // Javascript closure class that intializes context and creates program in the 3 | // context. Javascript analog to the glslUtility class that you are used to 4 | // seeing in the CUDA assignments. 5 | // University of Pennsylvania (c) 2014 6 | 7 | (function(exports) { 8 | "use strict"; 9 | 10 | exports = exports || window; 11 | 12 | /////////////////////////////////////////////////////////////////////////// 13 | // Shim from http://paulirish.com/2011/requestanimationframe-for-smart-animating/ 14 | 15 | exports.requestAnimFrame = 16 | window.requestAnimationFrame || 17 | window.webkitRequestAnimationFrame || 18 | window.mozRequestAnimationFrame || 19 | window.oRequestAnimationFrame || 20 | window.msRequestAnimationFrame || 21 | function( callback ){ 22 | window.setTimeout(callback, 1000 / 60); 23 | }; 24 | 25 | /////////////////////////////////////////////////////////////////////////// 26 | // getShader based on http://learningwebgl.com/cookbook/index.php/Loading_shaders_from_HTML_script_tags 27 | 28 | // LOOK : Grabs the text shader source from the document for shader 29 | // compilation 30 | exports.getShaderSource = function(script) { 31 | var str = ""; 32 | var k = script.firstChild; 33 | while (k) { 34 | if (k.nodeType == 3) { 35 | str += k.textContent; 36 | } 37 | k = k.nextSibling; 38 | } 39 | 40 | return str; 41 | }; 42 | 43 | /////////////////////////////////////////////////////////////////////////// 44 | 45 | // LOOK : Like OpenGL, we will create a webGL context for renderering 46 | 47 | exports.createWebGLContext = function(canvas, message) { 48 | // Detect if browser supports WebGL 49 | if (!window.WebGLRenderingContext) { 50 | message.innerText = "The browser does not support WebGL. Visit http://get.webgl.org."; 51 | return undefined; 52 | } 53 | var options = { alpha: false }; 54 | var context = canvas.getContext("webgl") || canvas.getContext("experimental-webgl", options); 55 | 56 | if (!context && message) { 57 | message.innerText = "The browser supports WebGL, but initialization failed."; 58 | } 59 | 60 | return context; 61 | }; 62 | 63 | // LOOK : Create program from the vertex and fragment shaders passed in 64 | exports.createProgram = function(context, vertexShaderSource, fragmentShaderSource, message) { 65 | var program = context.createProgram(); 66 | var vs = context.createShader(context.VERTEX_SHADER); 67 | var fs = context.createShader(context.FRAGMENT_SHADER); 68 | 69 | context.attachShader(program, vs); 70 | context.attachShader(program, fs); 71 | 72 | // Mark shader for deletion when the program is deleted 73 | context.deleteShader(vs); 74 | context.deleteShader(fs); 75 | 76 | context.shaderSource(vs, vertexShaderSource); 77 | context.compileShader(vs); 78 | if (!context.getShaderParameter(vs, context.COMPILE_STATUS)) { 79 | if (message) { 80 | message.innerText += context.getShaderInfoLog(vs) + "\n"; 81 | alert(context.getShaderInfoLog(vs)); 82 | } 83 | else 84 | { 85 | alert(context.getShaderInfoLog(vs)); 86 | } 87 | context.deleteProgram(program); 88 | return; 89 | } 90 | 91 | context.shaderSource(fs, fragmentShaderSource); 92 | context.compileShader(fs); 93 | if (!context.getShaderParameter(fs, context.COMPILE_STATUS)) { 94 | if (message) { 95 | message.innerText += context.getShaderInfoLog(fs) + "\n"; 96 | alert(context.getShaderInfoLog(fs)); 97 | } 98 | else 99 | { 100 | alert(context.getShaderInfoLog(fs)); 101 | } 102 | context.deleteProgram(program); 103 | return; 104 | } 105 | 106 | // Link program 107 | context.linkProgram(program); 108 | if (!context.getProgramParameter(program, context.LINK_STATUS)) { 109 | if (message) { 110 | message.innerText += context.getProgramInfoLog(program) + "\n"; 111 | alert(context.getShaderInfoLog(program)); 112 | } 113 | else 114 | { 115 | alert(context.getShaderInfoLog(program)); 116 | } 117 | context.deleteProgram(program); 118 | return; 119 | } 120 | 121 | return program; 122 | }; 123 | 124 | }()); 125 | -------------------------------------------------------------------------------- /left-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/left-arrow.png -------------------------------------------------------------------------------- /right-arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wulinjiansheng/WebGL_PathTracer/8c5fd06994cfdf025d50a2f22cc5eb510d631310/right-arrow.png --------------------------------------------------------------------------------