diff --git a/README.md b/README.md index 25002db..92d8d45 100644 --- a/README.md +++ b/README.md @@ -3,28 +3,55 @@ WebGL Deferred Shading **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) **Google Chrome 222.2** on - Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* Bowen Bao +* Tested on: Firefox 49.0.2 on + Windows 10, i7-6700K @ 4.00GHz 32GB, GTX 1080 8192MB (Personal Computer) -### Live Online +## Overview -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +1. Core Features: + * Blinn-Phong shading + * Bloom using Gaussian blur + * Scissor test optimization + * g-buffer optimization +2. Extra Features: + * Toon shading + * Screen-space motion blur -### Demo Video/GIF +## Performance -[![](img/video.png)](TODO) +We first compare the performance under different effects with settings of different amount of lights. -### (TODO: Your README) +![](/img/perf.png) -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +It is interesting as we could observe that the effects are not putting a heavy load on the performance. And we get a linearly decrease in frame rates with the increasing number of lights. -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! +![](/img/perf2.png) +Observe that we used to pass four arrays in GBuffer, but two(normal and normal map) among them are only required so that we could calculate the modified normal for later use. We can optimize this step by pre calculating the normal, and pass it in one array in the GBuffer. Now we only pass 3 arrays each time. And as observed, the performance rises. -### Credits +![](/img/scissor.png) + +Above shows the box of scissor test. We improve performance by only rendering the region which is affected by each light(the red rectangle). + +## Effects + +### Basic + +![](/img/basic.png) + +### Bloom + +![](/img/bloom.png) + +(Due to the number of lights, the bloom effect is not that obvious... The lights now would seem to glow compared to the basic rendering) + +### Motion Blur + +![](/img/blur.gif) + + +## Credits * [Three.js](https://github.com/mrdoob/three.js) by [@mrdoob](https://github.com/mrdoob) and contributors * [stats.js](https://github.com/mrdoob/stats.js) by [@mrdoob](https://github.com/mrdoob) and contributors diff --git a/glsl/copy.frag.glsl b/glsl/copy.frag.glsl index 823ebcd..7782d3e 100644 --- a/glsl/copy.frag.glsl +++ b/glsl/copy.frag.glsl @@ -10,11 +10,25 @@ varying vec3 v_position; varying vec3 v_normal; varying vec2 v_uv; +vec3 applyNormalMap(vec3 geomnor, vec3 normap) { + normap = normap * 2.0 - 1.0; + vec3 up = normalize(vec3(0.001, 1, 0.001)); + vec3 surftan = normalize(cross(geomnor, up)); + vec3 surfbinor = cross(geomnor, surftan); + return normap.y * surftan + normap.x * surfbinor + normap.z * geomnor; +} + void main() { // TODO: copy values into gl_FragData[0], [1], etc. // You can use the GLSL texture2D function to access the textures using // the UV in v_uv. // this gives you the idea - // gl_FragData[0] = vec4( v_position, 1.0 ); + gl_FragData[0] = vec4( v_position, 1.0 ); + + vec3 normap = texture2D(u_normap, v_uv).xyz; + + gl_FragData[1] = vec4(normalize(applyNormalMap(v_normal, normap)) , 0.0); + gl_FragData[2] = texture2D(u_colmap, v_uv); + //gl_FragData[3] = texture2D(u_normap, v_uv); } diff --git a/glsl/deferred/ambient.frag.glsl b/glsl/deferred/ambient.frag.glsl index 1fd4647..7790324 100644 --- a/glsl/deferred/ambient.frag.glsl +++ b/glsl/deferred/ambient.frag.glsl @@ -3,7 +3,8 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +//#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; @@ -14,7 +15,7 @@ void main() { vec4 gb0 = texture2D(u_gbufs[0], v_uv); vec4 gb1 = texture2D(u_gbufs[1], v_uv); vec4 gb2 = texture2D(u_gbufs[2], v_uv); - vec4 gb3 = texture2D(u_gbufs[3], v_uv); + //vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables @@ -23,5 +24,6 @@ void main() { return; } - gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + //gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + gl_FragColor = vec4(0.1 * gb2.rbg, 1.0); } diff --git a/glsl/deferred/blinnphong-pointlight.frag.glsl b/glsl/deferred/blinnphong-pointlight.frag.glsl index b24a54a..944b8a7 100644 --- a/glsl/deferred/blinnphong-pointlight.frag.glsl +++ b/glsl/deferred/blinnphong-pointlight.frag.glsl @@ -2,7 +2,8 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +//#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 uniform vec3 u_lightCol; uniform vec3 u_lightPos; @@ -10,6 +11,8 @@ uniform float u_lightRad; uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; +uniform int u_toon; + varying vec2 v_uv; vec3 applyNormalMap(vec3 geomnor, vec3 normap) { @@ -24,10 +27,18 @@ void main() { vec4 gb0 = texture2D(u_gbufs[0], v_uv); vec4 gb1 = texture2D(u_gbufs[1], v_uv); vec4 gb2 = texture2D(u_gbufs[2], v_uv); - vec4 gb3 = texture2D(u_gbufs[3], v_uv); + //vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables + vec3 pos = gb0.xyz; + //vec3 geomNor = gb1.xyz; + vec3 col = gb2.rgb; + //vec3 nor = gb3.xyz; + vec3 nor_ = gb1.xyz;//applyNormalMap(geomNor, nor); + + float dist = distance(pos, u_lightPos); + // If nothing was rendered to this pixel, set alpha to 0 so that the // postprocessing step can render the sky color. if (depth == 1.0) { @@ -35,5 +46,28 @@ void main() { return; } - gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + //gl_FragColor = vec4(1, 0, 0, 1); // TODO: perform lighting calculations + vec3 lightDir = normalize(u_lightPos - pos); + vec3 viewDir = normalize(-pos); + vec3 halfDir = normalize(lightDir + viewDir); + + float attenuation = max(0.0, u_lightRad - dist); + float lambertian = max(dot(lightDir, nor_), 0.0); + float specAngle = max(dot(halfDir, nor_), 0.0); + vec3 colLinear = 0.3 * specAngle * u_lightCol + 0.7 * col * u_lightCol * lambertian; + if (u_toon == 1) + { + float steps = 3.0; + specAngle = ceil(specAngle * steps) / steps; + lambertian = ceil(lambertian * steps) / steps; + attenuation = ceil(attenuation * steps) / steps; + + colLinear = 0.3 * specAngle * u_lightCol + 0.7 * col * u_lightCol * lambertian; + + gl_FragColor = vec4(colLinear * attenuation, 1.0); + } + else + { + gl_FragColor = vec4(colLinear * attenuation, 1.0); + } } diff --git a/glsl/deferred/debug.frag.glsl b/glsl/deferred/debug.frag.glsl index 007466f..9ea76cc 100644 --- a/glsl/deferred/debug.frag.glsl +++ b/glsl/deferred/debug.frag.glsl @@ -2,7 +2,8 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +//#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 uniform int u_debug; uniform sampler2D u_gbufs[NUM_GBUFFERS]; @@ -24,29 +25,31 @@ void main() { vec4 gb0 = texture2D(u_gbufs[0], v_uv); vec4 gb1 = texture2D(u_gbufs[1], v_uv); vec4 gb2 = texture2D(u_gbufs[2], v_uv); - vec4 gb3 = texture2D(u_gbufs[3], v_uv); + //vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables // These definitions are suggested for starting out, but you will probably want to change them. vec3 pos = gb0.xyz; // World-space position - vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + //vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping vec3 colmap = gb2.rgb; // The color map - unlit "albedo" (surface color) - vec3 normap = gb3.xyz; // The raw normal map (normals relative to the surface they're on) - vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) + vec3 normap = gb1.xyz; // The raw normal map (normals relative to the surface they're on) + //vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) // TODO: uncomment if (u_debug == 0) { gl_FragColor = vec4(vec3(depth), 1.0); } else if (u_debug == 1) { - // gl_FragColor = vec4(abs(pos) * 0.1, 1.0); + gl_FragColor = vec4(abs(pos) * 0.1, 1.0); } else if (u_debug == 2) { - // gl_FragColor = vec4(abs(geomnor), 1.0); + //gl_FragColor = vec4(abs(geomnor), 1.0); + gl_FragColor = vec4(normap, 1.0); } else if (u_debug == 3) { - // gl_FragColor = vec4(colmap, 1.0); + gl_FragColor = vec4(colmap, 1.0); } else if (u_debug == 4) { - // gl_FragColor = vec4(normap, 1.0); + //gl_FragColor = vec4(normap, 1.0); + gl_FragColor = vec4(normap, 1.0); } else if (u_debug == 5) { - // gl_FragColor = vec4(abs(nor), 1.0); + gl_FragColor = vec4(normap, 1.0); } else { gl_FragColor = vec4(1, 0, 1, 1); } diff --git a/glsl/post/blur.frag.glsl b/glsl/post/blur.frag.glsl new file mode 100644 index 0000000..0ea65a1 --- /dev/null +++ b/glsl/post/blur.frag.glsl @@ -0,0 +1,39 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform mat4 u_prev; +uniform sampler2D u_pos; + +varying vec2 v_uv; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() +{ + vec4 color = texture2D(u_color, v_uv); + + if (color.a == 0.0) + { + gl_FragColor = SKY_COLOR; + return; + } + + vec3 pos = texture2D(u_pos, v_uv).xyz; + vec4 prev_pos = u_prev * vec4(pos, 1.0); + prev_pos = prev_pos / prev_pos.w; + + prev_pos = prev_pos * 0.5 + 0.5; + + vec2 velocity = ((v_uv - prev_pos.xy) * 0.35); + + vec2 coord = v_uv + velocity; + + for (int i = 0; i < 10; ++i) + { + color += texture2D(u_color, coord); + coord += velocity; + } + gl_FragColor = color * 0.1; +} \ No newline at end of file diff --git a/glsl/post/old.frag.glsl b/glsl/post/old.frag.glsl new file mode 100644 index 0000000..d80603c --- /dev/null +++ b/glsl/post/old.frag.glsl @@ -0,0 +1,19 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; + +varying vec2 v_uv; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + vec4 color = texture2D(u_color, v_uv); + + if (color.a == 0.0) { + color = SKY_COLOR; + } + + gl_FragColor = color; +} diff --git a/glsl/post/one.frag.glsl b/glsl/post/one.frag.glsl index 94191cd..70f3ce0 100644 --- a/glsl/post/one.frag.glsl +++ b/glsl/post/one.frag.glsl @@ -3,18 +3,27 @@ precision highp float; precision highp int; uniform sampler2D u_color; +uniform vec2 u_screen_inv; varying vec2 v_uv; const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); +const vec3 Gaussian = vec3(0.4, 0.25, 0.05); + void main() { vec4 color = texture2D(u_color, v_uv); if (color.a == 0.0) { - gl_FragColor = SKY_COLOR; - return; - } + color = SKY_COLOR; + } + + vec4 l2 = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x * 2.0, v_uv.y)); + vec4 l1 = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x * 1.0, v_uv.y)); + vec4 r2 = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x * 2.0, v_uv.y)); + vec4 r1 = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x * 1.0, v_uv.y)); - gl_FragColor = color; + gl_FragColor = color * Gaussian.x; + + l2 * Gaussian.z + l1 * Gaussian.y + + r2 * Gaussian.z + r1 * Gaussian.y; } diff --git a/glsl/post/toon1.frag.glsl b/glsl/post/toon1.frag.glsl new file mode 100644 index 0000000..e2495cf --- /dev/null +++ b/glsl/post/toon1.frag.glsl @@ -0,0 +1,24 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform vec2 u_screen_inv; + +varying vec2 v_uv; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + vec4 color = texture2D(u_color, v_uv); + + vec4 l1 = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x, v_uv.y)); + vec4 l1t = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x, v_uv.y - u_screen_inv.y)); + vec4 l1b = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x, v_uv.y + u_screen_inv.y)); + + vec4 r1 = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x * 1.0, v_uv.y)); + vec4 r1t = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x, v_uv.y - u_screen_inv.y)); + vec4 r1b = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x, v_uv.y + u_screen_inv.y)); + + gl_FragColor = -l1 - l1t - l1b + r1 + r1t + r1b; +} diff --git a/glsl/post/toon2.frag.glsl b/glsl/post/toon2.frag.glsl new file mode 100644 index 0000000..ef12014 --- /dev/null +++ b/glsl/post/toon2.frag.glsl @@ -0,0 +1,38 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform sampler2D u_old_color; +uniform vec2 u_screen_inv; + +varying vec2 v_uv; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + vec4 color = texture2D(u_color, v_uv); + vec4 old_color = texture2D(u_old_color, v_uv); + + vec4 t1 = texture2D(u_color, vec2(v_uv.x, v_uv.y - u_screen_inv.y)); + vec4 t1l = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x, v_uv.y - u_screen_inv.y)); + vec4 t1r = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x, v_uv.y - u_screen_inv.y)); + + vec4 b1 = texture2D(u_color, vec2(v_uv.x, v_uv.y + u_screen_inv.y)); + vec4 b1l = texture2D(u_color, vec2(v_uv.x - u_screen_inv.x, v_uv.y + u_screen_inv.y)); + vec4 b1r = texture2D(u_color, vec2(v_uv.x + u_screen_inv.x, v_uv.y + u_screen_inv.y)); + + vec4 blend = -t1 - t1l - t1r + b1 + b1l + b1r; + float edge = max(max(blend.x, blend.y), blend.z); + + if (edge <= 0.3) + { + blend = vec4(0.0, 0.0, 0.0, 1.0); + } + else + { + blend = vec4(1.0); + } + + gl_FragColor = (1.0 - blend) * old_color; +} diff --git a/glsl/post/two.frag.glsl b/glsl/post/two.frag.glsl new file mode 100644 index 0000000..0574793 --- /dev/null +++ b/glsl/post/two.frag.glsl @@ -0,0 +1,35 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform sampler2D u_old_color; +uniform vec2 u_screen_inv; + +varying vec2 v_uv; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +const vec3 Gaussian = vec3(0.4, 0.25, 0.05); + +void main() +{ + vec4 color = texture2D(u_color, v_uv); + vec4 old_color = texture2D(u_old_color, v_uv); + + if (color.a == 0.0) + { + color = SKY_COLOR; + } + + vec4 t2 = texture2D(u_color, vec2(v_uv.x, v_uv.y - u_screen_inv.y * 2.0)); + vec4 t1 = texture2D(u_color, vec2(v_uv.x, v_uv.y - u_screen_inv.y * 1.0)); + vec4 b2 = texture2D(u_color, vec2(v_uv.x, v_uv.y + u_screen_inv.y * 2.0)); + vec4 b1 = texture2D(u_color, vec2(v_uv.x, v_uv.y + u_screen_inv.y * 1.0)); + + vec4 blend = old_color + color * Gaussian.x + + t1 * Gaussian.y + t2 * Gaussian.z + + b1 * Gaussian.y + b2 * Gaussian.z; + + gl_FragColor = 0.7 * blend; +} \ No newline at end of file diff --git a/glsl/red.frag.glsl b/glsl/red.frag.glsl index f8ef1ec..3389ee5 100644 --- a/glsl/red.frag.glsl +++ b/glsl/red.frag.glsl @@ -3,5 +3,5 @@ precision highp float; precision highp int; void main() { - gl_FragColor = vec4(1, 0, 0, 1); + gl_FragColor = vec4(1, 0, 0, 0.3); } diff --git a/img/basic.png b/img/basic.png new file mode 100644 index 0000000..19d7812 Binary files /dev/null and b/img/basic.png differ diff --git a/img/bloom.png b/img/bloom.png new file mode 100644 index 0000000..d78bf68 Binary files /dev/null and b/img/bloom.png differ diff --git a/img/blur.gif b/img/blur.gif new file mode 100644 index 0000000..7167ffe Binary files /dev/null and b/img/blur.gif differ diff --git a/img/perf.png b/img/perf.png new file mode 100644 index 0000000..f6c3789 Binary files /dev/null and b/img/perf.png differ diff --git a/img/perf2.png b/img/perf2.png new file mode 100644 index 0000000..419e2dc Binary files /dev/null and b/img/perf2.png differ diff --git a/img/scissor.png b/img/scissor.png new file mode 100644 index 0000000..9503062 Binary files /dev/null and b/img/scissor.png differ diff --git a/img/toon.png b/img/toon.png new file mode 100644 index 0000000..8329fc0 Binary files /dev/null and b/img/toon.png differ diff --git a/js/deferredRender.js b/js/deferredRender.js index bb3edd4..ae0f586 100644 --- a/js/deferredRender.js +++ b/js/deferredRender.js @@ -1,6 +1,7 @@ (function() { 'use strict'; // deferredSetup.js must be loaded first + var prevMat = null; R.deferredRender = function(state) { if (!aborted && ( @@ -10,8 +11,12 @@ !R.prog_Ambient || !R.prog_BlinnPhong_PointLight || !R.prog_Debug || - !R.progPost1)) { - console.log('waiting for programs to load...'); + !R.progPost1 || + !R.progPost2 || + !R.progPostToon1 || + !R.progPostToon2 || + !R.progBlur)) { + console.log('waiting for programs to load... ' + R.progBlur + " " + R.progPostToon2); return; } @@ -28,12 +33,12 @@ // CHECKITOUT: START HERE! You can even uncomment this: //debugger; - { // TODO: this block should be removed after testing renderFullScreenQuad - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - // TODO: Implement/test renderFullScreenQuad first - renderFullScreenQuad(R.progRed); - return; - } + //{ // TODO: this block should be removed after testing renderFullScreenQuad + // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + // // TODO: Implement/test renderFullScreenQuad first + // renderFullScreenQuad(R.progRed); + // return; + //} R.pass_copy.render(state); @@ -44,8 +49,31 @@ } else { // * Deferred pass and postprocessing pass(es) // TODO: uncomment these - // R.pass_deferred.render(state); - // R.pass_post1.render(state); + R.pass_deferred.render(state); + //console.log("deferred render"); + + if (!cfg.bloom && !cfg.toon && !cfg.blur) + { + //console.log("old render"); + R.pass_post1.oldRender(state); + } + else if (cfg.bloom) + { + R.pass_post1.render(state); + R.pass_post2.render(state); + } + else if (cfg.toon) + { + R.pass_postToon1.render(state); + R.pass_postToon2.render(state); + } + else if (cfg.blur) + { + //console.log("blur"); + R.pass_blur.render(state); + } + + //console.log("blusr state " + cfg.blur); // OPTIONAL TODO: call more postprocessing passes, if any } @@ -57,21 +85,21 @@ R.pass_copy.render = function(state) { // * Bind the framebuffer R.pass_copy.fbo // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); + gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); // * Clear screen using R.progClear // TODO: uncomment - // renderFullScreenQuad(R.progClear); + renderFullScreenQuad(R.progClear); // * Clear depth buffer to value 1.0 using gl.clearDepth and gl.clear // TODO: uncomment - // gl.clearDepth(1.0); - // gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); // * "Use" the program R.progCopy.prog // TODO: uncomment - // gl.useProgram(R.progCopy.prog); + gl.useProgram(R.progCopy.prog); // TODO: Go write code in glsl/copy.frag.glsl @@ -79,11 +107,11 @@ // * Upload the camera matrix m to the uniform R.progCopy.u_cameraMat // using gl.uniformMatrix4fv // TODO: uncomment - // gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); + gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); // * Draw the scene // TODO: uncomment - // drawScene(state); + drawScene(state); }; var drawScene = function(state) { @@ -101,17 +129,17 @@ R.pass_debug.render = function(state) { // * Unbind any framebuffer, so we can write to the screen // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); // * Bind/setup the debug "lighting" pass // * Tell shader which debug view to use // TODO: uncomment - // bindTexturesForLightPass(R.prog_Debug); - // gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); + bindTexturesForLightPass(R.prog_Debug); + gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); // * Render a fullscreen quad to perform shading on // TODO: uncomment - // renderFullScreenQuad(R.prog_Debug); + renderFullScreenQuad(R.prog_Debug); }; /** @@ -133,9 +161,9 @@ // Here is a wonderful demo of showing how blend function works: // http://mrdoob.github.io/webgl-blendfunctions/blendfunc.html // TODO: uncomment - // gl.enable(gl.BLEND); - // gl.blendEquation( gl.FUNC_ADD ); - // gl.blendFunc(gl.ONE,gl.ONE); + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc(gl.ONE,gl.ONE); // * Bind/setup the ambient pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_Ambient); @@ -147,7 +175,47 @@ // TODO: add a loop here, over the values in R.lights, which sets the // uniforms R.prog_BlinnPhong_PointLight.u_lightPos/Col/Rad etc., // then does renderFullScreenQuad(R.prog_BlinnPhong_PointLight). + + if (cfg.debugScissor) + { + gl.enable(gl.SCISSOR_TEST); + } + for (var i = 0; i < R.lights.length; ++i) + { + var light = R.lights[i]; + if (cfg.debugScissor) + { + var sc = getScissorForLight(state.viewMat, state.projMat, light); + if (sc != null) { + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + } + else continue; + } + gl.uniform1i(R.prog_BlinnPhong_PointLight.u_toon, cfg.toon); + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightPos, light.pos); + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightCol, light.col); + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, light.rad); + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + } + + gl.disable(gl.SCISSOR_TEST); + + if (cfg.debugScissor) + { + gl.enable(gl.SCISSOR_TEST); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + for (var i = 0; i < R.lights.length; ++i) { + var light = R.lights[i]; + var sc = getScissorForLight(state.viewMat, state.projMat, light); + if (sc != null) { + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + renderFullScreenQuad(R.progRed); + } + else continue; + } + } + gl.disable(gl.SCISSOR_TEST); // TODO: In the lighting loop, use the scissor test optimization // Enable gl.SCISSOR_TEST, render all lights, then disable it. // @@ -175,12 +243,40 @@ gl.uniform1i(prog.u_depth, R.NUM_GBUFFERS); }; + R.pass_post1.oldRender = function (state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progPostOld.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progPost1.u_color uniform to point at texture unit 0 + gl.uniform1i(R.progPostOld.u_color, 0); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progPostOld); + }; + + /** * 'post1' pass: Perform (first) pass of post-processing */ R.pass_post1.render = function(state) { // * Unbind any existing framebuffer (if there are no more passes) - gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_post1.fbo); + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); // * Clear the framebuffer depth to 1.0 gl.clearDepth(1.0); @@ -192,19 +288,147 @@ // * Bind the deferred pass's color output as a texture input // Set gl.TEXTURE0 as the gl.activeTexture unit // TODO: uncomment - // gl.activeTexture(gl.TEXTURE0); + gl.activeTexture(gl.TEXTURE0); // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit // TODO: uncomment - // gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); // Configure the R.progPost1.u_color uniform to point at texture unit 0 gl.uniform1i(R.progPost1.u_color, 0); - + gl.uniform2f(R.progPost1.u_screen_inv, 1.0 / width, 1.0 / height); + //console.log("inv: " + 1.0 / width + " " + 1.0 / height); // * Render a fullscreen quad to perform shading on renderFullScreenQuad(R.progPost1); }; + R.pass_post2.render = function (state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progPost2.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progPost1.u_color uniform to point at texture unit 0 + gl.uniform1i(R.progPost2.u_old_color, 0); + + gl.activeTexture(gl.TEXTURE1); + + gl.bindTexture(gl.TEXTURE_2D, R.pass_post1.colorTex); + + gl.uniform1i(R.progPost2.u_color, 1); + + gl.uniform2f(R.progPost2.u_screen_inv, 1.0 / width, 1.0 / height); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progPost2); + }; + + R.pass_postToon1.render = function (state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_postToon1.fbo); + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progPostToon1.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progPost1.u_color uniform to point at texture unit 0 + gl.uniform1i(R.progPostToon1.u_color, 0); + gl.uniform2f(R.progPostToon1.u_screen_inv, 1.0 / width, 1.0 / height); + //console.log("inv: " + 1.0 / width + " " + 1.0 / height); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progPostToon1); + }; + + R.pass_postToon2.render = function (state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progPostToon2.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progPost1.u_color uniform to point at texture unit 0 + gl.uniform1i(R.progPostToon2.u_old_color, 0); + + gl.activeTexture(gl.TEXTURE1); + + gl.bindTexture(gl.TEXTURE_2D, R.pass_postToon1.colorTex); + + gl.uniform1i(R.progPostToon2.u_color, 1); + + gl.uniform2f(R.progPostToon2.u_screen_inv, 1.0 / width, 1.0 / height); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progPostToon2); + }; + + R.pass_blur.render = function (state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + // * Bind the postprocessing shader program + gl.useProgram(R.progBlur.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + gl.uniform1i(R.progBlur.u_color, 0); + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, R.pass_copy.gbufs[0]); + gl.uniform1i(R.progBlur.u_pos, 1); + + if (R.prevMat == null) + { + R.prevMat = new Float32Array(state.cameraMat.elements); + } + gl.uniformMatrix4fv(R.progBlur.u_prev, false, R.prevMat); + + renderFullScreenQuad(R.progBlur); + + R.prevMat = new Float32Array(state.cameraMat.elements); + }; + var renderFullScreenQuad = (function() { // The variables in this function are private to the implementation of // renderFullScreenQuad. They work like static local variables in C++. @@ -230,12 +454,12 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER,vbo); + gl.bindBuffer(gl.ARRAY_BUFFER,vbo); // Upload the positions array to the currently-bound array buffer // using gl.bufferData in static draw mode. // TODO: uncomment - // gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); }; return function(prog) { @@ -249,21 +473,21 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // Enable the bound buffer as the vertex attrib array for // prog.a_position, using gl.enableVertexAttribArray // TODO: uncomment - // gl.enableVertexAttribArray(prog.a_position); + gl.enableVertexAttribArray(prog.a_position); // Use gl.vertexAttribPointer to tell WebGL the type/layout for // prog.a_position's access pattern. // TODO: uncomment - // gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); // Use gl.drawArrays (or gl.drawElements) to draw your quad. // TODO: uncomment - // gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Unbind the array buffer. gl.bindBuffer(gl.ARRAY_BUFFER, null); diff --git a/js/deferredSetup.js b/js/deferredSetup.js index 65136e0..c299563 100644 --- a/js/deferredSetup.js +++ b/js/deferredSetup.js @@ -6,6 +6,10 @@ R.pass_debug = {}; R.pass_deferred = {}; R.pass_post1 = {}; + R.pass_post2 = {}; + R.pass_postToon1 = {}; + R.pass_postToon2 = {}; + R.pass_blur = {}; R.lights = []; R.NUM_GBUFFERS = 4; @@ -18,6 +22,11 @@ loadAllShaderPrograms(); R.pass_copy.setup(); R.pass_deferred.setup(); + R.pass_post1.setup(); + R.pass_post2.setup(); + R.pass_postToon1.setup(); + R.pass_postToon2.setup(); + R.pass_blur.setup(); }; // TODO: Edit if you want to change the light initial positions @@ -25,7 +34,8 @@ R.light_max = [14, 18, 6]; R.light_dt = -0.03; R.LIGHT_RADIUS = 4.0; - R.NUM_LIGHTS = 20; // TODO: test with MORE lights! + R.NUM_LIGHTS = 400; // TODO: test with MORE lights! + //R.NUM_LIGHTS = cfg.numberOfLights; var setupLights = function() { Math.seedrandom(0); @@ -98,6 +108,47 @@ gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; + R.pass_post1.setup = function() + { + R.pass_post1.fbo = gl.createFramebuffer(); + R.pass_post1.colorTex = createAndBindColorTargetTexture( + R.pass_post1.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_post1.fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + } + + R.pass_post2.setup = function () { + R.pass_post2.fbo = gl.createFramebuffer(); + R.pass_post2.colorTex = createAndBindColorTargetTexture( + R.pass_post2.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_post2.fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + } + + R.pass_postToon1.setup = function () { + R.pass_postToon1.fbo = gl.createFramebuffer(); + R.pass_postToon1.colorTex = createAndBindColorTargetTexture( + R.pass_postToon1.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_postToon1.fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + } + + R.pass_postToon2.setup = function () { + R.pass_postToon2.fbo = gl.createFramebuffer(); + R.pass_postToon2.colorTex = createAndBindColorTargetTexture( + R.pass_postToon2.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_postToon2.fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + } + + R.pass_blur.setup = function () { + R.pass_blur.fbo = gl.createFramebuffer(); + R.pass_blur.colorTex = createAndBindColorTargetTexture( + R.pass_blur.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_blur.fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + } + /** * Loads all of the shader programs used in the pipeline. */ @@ -141,6 +192,7 @@ p.u_lightPos = gl.getUniformLocation(p.prog, 'u_lightPos'); p.u_lightCol = gl.getUniformLocation(p.prog, 'u_lightCol'); p.u_lightRad = gl.getUniformLocation(p.prog, 'u_lightRad'); + p.u_toon = gl.getUniformLocation(p.prog, 'u_toon'); R.prog_BlinnPhong_PointLight = p; }); @@ -150,12 +202,49 @@ R.prog_Debug = p; }); + loadPostProgram('old', function (p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + // Save the object into this variable for access later + R.progPostOld = p; + }); + loadPostProgram('one', function(p) { - p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_screen_inv = gl.getUniformLocation(p.prog, 'u_screen_inv'); // Save the object into this variable for access later R.progPost1 = p; }); + loadPostProgram('two', function (p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_old_color = gl.getUniformLocation(p.prog, 'u_old_color'); + p.u_screen_inv = gl.getUniformLocation(p.prog, 'u_screen_inv'); + R.progPost2 = p; + }); + + loadPostProgram('toon1', function (p) + { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_screen_inv = gl.getUniformLocation(p.prog, 'u_screen_inv'); + // Save the object into this variable for access later + R.progPostToon1 = p; + }); + + loadPostProgram('toon2', function (p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_old_color = gl.getUniformLocation(p.prog, 'u_old_color'); + p.u_screen_inv = gl.getUniformLocation(p.prog, 'u_screen_inv'); + R.progPostToon2 = p; + }); + + loadPostProgram('blur', function (p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_prev = gl.getUniformLocation(p.prog, 'u_prev'); + p.u_pos = gl.getUniformLocation(p.prog, 'u_pos'); + R.progBlur = p; + console.log("load blur complete" + p.progBlur + " " + p); + }); + // TODO: If you add more passes, load and set up their shader programs. }; diff --git a/js/framework.js b/js/framework.js index 435bd43..8c4004f 100644 --- a/js/framework.js +++ b/js/framework.js @@ -67,7 +67,8 @@ var width, height; var init = function() { // TODO: For performance measurements, disable debug mode! - var debugMode = true; + //var debugMode = true; + var debugMode = false; canvas = document.getElementById('canvas'); renderer = new THREE.WebGLRenderer({ @@ -269,7 +270,7 @@ var width, height; resize(); - // renderer.render(scene, camera); + //renderer.render(scene, camera); gl.clearColor(0.5, 0.5, 0.5, 0.5); gl.clearDepth(1.0); diff --git a/js/ui.js b/js/ui.js index abd6119..6ccc6ba 100644 --- a/js/ui.js +++ b/js/ui.js @@ -8,6 +8,10 @@ var cfg; this.debugView = -1; this.debugScissor = false; this.enableEffect0 = false; + this.numberOfLights = 20; + this.bloom = false; + this.toon = false; + this.blur = false; }; var init = function() { @@ -25,10 +29,19 @@ var cfg; '5 Surface normal': 5 }); gui.add(cfg, 'debugScissor'); + gui.add(cfg, 'numberOfLights', { + '20': 20, + '40': 40, + '60': 60, + '80': 80, + '100': 100 + }); - var eff0 = gui.addFolder('EFFECT NAME HERE'); + var eff0 = gui.addFolder('Effects'); eff0.open(); - eff0.add(cfg, 'enableEffect0'); + eff0.add(cfg, 'bloom'); + eff0.add(cfg, 'toon'); + eff0.add(cfg, 'blur'); // TODO: add more effects toggles and parameters here }; diff --git a/server.py b/server.py index 17ff1a6..78a3dc2 100755 --- a/server.py +++ b/server.py @@ -8,7 +8,7 @@ from http.server import SimpleHTTPRequestHandler from socketserver import TCPServer -PORT = 10565 +PORT = 10632 Handler = SimpleHTTPRequestHandler