diff --git a/README.md b/README.md index 25002db..2ab75d6 100644 --- a/README.md +++ b/README.md @@ -3,26 +3,96 @@ 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) +* Liang Peng +* Tested on: **54.0.2840.87 m (64-bit)** on + Windows 10, i7-6700HQ @ 2.6GHz 8GB, GTX 960 (Personal Laptop) ### Live Online -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +[Click Me!](http://itoupeter.github.io/Project5-WebGL-Deferred-Shading-with-glTF/) ### Demo Video/GIF -[![](img/video.png)](TODO) +![](img/bloom_on.gif) -### (TODO: Your README) +### Features -*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. +* [x] Basic Pipeline + * [x] Render to G-Buffer + * [x] Deferred Shading +* [x] Scissor Test + * [x] Scissor Mask Visualization +* [x] Post Effect + * [x] Sky Color + * [x] Bloom Effect +* [x] Performance Analysis -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! +### Basic Pipeline +Depth | Position | Normal +--- | --- | --- +![](img/depth.PNG) | ![](img/position.png) | ![](img/normal.png) + +Color Map | Normal Map | Surface Normal +--- | --- | --- +![](img/color.png) | ![](img/normal_map.png) | ![](img/surface_normal.png) + +Ambient Lighting | Blinn-Phong Lighting +--- | --- +![](img/ambient.png) | ![](img/blinn_phong.gif) + +Bloom OFF | Bloom ON +--- | --- +![](img/bloom_off.gif) | ![](img/bloom_on.gif) + +__Scissor Mask Visualization__ + +![](img/scissor_mask.gif) + +### Performance Analysis + +#### Lighting + +Ambient | Blinn-Phong | Ambient + Blinn-Phong +:---:|:---:|:---: +16 ms/frame | 60 ms/frame | 65 ms/frame + +![](img/perf_lighting.png) + +_*Note_ Ambient lighting is present due to infinite light bouncing in the space, which finally lights every object in the scene evenly (from all angle). Blinn-Phong light consists of, in the simpliest case, two types of lighting, lambert diffuse and specular. Diffuse reflectance is proportional to _dot product of surface normal and light direction_, while specular reflectance is proportional to _dot prodect of surface normal and halfway direction raised to power of shininess_, where halfway direction is the bisectional direction of light direction and view direction. + +#### Number of Lights + +Num | 1 | 4 | 16 | 64 | 256 | 1024 +:---:|:---:|:---:|:---:|:---:|:---:|:---: +ms/frame | 20 | 25 | 50 | 190 | 640 | - +FPS | 55 | 40 | 20 | 6 | 2 | - +_Note*_ Data above are measured __without scissor test__. + +Num | 1 | 4 | 16 | 64 | 256 | 1024 +:---:|:---:|:---:|:---:|:---:|:---:|:---: +ms/frame | 19 | 24 | 28 | 60 | 200 | 800 +FPS | 60 | 57 | 40 | 16 | 5 | 1 +_Note*_ Data above are measured __with scissor test__. + +#### Scissor Test + +Scissor Test OFF | Scissor Test ON +:---:|:---: +![](img/scissor_off.gif) | ![](img/scissor_on.gif) +_Note*_ Scissor box calculation is not accurate enough, thus results in noticeable artifacts. + +![](img/perf_scissor.png) + +_Note*_ With scissor test turned on, only pixels close enough to a particular light for which lighting will be computed, thus a considerable performance gain can be noticed in the figure. + +#### Bloom + +Bloom OFF | Bloom ON +:---:|:---: +36 ms/frame | 40 ms/frame + +_*Note_ To achieve bloom effect, we first extract bright color from original color, then bleed the bright color of each pixel into its neighboring pixels, subject to a gaussian distribution. Commonly we will first do the color bleeding first in horizontal direction, then vertical direction. Finally we composite the blurred bright color and the original color to achieve bloom effect. ### Credits diff --git a/glsl/copy.frag.glsl b/glsl/copy.frag.glsl index 823ebcd..2d2da97 100644 --- a/glsl/copy.frag.glsl +++ b/glsl/copy.frag.glsl @@ -16,5 +16,8 @@ void main() { // 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.); + gl_FragData[1] = vec4(v_normal, 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..35d2cd9 100644 --- a/glsl/deferred/ambient.frag.glsl +++ b/glsl/deferred/ambient.frag.glsl @@ -16,12 +16,14 @@ void main() { vec4 gb2 = texture2D(u_gbufs[2], 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 + + // extract properties from G Buffer + vec3 colmap = gb2.rgb; if (depth == 1.0) { gl_FragColor = vec4(0, 0, 0, 0); // set alpha to 0 return; } - gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + gl_FragColor = vec4(colmap * .1, 1); } diff --git a/glsl/deferred/blinnphong-pointlight.frag.glsl b/glsl/deferred/blinnphong-pointlight.frag.glsl index b24a54a..51530b0 100644 --- a/glsl/deferred/blinnphong-pointlight.frag.glsl +++ b/glsl/deferred/blinnphong-pointlight.frag.glsl @@ -26,7 +26,20 @@ void main() { vec4 gb2 = texture2D(u_gbufs[2], 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 colmap = gb2.rgb; + vec3 normap = gb3.xyz; + vec3 N = applyNormalMap(geomnor, normap); + vec3 E = vec3(0, 0, 1); + vec3 L = normalize(u_lightPos - pos); + vec3 H = normalize(E + L); + vec3 diffuse; + vec3 specular; + float dist = distance(pos, u_lightPos); + float attenuation = u_lightRad - dist; // If nothing was rendered to this pixel, set alpha to 0 so that the // postprocessing step can render the sky color. @@ -35,5 +48,13 @@ void main() { return; } - gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + // light too far away + if (dist >= u_lightRad) { + return; + } + + diffuse = u_lightCol * colmap * max(0., dot(N, L)) * attenuation; + specular = u_lightCol * pow(max(0., dot(N, H)), 1000.) * attenuation; + + gl_FragColor = vec4(diffuse + specular, 1.); } diff --git a/glsl/deferred/debug.frag.glsl b/glsl/deferred/debug.frag.glsl index 007466f..d3b0d5e 100644 --- a/glsl/deferred/debug.frag.glsl +++ b/glsl/deferred/debug.frag.glsl @@ -32,21 +32,21 @@ void main() { 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 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); } 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); } else if (u_debug == 5) { - // gl_FragColor = vec4(abs(nor), 1.0); + gl_FragColor = vec4(abs(nor), 1.0); } else { gl_FragColor = vec4(1, 0, 1, 1); } diff --git a/glsl/post/add.frag.glsl b/glsl/post/add.frag.glsl new file mode 100644 index 0000000..334f9e3 --- /dev/null +++ b/glsl/post/add.frag.glsl @@ -0,0 +1,16 @@ +#version 100 +#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform sampler2D u_bright; + +varying vec2 v_uv; + +void main() { + vec4 color = texture2D(u_color, v_uv); + vec4 bright = texture2D(u_bright, v_uv); + + gl_FragColor = color + bright; +} diff --git a/glsl/post/blur.frag.glsl b/glsl/post/blur.frag.glsl new file mode 100644 index 0000000..1c8d6df --- /dev/null +++ b/glsl/post/blur.frag.glsl @@ -0,0 +1,35 @@ +#version 100 +#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; + +uniform sampler2D u_bright; +uniform bool u_horizontal; +uniform vec4 u_weight; +uniform vec2 u_screenSize; + +varying vec2 v_uv; + +void main() { + vec4 brightColor = texture2D(u_bright, v_uv); + vec2 offset = 1. / u_screenSize; + vec3 result = texture2D(u_bright, v_uv).rgb * 0.227027; + + if (u_horizontal) { + float os = 0.; + for (int i = 1; i < 5; ++i) { + os += offset.x; + result += texture2D(u_bright, v_uv + vec2(os, 0.)).rgb * u_weight[i - 1]; + result += texture2D(u_bright, v_uv - vec2(os, 0.)).rgb * u_weight[i - 1]; + } + } else { + float os = 0.; + for (int i = 1; i < 5; ++i) { + os += offset.y; + result += texture2D(u_bright, v_uv + vec2(0., os)).rgb * u_weight[i - 1]; + result += texture2D(u_bright, v_uv - vec2(0., os)).rgb * u_weight[i - 1]; + } + } + + gl_FragColor = vec4(result, 0.); +} diff --git a/glsl/post/one.frag.glsl b/glsl/post/one.frag.glsl index 94191cd..25ff50f 100644 --- a/glsl/post/one.frag.glsl +++ b/glsl/post/one.frag.glsl @@ -1,4 +1,5 @@ #version 100 +#extension GL_EXT_draw_buffers: enable precision highp float; precision highp int; @@ -12,9 +13,17 @@ void main() { vec4 color = texture2D(u_color, v_uv); if (color.a == 0.0) { - gl_FragColor = SKY_COLOR; - return; - } + gl_FragData[0] = SKY_COLOR; + } else { + gl_FragData[0] = color; + } - gl_FragColor = color; + // output bright color + float brightness = dot(gl_FragData[0].rgb, vec3(.2126, .7152, .0722)); + + if (brightness > 1.) { + gl_FragData[1] = gl_FragData[0]; + } else { + gl_FragData[1] = vec4(0.); + } } diff --git a/glsl/red.frag.glsl b/glsl/red.frag.glsl index f8ef1ec..641561e 100644 --- a/glsl/red.frag.glsl +++ b/glsl/red.frag.glsl @@ -2,6 +2,8 @@ precision highp float; precision highp int; +uniform vec4 u_color; + void main() { - gl_FragColor = vec4(1, 0, 0, 1); + gl_FragColor = u_color; } diff --git a/img/ambient.png b/img/ambient.png new file mode 100644 index 0000000..33fef4d Binary files /dev/null and b/img/ambient.png differ diff --git a/img/blinn_phong.gif b/img/blinn_phong.gif new file mode 100644 index 0000000..40a05a8 Binary files /dev/null and b/img/blinn_phong.gif differ diff --git a/img/bloom_off.gif b/img/bloom_off.gif new file mode 100644 index 0000000..6e9cf72 Binary files /dev/null and b/img/bloom_off.gif differ diff --git a/img/bloom_on.gif b/img/bloom_on.gif new file mode 100644 index 0000000..6b4b371 Binary files /dev/null and b/img/bloom_on.gif differ diff --git a/img/color.png b/img/color.png new file mode 100644 index 0000000..9003701 Binary files /dev/null and b/img/color.png differ diff --git a/img/depth.PNG b/img/depth.PNG new file mode 100644 index 0000000..cca1623 Binary files /dev/null and b/img/depth.PNG differ diff --git a/img/dummy.jpg b/img/dummy.jpg new file mode 100644 index 0000000..6fbab77 Binary files /dev/null and b/img/dummy.jpg differ diff --git a/img/normal.png b/img/normal.png new file mode 100644 index 0000000..f46f9c8 Binary files /dev/null and b/img/normal.png differ diff --git a/img/normal_map.png b/img/normal_map.png new file mode 100644 index 0000000..71dfc07 Binary files /dev/null and b/img/normal_map.png differ diff --git a/img/perf_lighting.png b/img/perf_lighting.png new file mode 100644 index 0000000..d1ef6c3 Binary files /dev/null and b/img/perf_lighting.png differ diff --git a/img/perf_scissor.png b/img/perf_scissor.png new file mode 100644 index 0000000..a65b82e Binary files /dev/null and b/img/perf_scissor.png differ diff --git a/img/position.png b/img/position.png new file mode 100644 index 0000000..ddabf8c Binary files /dev/null and b/img/position.png differ diff --git a/img/scissor_mask.gif b/img/scissor_mask.gif new file mode 100644 index 0000000..817678a Binary files /dev/null and b/img/scissor_mask.gif differ diff --git a/img/scissor_off.gif b/img/scissor_off.gif new file mode 100644 index 0000000..9e8e82c Binary files /dev/null and b/img/scissor_off.gif differ diff --git a/img/scissor_on.gif b/img/scissor_on.gif new file mode 100644 index 0000000..20732b3 Binary files /dev/null and b/img/scissor_on.gif differ diff --git a/img/surface_normal.png b/img/surface_normal.png new file mode 100644 index 0000000..db6ab66 Binary files /dev/null and b/img/surface_normal.png differ diff --git a/index.html b/index.html index 7ada197..798819e 100644 --- a/index.html +++ b/index.html @@ -101,7 +101,7 @@ DEBUG MODE! (Disable before measuring performance.)
- +
diff --git a/js/deferredRender.js b/js/deferredRender.js index bb3edd4..983eb94 100644 --- a/js/deferredRender.js +++ b/js/deferredRender.js @@ -10,7 +10,9 @@ !R.prog_Ambient || !R.prog_BlinnPhong_PointLight || !R.prog_Debug || - !R.progPost1)) { + !R.progPost1_1 || + !R.progPost1_2 || + !R.progPost1_3)) { console.log('waiting for programs to load...'); return; } @@ -26,26 +28,30 @@ // Execute deferred shading pipeline // CHECKITOUT: START HERE! You can even uncomment this: - //debugger; + // 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: test rendering red screen + // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + // gl.useProgram(R.progRed.prog); + // gl.uniform4fv(R.progRed.u_color, [1., 0., 0., 1.]); + // renderFullScreenQuad(R.progRed); + // return; + // } R.pass_copy.render(state); if (cfg && cfg.debugView >= 0) { - // Do a debug render instead of a regular render + // Do a debug render in stead of a regular render // Don't do any post-processing in debug mode R.pass_debug.render(state); } 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); + R.pass_post1.render(state); + if (cfg && cfg.debugScissor) { + R.pass_scissor.render(state); + } // OPTIONAL TODO: call more postprocessing passes, if any } @@ -57,21 +63,20 @@ 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 +84,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) { @@ -93,7 +98,6 @@ // If you want to render one model many times, note: // readyModelForDraw only needs to be called once. readyModelForDraw(R.progCopy, m); - drawReadyModel(m); } }; @@ -101,17 +105,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); }; /** @@ -130,12 +134,12 @@ // Enable blending and use gl.blendFunc to blend with: // color = 1 * src_color + 1 * dst_color - // Here is a wonderful demo of showing how blend function works: + // 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,6 +151,19 @@ // 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). + gl.enable(gl.SCISSOR_TEST); + for (var i = 0; i < R.NUM_LIGHTS; ++i) { + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + + if (!sc) continue; + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightPos, R.lights[i].pos); + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightCol, R.lights[i].col); + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, R.lights[i].rad); + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + + } + 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. @@ -179,32 +196,77 @@ * '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); - - // * 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.progPost1.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.progPost1.u_color, 0); - - // * Render a fullscreen quad to perform shading on - renderFullScreenQuad(R.progPost1); + // compute color and extract bright color + // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_post1.fbo1); + gl.useProgram(R.progPost1_1.prog); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + gl.uniform1i(R.progPost1_1.u_color, 0); + renderFullScreenQuad(R.progPost1_1); + + // compute gaussian blur in horizontal direction + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_post1.fbo2); + gl.useProgram(R.progPost1_2.prog); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_post1.brightTex); + gl.uniform1i(R.progPost1_2.u_bright, 0); + gl.uniform1i(R.progPost1_2.u_horizontal, 1); + gl.uniform4f(R.progPost1_2.u_weight, 0.1945946, 0.1216216, 0.054054, 0.016216); + gl.uniform2f(R.progPost1_2.u_screenSize, R.width, R.height); + renderFullScreenQuad(R.progPost1_2); + + // compute gaussian blur in vertical direction + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_post1.fbo3); + gl.bindTexture(gl.TEXTURE_2D, R.pass_post1.blurredTex); + gl.uniform1i(R.progPost1_2.u_horizontal, 0); + renderFullScreenQuad(R.progPost1_2); + + // combine color and blurred bright color + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.useProgram(R.progPost1_3.prog); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_post1.colorTex); + gl.uniform1i(R.progPost1_3.u_color, 0); + gl.activeTexture(gl.TEXTURE1); + if (cfg && cfg.enableEffect0) { + gl.bindTexture(gl.TEXTURE_2D, R.pass_post1.brightTex); + } else { + gl.bindTexture(gl.TEXTURE_2D, null); + } + gl.uniform1i(R.progPost1_3.u_bright, 1); + renderFullScreenQuad(R.progPost1_3); }; + R.pass_scissor.render = function(state) { + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.useProgram(R.progRed.prog); + gl.enable(gl.BLEND); + gl.blendEquation(gl.FUNC_ADD); + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + for (var i = 0; i < R.NUM_LIGHTS; ++i) { + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + + if (!sc) continue; + + var minX = sc[0] / R.width * 2. - 1.; + var maxX = (sc[0] + sc[2]) / R.width * 2. - 1.; + var minY = sc[1] / R.height * 2. - 1.; + var maxY = (sc[1] + sc[3]) / R.height * 2. - 1.; + var rect = new Float32Array([ + minX, minY, 0., + maxX, minY, 0., + minX, maxY, 0., + maxX, maxY, 0. + ]); + + gl.useProgram(R.progRed.prog); + gl.uniform4fv(R.progRed.u_color, [1., 0., 0., .05]); + renderFullScreenQuad(R.progRed, rect); + } + gl.disable(gl.BLEND); + }; + var renderFullScreenQuad = (function() { // The variables in this function are private to the implementation of // renderFullScreenQuad. They work like static local variables in C++. @@ -230,16 +292,16 @@ // 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) { - if (!vbo) { + return function(prog, rect = null) { + if (!vbo) { // If the vbo hasn't been initialized, initialize it. init(); } @@ -248,22 +310,30 @@ gl.useProgram(prog.prog); // Bind the VBO as the gl.ARRAY_BUFFER - // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + // TODO: uncommentif (rect) { + if (rect) { + var vbo_rect = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, vbo_rect); + gl.bufferData(gl.ARRAY_BUFFER, rect, gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo_rect); + } else { + 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..94e398a 100644 --- a/js/deferredSetup.js +++ b/js/deferredSetup.js @@ -6,6 +6,7 @@ R.pass_debug = {}; R.pass_deferred = {}; R.pass_post1 = {}; + R.pass_scissor = {}; R.lights = []; R.NUM_GBUFFERS = 4; @@ -14,10 +15,11 @@ * Set up the deferred pipeline framebuffer objects and textures. */ R.deferredSetup = function() { - setupLights(); - loadAllShaderPrograms(); + setupLights(); + loadAllShaderPrograms(); R.pass_copy.setup(); R.pass_deferred.setup(); + R.pass_post1.setup(); }; // TODO: Edit if you want to change the light initial positions @@ -98,6 +100,45 @@ gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; + /** + * Create/configure framebuffer between "deferred" and "post1" stages + */ + R.pass_post1.setup = function() { + // framebuffer for bright color + R.pass_post1.fbo1 = gl.createFramebuffer(); + R.pass_post1.colorTex = createAndBindColorTargetTexture( + R.pass_post1.fbo1, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + R.pass_post1.brightTex = createAndBindColorTargetTexture( + R.pass_post1.fbo1, gl_draw_buffers.COLOR_ATTACHMENT1_WEBGL); + + // * Check for framebuffer errors + abortIfFramebufferIncomplete(R.pass_post1.fbo1); + + // * Tell the WEBGL_draw_buffers extension which FBO attachments are + // being used. (This extension allows for multiple render targets.) + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL, + gl_draw_buffers.COLOR_ATTACHMENT1_WEBGL]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // framebuffer for gaussian blur horizontal + R.pass_post1.fbo2 = gl.createFramebuffer(); + R.pass_post1.blurredTex = createAndBindColorTargetTexture( + R.pass_post1.fbo2, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(R.pass_post1.fbo2); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // framebuffer for gaussian blur vertical + R.pass_post1.fbo3 = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_post1.fbo3); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL, + gl.TEXTURE_2D, R.pass_post1.brightTex, 0); + abortIfFramebufferIncomplete(R.pass_post1.fbo3); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + }; + /** * Loads all of the shader programs used in the pipeline. */ @@ -122,7 +163,10 @@ loadShaderProgram(gl, 'glsl/quad.vert.glsl', 'glsl/red.frag.glsl', function(prog) { // Create an object to hold info about this shader program - R.progRed = { prog: prog }; + var p = { prog: prog }; + + p.u_color = gl.getUniformLocation(prog, 'u_color'); + R.progRed = p; }); loadShaderProgram(gl, 'glsl/quad.vert.glsl', 'glsl/clear.frag.glsl', @@ -153,7 +197,23 @@ loadPostProgram('one', function(p) { p.u_color = gl.getUniformLocation(p.prog, 'u_color'); // Save the object into this variable for access later - R.progPost1 = p; + R.progPost1_1 = p; + }); + + loadPostProgram('blur', function(p) { + p.u_bright = gl.getUniformLocation(p.prog, 'u_bright'); + p.u_horizontal = gl.getUniformLocation(p.prog, 'u_horizontal'); + p.u_weight = gl.getUniformLocation(p.prog, 'u_weight'); + p.u_screenSize = gl.getUniformLocation(p.prog, 'u_screenSize'); + // Save the object into this variable for access later + R.progPost1_2 = p; + }); + + loadPostProgram('add', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_bright = gl.getUniformLocation(p.prog, 'u_bright'); + // Save the object into this variable for access later + R.progPost1_3 = 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 4f944ee..dcab17e 100644 --- a/js/framework.js +++ b/js/framework.js @@ -67,7 +67,7 @@ var width, height; var init = function() { // TODO: For performance measurements, disable debug mode! - var debugMode = true; + var debugMode = false; canvas = document.getElementById('canvas'); renderer = new THREE.WebGLRenderer({ @@ -96,8 +96,8 @@ var width, height; scene = new THREE.Scene(); - width = canvas.width; - height = canvas.height; + R.width = width = canvas.width; + R.height = height = canvas.height; camera = new THREE.PerspectiveCamera( 45, // Field of view width / height, // Aspect ratio @@ -123,7 +123,7 @@ var width, height; }); // var glTFURL = 'models/glTF-duck/duck.gltf'; - var glTFURL = 'models/glTF-sponza-kai-fix/sponza.gltf'; + var glTFURL = 'models/gltf-sponza-kai-fix/sponza.gltf'; var glTFLoader = new MinimalGLTFLoader.glTFLoader(gl); glTFLoader.loadGLTF(glTFURL, function (glTF) { var curScene = glTF.scenes[glTF.defaultScene]; @@ -187,8 +187,8 @@ var width, height; gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magFilter); gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrapS); gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrapT); - if (minFilter == gl.NEAREST_MIPMAP_NEAREST || - minFilter == gl.NEAREST_MIPMAP_LINEAR || + if (minFilter == gl.NEAREST_MIPMAP_NEAREST || + minFilter == gl.NEAREST_MIPMAP_LINEAR || minFilter == gl.LINEAR_MIPMAP_NEAREST || minFilter == gl.LINEAR_MIPMAP_LINEAR ) { gl.generateMipmap(target); @@ -245,7 +245,7 @@ var width, height; uvInfo: {size: uvInfo.size, type: uvInfo.type, stride: uvInfo.stride, offset: uvInfo.offset}, // specific textures temp test - colmap: webGLTextures[colorTextureName].texture, + colmap: webGLTextures[colorTextureName].texture, normap: webGLTextures[normalTextureName].texture }); @@ -254,7 +254,7 @@ var width, height; } - + }); diff --git a/js/ui.js b/js/ui.js index abd6119..196ed3e 100644 --- a/js/ui.js +++ b/js/ui.js @@ -26,7 +26,7 @@ var cfg; }); gui.add(cfg, 'debugScissor'); - var eff0 = gui.addFolder('EFFECT NAME HERE'); + var eff0 = gui.addFolder('Bloom'); eff0.open(); eff0.add(cfg, 'enableEffect0'); // TODO: add more effects toggles and parameters here