From a952093f3797cc8dd3998e780d9eb91dfc296fd3 Mon Sep 17 00:00:00 2001 From: XJMa Date: Wed, 12 Nov 2014 15:20:58 -0500 Subject: [PATCH 01/14] finish basic --- assets/deferred/diffuse.frag | 34 +- assets/deferred/diffuse.frag.bak | 49 ++ assets/shader/deferred/diffuse.frag | 34 +- assets/shader/deferred/diffuse.frag.bak | 49 ++ assets/shader/deferred/post.frag | 105 ++++- assets/shader/deferred/post.frag.bak | 118 +++++ js/core/fbo-util.js.bak | 200 ++++++++ js/main.js | 128 +++++- js/main.js.bak | 576 ++++++++++++++++++++++++ js/screenQuad.js | 6 +- screenshots/bloom.jpg | Bin 0 -> 36225 bytes screenshots/diffuseSpec.jpg | Bin 0 -> 30672 bytes screenshots/toon.jpg | Bin 0 -> 37512 bytes 13 files changed, 1265 insertions(+), 34 deletions(-) create mode 100644 assets/deferred/diffuse.frag.bak create mode 100644 assets/shader/deferred/diffuse.frag.bak create mode 100644 assets/shader/deferred/post.frag.bak create mode 100644 js/core/fbo-util.js.bak create mode 100644 js/main.js.bak create mode 100644 screenshots/bloom.jpg create mode 100644 screenshots/diffuseSpec.jpg create mode 100644 screenshots/toon.jpg diff --git a/assets/deferred/diffuse.frag b/assets/deferred/diffuse.frag index ef0c5fc..02b245d 100644 --- a/assets/deferred/diffuse.frag +++ b/assets/deferred/diffuse.frag @@ -8,16 +8,42 @@ uniform sampler2D u_depthTex; uniform float u_zFar; uniform float u_zNear; uniform int u_displayType; +uniform vec4 u_Light; varying vec2 v_texcoord; float linearizeDepth( float exp_depth, float near, float far ){ - return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } void main() { - // Write a diffuse shader and a Blinn-Phong shader - // NOTE : You may need to add your own normals to fulfill the second's requirements - gl_FragColor = vec4(texture2D(u_colorTex, v_texcoord).rgb, 1.0); + + // Diffuse calculation + vec4 lightColor = vec4(0.5, 0.5, 0.5, 1.0); + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + vec3 lightDir = normalize(u_Light.xyz - position); + vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); + float specular = 0.0; + + + vec3 viewDir = normalize(-position); + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, 80.0); + + + //change background color + float depth = texture2D( u_depthTex, v_texcoord ).x; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + + if (depth > 0.99) { + gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); + } else { + gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); + } + } diff --git a/assets/deferred/diffuse.frag.bak b/assets/deferred/diffuse.frag.bak new file mode 100644 index 0000000..7d1f3d9 --- /dev/null +++ b/assets/deferred/diffuse.frag.bak @@ -0,0 +1,49 @@ +precision highp float; + +uniform sampler2D u_positionTex; +uniform sampler2D u_normalTex; +uniform sampler2D u_colorTex; +uniform sampler2D u_depthTex; + +uniform float u_zFar; +uniform float u_zNear; +uniform int u_displayType; +uniform vec4 u_Light; + +varying vec2 v_texcoord; + +float linearizeDepth( float exp_depth, float near, float far ){ + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); +} + +void main() +{ + + // Diffuse calculation + vec4 lightColor = vec4(0.5, 0.5, 0.5, 1.0); + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + vec3 lightDir = normalize(u_Light.xyz - position); + vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); + float specular = 0.0; + + + vec3 viewDir = normalize(-position); + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, 80.0); + + + //change background color + float depth = texture2D( u_depthTex, v_texcoord ).x; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + + if (depth > 0.99) { + gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); + } else { + //gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); + } + +} diff --git a/assets/shader/deferred/diffuse.frag b/assets/shader/deferred/diffuse.frag index ef0c5fc..6792c89 100644 --- a/assets/shader/deferred/diffuse.frag +++ b/assets/shader/deferred/diffuse.frag @@ -8,16 +8,42 @@ uniform sampler2D u_depthTex; uniform float u_zFar; uniform float u_zNear; uniform int u_displayType; +uniform vec4 u_Light; varying vec2 v_texcoord; float linearizeDepth( float exp_depth, float near, float far ){ - return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } void main() { - // Write a diffuse shader and a Blinn-Phong shader - // NOTE : You may need to add your own normals to fulfill the second's requirements - gl_FragColor = vec4(texture2D(u_colorTex, v_texcoord).rgb, 1.0); + + // Diffuse calculation + vec4 lightColor = vec4(0.5, 0.5, 0.5, 1.0); + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + vec3 lightDir = normalize(u_Light.xyz - position); + vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); + float specular = 0.0; + + + vec3 viewDir = normalize(-position); + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, 80.0); + + + //change background color + float depth = texture2D( u_depthTex, v_texcoord ).x; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + + if (depth > 0.99 && u_displayType == 6) { + gl_FragColor = vec4(vec3(0.8), 1.0); + } else { + gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(0.7), 1.0); + } + } diff --git a/assets/shader/deferred/diffuse.frag.bak b/assets/shader/deferred/diffuse.frag.bak new file mode 100644 index 0000000..d9d8446 --- /dev/null +++ b/assets/shader/deferred/diffuse.frag.bak @@ -0,0 +1,49 @@ +precision highp float; + +uniform sampler2D u_positionTex; +uniform sampler2D u_normalTex; +uniform sampler2D u_colorTex; +uniform sampler2D u_depthTex; + +uniform float u_zFar; +uniform float u_zNear; +uniform int u_displayType; +uniform vec4 u_Light; + +varying vec2 v_texcoord; + +float linearizeDepth( float exp_depth, float near, float far ){ + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); +} + +void main() +{ + + // Diffuse calculation + vec4 lightColor = vec4(0.5, 0.5, 0.5, 1.0); + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + vec3 lightDir = normalize(u_Light.xyz - position); + vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); + float specular = 0.0; + + + vec3 viewDir = normalize(-position); + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, 80.0); + + + //change background color + float depth = texture2D( u_depthTex, v_texcoord ).x; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + + if (depth > 0.99) { + gl_FragColor = vec4(vec3(u_displayType == 6 ? 0.0 : 0.8), 1.0); + } else { + gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(0.7), 1.0); + } + +} diff --git a/assets/shader/deferred/post.frag b/assets/shader/deferred/post.frag index 52edda2..2eb65c1 100644 --- a/assets/shader/deferred/post.frag +++ b/assets/shader/deferred/post.frag @@ -1,17 +1,118 @@ precision highp float; uniform sampler2D u_shadeTex; +uniform sampler2D u_colorTex; +uniform sampler2D u_positionTex; +uniform sampler2D u_normalTex; +uniform sampler2D u_depthTex; + +uniform float u_zFar; +uniform float u_zNear; + +uniform int u_width; +uniform int u_height; +uniform int u_displayType; +uniform vec3 u_kernel[100]; + varying vec2 v_texcoord; +#define KERNEL_SIZE 25 // has to be an odd number +#define DISPLAY_TOON 7 +#define DISPLAY_BLOOM 6 +#define DISPLAY_BLINN 5 +#define DISPLAY_SSAO 8 +#define DISPLAY_BLUR 9 + +float w = 1.0 / float(u_width); +float h = 1.0 / float(u_height); float linearizeDepth( float exp_depth, float near, float far ){ - return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); +} + +float gaussian2d(int x,int y,int n) { + float sigma = 2.0; + float fx = float(x) - (float(n) - 1.0) / 2.0; + float fy = float(y) - (float(n) - 1.0) / 2.0; + return (exp(-abs(fx*fy)/ (2.0*sigma*sigma)))/ (2.0 * 3.1415926 *sigma*sigma); } +vec4 bloomShader() { + + float width = (float(KERNEL_SIZE) - 1.0) / 2.0; + vec3 color = vec3(0.0,0.0,0.0); + for(int i=0; i< KERNEL_SIZE; i++){ + for(int j=0; j< KERNEL_SIZE; j++){ + vec2 tc = v_texcoord; + tc.y += (float(i)-width)*h; + tc.x += (float(j)-width)*w; + color += gaussian2d(i,j,KERNEL_SIZE) * texture2D(u_shadeTex, tc).rgb; + } + } + + return vec4(color,1.0); +} +//This function blurs the depth buffer and subtracts it from the original (a high pass filter) +float round(float f, int num) { + return floor(float(num+1)*f)/float(num); +} +float detectEdge() { + float result = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + result -= linearizeDepth(texture2D(u_depthTex, v_texcoord + vec2(w*float(i), h*float(j))).x, u_zNear, u_zFar)/(81.0); + } + } + return result; +} +vec4 toonShader(vec3 color, int numColors) { + // Flatten the color + vec3 p_color = vec3(round(color.r, numColors), round(color.g, numColors), round(color.b, numColors)); + // Sharpen the edges + return abs(detectEdge()) > 0.005 ? vec4(vec3(0.0), 1.0) : vec4(p_color, 1.0); +} + +vec4 SSAO(vec3 color) { + float occlusion = 0.0; + + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + + vec3 rvec = vec3(1.0, 0.0, 0.0); + vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + float thisDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); + + for (int i = 0; i < 100; i++) { + vec3 sample = tbn * u_kernel[i]; + vec2 offset = sample.xy; + offset.x *= w; + offset.y *= h; + + float sampleDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord + offset.xy).x, u_zNear, u_zFar); + + occlusion += (thisDepth - sample.z > sampleDepth) ? 1.0 : 0.0; + } + + occlusion = 1.0 - (occlusion / float(100)); + + return vec4(vec3(occlusion), 1.0); +} void main() { // Currently acts as a pass filter that immmediately renders the shaded texture // Fill in post-processing as necessary HERE // NOTE : You may choose to use a key-controlled switch system to display one feature at a time - gl_FragColor = vec4(texture2D( u_shadeTex, v_texcoord).rgb, 1.0); + vec3 color = texture2D( u_shadeTex, v_texcoord).rgb; + if(u_displayType == DISPLAY_BLINN) + gl_FragColor = vec4(color, 1.0); + if(u_displayType == DISPLAY_BLOOM) + gl_FragColor = bloomShader(); + if(u_displayType == DISPLAY_TOON) + gl_FragColor = toonShader(color, 3); + if(u_displayType == DISPLAY_SSAO) + gl_FragColor = SSAO(color); + } diff --git a/assets/shader/deferred/post.frag.bak b/assets/shader/deferred/post.frag.bak new file mode 100644 index 0000000..ed7d8af --- /dev/null +++ b/assets/shader/deferred/post.frag.bak @@ -0,0 +1,118 @@ +precision highp float; + +uniform sampler2D u_shadeTex; +uniform sampler2D u_colorTex; +uniform sampler2D u_positionTex; +uniform sampler2D u_normalTex; +uniform sampler2D u_depthTex; + +uniform float u_zFar; +uniform float u_zNear; + +uniform int u_width; +uniform int u_height; +uniform int u_displayType; +uniform vec3 u_kernel[100]; + + +varying vec2 v_texcoord; + +#define KERNEL_SIZE 25 // has to be an odd number +#define DISPLAY_TOON 7 +#define DISPLAY_BLOOM 6 +#define DISPLAY_BLINN 5 +#define DISPLAY_SSAO 8 +#define DISPLAY_BLUR 9 + +float w = 1.0 / float(u_width); +float h = 1.0 / float(u_height); +float linearizeDepth( float exp_depth, float near, float far ){ + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); +} + +float gaussian2d(int x,int y,int n) { + float sigma = 2.0; + float fx = float(x) - (float(n) - 1.0) / 2.0; + float fy = float(y) - (float(n) - 1.0) / 2.0; + return (exp(-abs(fx*fy)/ (2.0*sigma*sigma)))/ (2.0 * 3.1415926 *sigma*sigma); +} + +vec4 bloomShader() { + + float width = (float(KERNEL_SIZE) - 1.0) / 2.0; + vec3 color = vec3(0.0,0.0,0.0); + for(int i=0; i< KERNEL_SIZE; i++){ + for(int j=0; j< KERNEL_SIZE; j++){ + vec2 tc = v_texcoord; + tc.y += (float(i)-width)*h; + tc.x += (float(j)-width)*w; + color += gaussian2d(i,j,KERNEL_SIZE) * texture2D(u_shadeTex, tc).rgb; + } + } + + return vec4(color,1.0); +} +//This function blurs the depth buffer and subtracts it from the original (a high pass filter) +float round(float f, int num) { + return floor(float(num+1)*f)/float(num); +} +float detectEdge() { + float result = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); + for (int i = -4; i <= 4; i++) { + for (int j = -4; j <= 4; j++) { + result -= linearizeDepth(texture2D(u_depthTex, v_texcoord + vec2(w*float(i), h*float(j))).x, u_zNear, u_zFar)/(81.0); + } + } + return result; +} +vec4 toonShader(vec3 color, int numColors) { + // Flatten the color + vec3 p_color = vec3(round(color.r, numColors), round(color.g, numColors), round(color.b, numColors)); + // Sharpen the edges + return abs(detectEdge()) > 0.005 ? vec4(vec3(0.0), 1.0) : vec4(p_color, 1.0); +} + +vec4 SSAO(vec3 color) { + float occlusion = 0.0; + + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + + vec3 rvec = vec3(1.0, 0.0, 0.0); + vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + float thisDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); + + for (int i = 0; i < 100; i++) { + vec3 sample = tbn * u_kernel[i]; + vec2 offset = sample.xy; + offset.x *= w; + offset.y *= h; + + float sampleDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord + offset.xy).x, u_zNear, u_zFar); + + occlusion += (thisDepth - sample.z > sampleDepth) ? 1.0 : 0.0; + } + + occlusion = 1.0 - (occlusion / float(100)); + + return vec4(vec3(occlusion), 1.0); +} +void main() +{ + // Currently acts as a pass filter that immmediately renders the shaded texture + // Fill in post-processing as necessary HERE + // NOTE : You may choose to use a key-controlled switch system to display one feature at a time + vec3 color = texture2D( u_shadeTex, v_texcoord).rgb; + if(u_displayType == DISPLAY_BLINN) + gl_FragColor = vec4(color, 1.0); + if(u_displayType == DISPLAY_BLOOM) + gl_FragColor = bloomShader(); + if(u_displayType == DISPLAY_TOON) + gl_FragColor = toonShader(color, 3); + if(u_displayType == DISPLAY_SSAO) + gl_FragColor = SSAO; + +} diff --git a/js/core/fbo-util.js.bak b/js/core/fbo-util.js.bak new file mode 100644 index 0000000..e943f68 --- /dev/null +++ b/js/core/fbo-util.js.bak @@ -0,0 +1,200 @@ +//A wrapper for creating framebuffer objects + +//CIS565WEBGLCORE is a core function interface +var CIS565WEBGLCORE = CIS565WEBGLCORE || {}; + +var FBO_GBUFFER = 0; +var FBO_PBUFFER = 10; +var FBO_GBUFFER_POSITION = 0; +var FBO_GBUFFER_NORMAL = 1; +var FBO_GBUFFER_COLOR = 2; +var FBO_GBUFFER_DEPTH = 3; +var FBO_GBUFFER_TEXCOORD = 4; + +CIS565WEBGLCORE.createFBO = function(){ + "use strict" + + var textures = []; + var depthTex = null; + var fbo = []; + + var multipleTargets = true; + + function init( gl, width, height ){ + gl.getExtension( "OES_texture_float" ); + gl.getExtension( "OES_texture_float_linear" ); + var extDrawBuffers = gl.getExtension( "WEBGL_draw_buffers"); + var extDepthTex = gl.getExtension( "WEBGL_depth_texture" ); + + if( !extDepthTex ) { + alert("WARNING : Depth texture extension unavailabe on your browser!"); + return false; + } + + if ( !extDrawBuffers ){ + alert("WARNING : Draw buffer extension unavailable on your browser! Defaulting to multiple render pases."); + multipleTargets = false; + } + + //Create depth texture + depthTex = gl.createTexture(); + gl.bindTexture( gl.TEXTURE_2D, depthTex ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null); + + // Create textures for FBO attachment + for( var i = 0; i < 5; ++i ){ + textures[i] = gl.createTexture() + gl.bindTexture( gl.TEXTURE_2D, textures[i] ); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); + gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); + gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null); + } + + // Create GBuffer FBO + if (multipleTargets) { + fbo[FBO_GBUFFER] = gl.createFramebuffer(); + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo[FBO_GBUFFER] ); + + // Create render target; + var drawbuffers = []; + drawbuffers[0] = extDrawBuffers.COLOR_ATTACHMENT0_WEBGL; + drawbuffers[1] = extDrawBuffers.COLOR_ATTACHMENT1_WEBGL; + drawbuffers[2] = extDrawBuffers.COLOR_ATTACHMENT2_WEBGL; + drawbuffers[3] = extDrawBuffers.COLOR_ATTACHMENT3_WEBGL; + extDrawBuffers.drawBuffersWEBGL( drawbuffers ); + + //Attach textures to FBO + gl.framebufferTexture2D( gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0 ); + gl.framebufferTexture2D( gl.FRAMEBUFFER, drawbuffers[0], gl.TEXTURE_2D, textures[0], 0 ); + gl.framebufferTexture2D( gl.FRAMEBUFFER, drawbuffers[1], gl.TEXTURE_2D, textures[1], 0 ); + gl.framebufferTexture2D( gl.FRAMEBUFFER, drawbuffers[2], gl.TEXTURE_2D, textures[2], 0 ); + gl.framebufferTexture2D( gl.FRAMEBUFFER, drawbuffers[3], gl.TEXTURE_2D, textures[3], 0 ); + + var FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if( FBOstatus !== gl.FRAMEBUFFER_COMPLETE ){ + console.log( "GBuffer FBO incomplete! Initialization failed!" ); + return false; + } + + // Create PBuffer FBO + fbo[FBO_PBUFFER] = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_PBUFFER]); + + // Attach textures + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[4], 0); + + FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if(FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { + console.log("PBuffer FBO incomplete! Initialization failed!"); + return false; + } + } else { + fbo[FBO_GBUFFER_POSITION] = gl.createFramebuffer(); + + // Set up GBuffer Position + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_POSITION]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[0], 0); + + var FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { + console.log("GBuffer Position FBO incomplete! Init failed!"); + return false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + fbo[FBO_PBUFFER] = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_PBUFFER]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[4], 0); + + FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { + console.log("PBuffer FBO incomplete! Init failed!"); + return false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // Set up GBuffer Normal + fbo[FBO_GBUFFER_NORMAL] = gl.createFramebuffer(); + + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); + + FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { + console.log("GBuffer Normal FBO incomplete! Init failed!"); + return false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // Set up GBuffer Color + fbo[FBO_GBUFFER_COLOR] = gl.createFramebuffer(); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_COLOR]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[2], 0); + + FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); + if (FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { + console.log("GBuffer Color FBO incomplete! Init failed!"); + return false; + } + } + + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); + gl.bindTexture( gl.TEXTURE_2D, null ); + + return true; + } + + return { + ref: function(buffer){ + return fbo[buffer]; + }, + initialize: function( gl, width, height ){ + return init( gl, width, height ); + }, + bind: function(gl, buffer){ + gl.bindFramebuffer( gl.FRAMEBUFFER, fbo[buffer] ); + }, + unbind: function(gl){ + gl.bindFramebuffer( gl.FRAMEBUFFER, null ); + }, + texture: function(i){ + return textures[i]; + }, + depthTexture: function(){ + return depthTex; + }, + isMultipleTargets: function(){ + return multipleTargets; + }, + ///// The following 3 functions should be implemented for all objects + ///// whose resources are retrieved asynchronously + isReady: function(){ + var isReady = ready; + for( var i = 0; i < textures.length; ++i ){ + isReady &= textures[i].ready; + } + console.log( isReady ); + return isReady; + }, + addCallback: function( functor ){ + callbackFunArray[callbackFunArray.length] = functor; + }, + executeCallBackFunc: function(){ + var i; + for( i = 0; i < callbackFunArray.length; ++i ){ + callbackFunArray[i](); + } + } + }; +}; + diff --git a/js/main.js b/js/main.js index 4140ae1..7c06886 100644 --- a/js/main.js +++ b/js/main.js @@ -13,6 +13,9 @@ var objloader; // OBJ loader var model; // Model object var quad = {}; // Empty object for full-screen quad +//for SSAO +var kernel = []; // Random samples for SSAO to use +var kernelSize = 100; // Framebuffer var fbo = null; @@ -49,7 +52,7 @@ var main = function (canvasId, messageId) { // Set up shaders initShaders(); - + initKernel(); // Register our render callbacks CIS565WEBGLCORE.render = render; CIS565WEBGLCORE.renderLoop = renderLoop; @@ -157,6 +160,7 @@ var renderMulti = function () { fbo.bind(gl, FBO_GBUFFER_POSITION); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT);//add gl.enable(gl.DEPTH_TEST); gl.useProgram(posProg.ref()); @@ -176,7 +180,8 @@ var renderMulti = function () { fbo.bind(gl, FBO_GBUFFER_NORMAL); gl.clear(gl.COLOR_BUFFER_BIT); - + gl.enable(gl.DEPTH_TEST); + gl.useProgram(normProg.ref()); //update the normal matrix @@ -194,7 +199,7 @@ var renderMulti = function () { fbo.bind(gl, FBO_GBUFFER_COLOR); gl.clear(gl.COLOR_BUFFER_BIT); - + gl.enable(gl.DEPTH_TEST); gl.useProgram(colorProg.ref()); gl.uniformMatrix4fv(colorProg.uMVPLoc, false, mvpMat); @@ -215,26 +220,30 @@ var renderShade = function () { gl.clear(gl.COLOR_BUFFER_BIT); // Bind necessary textures - //gl.activeTexture( gl.TEXTURE0 ); //position - //gl.bindTexture( gl.TEXTURE_2D, fbo.texture(0) ); - //gl.uniform1i( shadeProg.uPosSamplerLoc, 0 ); - - //gl.activeTexture( gl.TEXTURE1 ); //normal - //gl.bindTexture( gl.TEXTURE_2D, fbo.texture(1) ); - //gl.uniform1i( shadeProg.uNormalSamplerLoc, 1 ); + //comment begin + gl.activeTexture( gl.TEXTURE0 ); //position + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(0) ); + gl.uniform1i( shadeProg.uPosSamplerLoc, 0 ); + gl.activeTexture( gl.TEXTURE1 ); //normal + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(1) ); + gl.uniform1i( shadeProg.uNormalSamplerLoc, 1 ); + //comment end gl.activeTexture( gl.TEXTURE2 ); //color gl.bindTexture( gl.TEXTURE_2D, fbo.texture(2) ); gl.uniform1i( shadeProg.uColorSamplerLoc, 2 ); - - //gl.activeTexture( gl.TEXTURE3 ); //depth - //gl.bindTexture( gl.TEXTURE_2D, fbo.depthTexture() ); - //gl.uniform1i( shadeProg.uDepthSamplerLoc, 3 ); - - // Bind necessary uniforms - //gl.uniform1f( shadeProg.uZNearLoc, zNear ); - //gl.uniform1f( shadeProg.uZFarLoc, zFar ); - + //comment begin + gl.activeTexture( gl.TEXTURE3 ); //depth + gl.bindTexture( gl.TEXTURE_2D, fbo.depthTexture() ); + gl.uniform1i( shadeProg.uDepthSamplerLoc, 3 ); + + //Bind necessary uniforms + gl.uniform1f( shadeProg.uZNearLoc, zNear ); + gl.uniform1f( shadeProg.uZFarLoc, zFar ); + var light = vec4.create(); + vec4.transformMat4(light, vec4.create([1.0, 1.0, 100.0, 1.0]), camera.getViewTransform()); + gl.uniform4fv(shadeProg.uLightLoc, light); + //comment out drawQuad(shadeProg); // Unbind FBO @@ -279,9 +288,34 @@ var renderPost = function () { gl.clear(gl.COLOR_BUFFER_BIT); // Bind necessary textures - gl.activeTexture( gl.TEXTURE4 ); + gl.activeTexture( gl.TEXTURE4 ); //shade gl.bindTexture( gl.TEXTURE_2D, fbo.texture(4) ); - gl.uniform1i(postProg.uShadeSamplerLoc, 4 ); + gl.uniform1i(postProg.uShadeSamplerLoc, 4); + + gl.activeTexture(gl.TEXTURE0); //position + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(0)); + gl.uniform1i(postProg.uPosSamplerLoc, 0); + + gl.activeTexture(gl.TEXTURE1); //normal + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(1)); + gl.uniform1i(postProg.uNormalSamplerLoc, 1); + + gl.activeTexture(gl.TEXTURE2); //color + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(2)); + gl.uniform1i(postProg.uColorSamplerLoc, 2); + + gl.activeTexture(gl.TEXTURE3); //depth + gl.bindTexture(gl.TEXTURE_2D, fbo.depthTexture()); + gl.uniform1i(postProg.uDepthSamplerLoc, 3); + + + + gl.uniform1f(postProg.uZNearLoc, zNear); + gl.uniform1f(postProg.uZFarLoc, zFar); + gl.uniform1i(postProg.uWidthLoc, canvas.width); + gl.uniform1i(postProg.uHeightLoc, canvas.height); + gl.uniform1i(postProg.uDisplayTypeLoc, texToDisplay); + gl.uniform3fv(postProg.uKernelLoc, kernel); drawQuad(postProg); }; @@ -337,6 +371,22 @@ var initCamera = function () { isDiagnostic = true; texToDisplay = 4; break; + case 53: + isDiagnostic = false; + texToDisplay = 5;//bloom + break; + case 54: + isDiagnostic = false; + texToDisplay = 6; + break; + case 55: + isDiagnostic = false; + texToDisplay = 7; + break; + case 56: + isDiagnostic = false; + texToDisplay = 8; + break; } } }; @@ -466,6 +516,7 @@ var initShaders = function () { shadeProg.uZNearLoc = gl.getUniformLocation( shadeProg.ref(), "u_zNear" ); shadeProg.uZFarLoc = gl.getUniformLocation( shadeProg.ref(), "u_zFar" ); shadeProg.uDisplayTypeLoc = gl.getUniformLocation( shadeProg.ref(), "u_displayType" ); + shadeProg.uLightLoc = gl.getUniformLocation(shadeProg.ref(), "u_Light"); }); CIS565WEBGLCORE.registerAsyncObj(gl, shadeProg); @@ -477,6 +528,21 @@ var initShaders = function () { postProg.aVertexTexcoordLoc = gl.getAttribLocation( postProg.ref(), "a_texcoord" ); postProg.uShadeSamplerLoc = gl.getUniformLocation( postProg.ref(), "u_shadeTex"); + postProg.aVertexTexcoordLoc = gl.getAttribLocation(postProg.ref(), "a_texcoord"); + + postProg.uShadeSamplerLoc = gl.getUniformLocation( postProg.ref(), "u_shadeTex"); + postProg.uPosSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_positionTex"); + postProg.uNormalSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_normalTex"); + postProg.uColorSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_colorTex"); + postProg.uDepthSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_depthTex"); + + + postProg.uZNearLoc = gl.getUniformLocation(postProg.ref(), "u_zNear"); + postProg.uZFarLoc = gl.getUniformLocation(postProg.ref(), "u_zFar"); + postProg.uWidthLoc = gl.getUniformLocation(postProg.ref(), "u_width"); + postProg.uHeightLoc = gl.getUniformLocation(postProg.ref(), "u_height"); + postProg.uDisplayTypeLoc = gl.getUniformLocation(postProg.ref(), "u_displayType"); + postProg.uKernelLoc = gl.getUniformLocation(postProg.ref(), "u_kernel"); }); CIS565WEBGLCORE.registerAsyncObj(gl, postProg); }; @@ -488,3 +554,23 @@ var initFramebuffer = function () { return; } }; +var initKernel = function () { + + for (var i = 0; i < kernelSize ; i++) { + var x = Math.random() * 2.0 - 1.0; + var y = Math.random() * 2.0 - 1.0; + var z = Math.random(); + //Normalize and rescale + var len = x * x + y * y + z * z; + var scale1 = Math.random(); + var scale2 = (i * 1.0) / (kernelSize * 1.0); + if (len > 0) { + len = 1 / Math.sqrt(len); + x = x * len * scale1 * (0.1 + 0.9 * scale2); + y = y * len * scale1 * (0.1 + 0.9 * scale2); + x = z * len * scale1 * (0.1 + 0.9 * scale2); + } + kernel = kernel.concat([x, y, z]); + } + +}; \ No newline at end of file diff --git a/js/main.js.bak b/js/main.js.bak new file mode 100644 index 0000000..ed06bc9 --- /dev/null +++ b/js/main.js.bak @@ -0,0 +1,576 @@ +// Written by Harmony Li. Based on Cheng-Tso Lin's CIS 700 starter engine. +// CIS 565 : GPU Programming, Fall 2014. +// University of Pennsvylania (c) 2014. + +// Global Variables +var canvas; // Canvas DOM Element +var gl; // GL context object +var camera; // Camera object +var interactor; // Camera interaction object +var objloader; // OBJ loader + +// Models +var model; // Model object +var quad = {}; // Empty object for full-screen quad + +//for SSAO +var kernel = []; // Random samples for SSAO to use +var kernelSize = 100; +// Framebuffer +var fbo = null; + +// Shader programs +var passProg; // Shader program for G-Buffer +var shadeProg; // Shader program for P-Buffer +var diagProg; // Shader program from diagnostic +var postProg; // Shader for post-process effects + +// Multi-Pass programs +var posProg; +var normProg; +var colorProg; + +var isDiagnostic = true; +var zNear = 20; +var zFar = 2000; +var texToDisplay = 1; + +var main = function (canvasId, messageId) { + var canvas; + + // Initialize WebGL + initGL(canvasId, messageId); + + // Set up camera + initCamera(canvas); + + // Set up FBOs + initFramebuffer(); + + // Set up models + initObjs(); + + // Set up shaders + initShaders(); + initKernel(); + // Register our render callbacks + CIS565WEBGLCORE.render = render; + CIS565WEBGLCORE.renderLoop = renderLoop; + + // Start the rendering loop + CIS565WEBGLCORE.run(gl); +}; + +var renderLoop = function () { + window.requestAnimationFrame(renderLoop); + render(); +}; + +var render = function () { + if (fbo.isMultipleTargets()) { + renderPass(); + } else { + renderMulti(); + } + + if (!isDiagnostic) { + renderShade(); + renderPost(); + } else { + renderDiagnostic(); + } + + gl.useProgram(null); +}; + +var drawModel = function (program, mask) { + // Bind attributes + for(var i = 0; i < model.numGroups(); i++) { + if (mask & 0x1) { + gl.bindBuffer(gl.ARRAY_BUFFER, model.vbo(i)); + gl.vertexAttribPointer(program.aVertexPosLoc, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(program.aVertexPosLoc); + } + + if (mask & 0x2) { + gl.bindBuffer(gl.ARRAY_BUFFER, model.nbo(i)); + gl.vertexAttribPointer(program.aVertexNormalLoc, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(program.aVertexNormalLoc); + } + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, model.ibo(i)); + gl.drawElements(gl.TRIANGLES, model.iboLength(i), gl.UNSIGNED_SHORT, 0); + } + + if (mask & 0x1) gl.disableVertexAttribArray(program.aVertexPosLoc); + if (mask & 0x2) gl.disableVertexAttribArray(program.aVertexNormalLoc); + + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); +}; + +var drawQuad = function (program) { + gl.bindBuffer(gl.ARRAY_BUFFER, quad.vbo); + gl.vertexAttribPointer(program.aVertexPosLoc, 3, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(program.aVertexPosLoc); + + gl.bindBuffer(gl.ARRAY_BUFFER, quad.tbo); + gl.vertexAttribPointer(program.aVertexTexcoordLoc, 2, gl.FLOAT, false, 0, 0); + gl.enableVertexAttribArray(program.aVertexTexcoordLoc); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quad.ibo); + gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_SHORT, 0); + + gl.disableVertexAttribArray(program.aVertexPosLoc); + gl.disableVertexAttribArray(program.aVertexTexcoordLoc); + + gl.bindBuffer(gl.ARRAY_BUFFER, null); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); +}; + +var renderPass = function () { + // Bind framebuffer object for gbuffer + fbo.bind(gl, FBO_GBUFFER); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.enable(gl.DEPTH_TEST); + + gl.useProgram(passProg.ref()); + + //update the model-view matrix + var mvpMat = mat4.create(); + mat4.multiply( mvpMat, persp, camera.getViewTransform() ); + + //update the normal matrix + var nmlMat = mat4.create(); + mat4.invert( nmlMat, camera.getViewTransform() ); + mat4.transpose( nmlMat, nmlMat); + + gl.uniformMatrix4fv( passProg.uModelViewLoc, false, camera.getViewTransform()); + gl.uniformMatrix4fv( passProg.uMVPLoc, false, mvpMat ); + gl.uniformMatrix4fv( passProg.uNormalMatLoc, false, nmlMat ); + + drawModel(passProg, 0x3); + + // Unbind framebuffer + fbo.unbind(gl); +}; + +var renderMulti = function () { + fbo.bind(gl, FBO_GBUFFER_POSITION); + + gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + gl.clear(gl.COLOR_BUFFER_BIT);//add + gl.enable(gl.DEPTH_TEST); + + gl.useProgram(posProg.ref()); + + //update the model-view matrix + var mvpMat = mat4.create(); + mat4.multiply( mvpMat, persp, camera.getViewTransform() ); + + gl.uniformMatrix4fv( posProg.uModelViewLoc, false, camera.getViewTransform()); + gl.uniformMatrix4fv( posProg.uMVPLoc, false, mvpMat ); + + drawModel(posProg, 1); + + gl.disable(gl.DEPTH_TEST); + fbo.unbind(gl); + gl.useProgram(null); + + fbo.bind(gl, FBO_GBUFFER_NORMAL); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.enable(gl.DEPTH_TEST); + + gl.useProgram(normProg.ref()); + + //update the normal matrix + var nmlMat = mat4.create(); + mat4.invert( nmlMat, camera.getViewTransform() ); + mat4.transpose( nmlMat, nmlMat); + + gl.uniformMatrix4fv(normProg.uMVPLoc, false, mvpMat); + gl.uniformMatrix4fv(normProg.uNormalMatLoc, false, nmlMat); + + drawModel(normProg, 3); + + gl.useProgram(null); + fbo.unbind(gl); + + fbo.bind(gl, FBO_GBUFFER_COLOR); + gl.clear(gl.COLOR_BUFFER_BIT); + gl.enable(gl.DEPTH_TEST); + gl.useProgram(colorProg.ref()); + + gl.uniformMatrix4fv(colorProg.uMVPLoc, false, mvpMat); + + drawModel(colorProg, 1); + + gl.useProgram(null); + fbo.unbind(gl); +}; + +var renderShade = function () { + gl.useProgram(shadeProg.ref()); + gl.disable(gl.DEPTH_TEST); + + // Bind FBO + fbo.bind(gl, FBO_PBUFFER); + + gl.clear(gl.COLOR_BUFFER_BIT); + + // Bind necessary textures + //comment begin + gl.activeTexture( gl.TEXTURE0 ); //position + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(0) ); + gl.uniform1i( shadeProg.uPosSamplerLoc, 0 ); + + gl.activeTexture( gl.TEXTURE1 ); //normal + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(1) ); + gl.uniform1i( shadeProg.uNormalSamplerLoc, 1 ); + //comment end + gl.activeTexture( gl.TEXTURE2 ); //color + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(2) ); + gl.uniform1i( shadeProg.uColorSamplerLoc, 2 ); + //comment begin + gl.activeTexture( gl.TEXTURE3 ); //depth + gl.bindTexture( gl.TEXTURE_2D, fbo.depthTexture() ); + gl.uniform1i( shadeProg.uDepthSamplerLoc, 3 ); + + //Bind necessary uniforms + gl.uniform1f( shadeProg.uZNearLoc, zNear ); + gl.uniform1f( shadeProg.uZFarLoc, zFar ); + var light = vec4.create(); + vec4.transformMat4(light, vec4.create([1.0, 1.0, 100.0, 1.0]), camera.getViewTransform()); + gl.uniform4fv(shadeProg.uLightLoc, light); + //comment out + drawQuad(shadeProg); + + // Unbind FBO + fbo.unbind(gl); +}; + +var renderDiagnostic = function () { + gl.useProgram(diagProg.ref()); + + gl.disable(gl.DEPTH_TEST); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Bind necessary textures + gl.activeTexture( gl.TEXTURE0 ); //position + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(0) ); + gl.uniform1i( diagProg.uPosSamplerLoc, 0 ); + + gl.activeTexture( gl.TEXTURE1 ); //normal + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(1) ); + gl.uniform1i( diagProg.uNormalSamplerLoc, 1 ); + + gl.activeTexture( gl.TEXTURE2 ); //color + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(2) ); + gl.uniform1i( diagProg.uColorSamplerLoc, 2 ); + + gl.activeTexture( gl.TEXTURE3 ); //depth + gl.bindTexture( gl.TEXTURE_2D, fbo.depthTexture() ); + gl.uniform1i( diagProg.uDepthSamplerLoc, 3 ); + + // Bind necessary uniforms + gl.uniform1f( diagProg.uZNearLoc, zNear ); + gl.uniform1f( diagProg.uZFarLoc, zFar ); + gl.uniform1i( diagProg.uDisplayTypeLoc, texToDisplay ); + + drawQuad(diagProg); +}; + +var renderPost = function () { + gl.useProgram(postProg.ref()); + + gl.disable(gl.DEPTH_TEST); + gl.clear(gl.COLOR_BUFFER_BIT); + + // Bind necessary textures + gl.activeTexture( gl.TEXTURE4 ); //shade + gl.bindTexture( gl.TEXTURE_2D, fbo.texture(4) ); + gl.uniform1i(postProg.uShadeSamplerLoc, 4); + + gl.activeTexture(gl.TEXTURE0); //position + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(0)); + gl.uniform1i(postProg.uPosSamplerLoc, 0); + + gl.activeTexture(gl.TEXTURE1); //normal + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(1)); + gl.uniform1i(postProg.uNormalSamplerLoc, 1); + + gl.activeTexture(gl.TEXTURE2); //color + gl.bindTexture(gl.TEXTURE_2D, fbo.texture(2)); + gl.uniform1i(postProg.uColorSamplerLoc, 2); + + gl.activeTexture(gl.TEXTURE3); //depth + gl.bindTexture(gl.TEXTURE_2D, fbo.depthTexture()); + gl.uniform1i(postProg.uDepthSamplerLoc, 3); + + + + gl.uniform1f(postProg.uZNearLoc, zNear); + gl.uniform1f(postProg.uZFarLoc, zFar); + gl.uniform1i(postProg.uWidthLoc, canvas.width); + gl.uniform1i(postProg.uHeightLoc, canvas.height); + gl.uniform1i(postProg.uDisplayTypeLoc, texToDisplay); + gl.uniform3fv(postProg.uKernelLoc, kernel); + + drawQuad(postProg); +}; + +var initGL = function (canvasId, messageId) { + var msg; + + // Get WebGL context + canvas = document.getElementById(canvasId); + msg = document.getElementById(messageId); + gl = CIS565WEBGLCORE.getWebGLContext(canvas, msg); + + if (!gl) { + return; // return if a WebGL context not found + } + + // Set up WebGL stuff + gl.viewport(0, 0, canvas.width, canvas.height); + gl.clearColor(0.3, 0.3, 0.3, 1.0); + gl.enable(gl.DEPTH_TEST); + gl.depthFunc(gl.LESS); +}; + +var initCamera = function () { + // Setup camera + persp = mat4.create(); + mat4.perspective(persp, todeg(60), canvas.width / canvas.height, 0.1, 2000); + + camera = CIS565WEBGLCORE.createCamera(CAMERA_TRACKING_TYPE); + camera.goHome([0, 0, 4]); + interactor = CIS565WEBGLCORE.CameraInteractor(camera, canvas); + + // Add key-input controls + window.onkeydown = function (e) { + interactor.onKeyDown(e); + switch(e.keyCode) { + case 48: + isDiagnostic = false; + break; + case 49: + isDiagnostic = true; + texToDisplay = 1; + break; + case 50: + isDiagnostic = true; + texToDisplay = 2; + break; + case 51: + isDiagnostic = true; + texToDisplay = 3; + break; + case 52: + isDiagnostic = true; + texToDisplay = 4; + break; + case 53: + isDiagnostic = false; + texToDisplay = 5; + break; + case 54: + isDiagnostic = false; + texToDisplay = 6; + break; + case 55: + isDiagnostic = false; + texToDisplay = 7; + break; + case 56: + isDiagnostic = false; + texToDisplay = 8; + break; + } + } +}; + +var initObjs = function () { + // Create an OBJ loader + objloader = CIS565WEBGLCORE.createOBJLoader(); + + // Load the OBJ from file + objloader.loadFromFile(gl, "assets/models/suzanne.obj", null); + + // Add callback to upload the vertices once loaded + objloader.addCallback(function () { + model = new Model(gl, objloader); + }); + + // Register callback item + CIS565WEBGLCORE.registerAsyncObj(gl, objloader); + + // Initialize full-screen quad + quad.vbo = gl.createBuffer(); + quad.ibo = gl.createBuffer(); + quad.tbo = gl.createBuffer(); + + gl.bindBuffer(gl.ARRAY_BUFFER, quad.vbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(screenQuad.vertices), gl.STATIC_DRAW); + + gl.bindBuffer(gl.ARRAY_BUFFER, quad.tbo); + gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(screenQuad.texcoords), gl.STATIC_DRAW); + gl.bindBuffer(gl.ARRAY_BUFFER, null) + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, quad.ibo); + gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(screenQuad.indices), gl.STATIC_DRAW); + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); +}; + +var initShaders = function () { + if (fbo.isMultipleTargets()) { + // Create a shader program for rendering the object we are loading + passProg = CIS565WEBGLCORE.createShaderProgram(); + + // Load the shader source asynchronously + passProg.loadShader(gl, "assets/shader/deferred/pass.vert", "assets/shader/deferred/pass.frag"); + + // Register the necessary callback functions + passProg.addCallback( function() { + gl.useProgram(passProg.ref()); + + // Add uniform locations + passProg.aVertexPosLoc = gl.getAttribLocation( passProg.ref(), "a_pos" ); + passProg.aVertexNormalLoc = gl.getAttribLocation( passProg.ref(), "a_normal" ); + passProg.aVertexTexcoordLoc = gl.getAttribLocation( passProg.ref(), "a_texcoord" ); + + passProg.uPerspLoc = gl.getUniformLocation( passProg.ref(), "u_projection" ); + passProg.uModelViewLoc = gl.getUniformLocation( passProg.ref(), "u_modelview" ); + passProg.uMVPLoc = gl.getUniformLocation( passProg.ref(), "u_mvp" ); + passProg.uNormalMatLoc = gl.getUniformLocation( passProg.ref(), "u_normalMat"); + passProg.uSamplerLoc = gl.getUniformLocation( passProg.ref(), "u_sampler"); + }); + + CIS565WEBGLCORE.registerAsyncObj(gl, passProg); + } else { + posProg = CIS565WEBGLCORE.createShaderProgram(); + posProg.loadShader(gl, "assets/shader/deferred/posPass.vert", "assets/shader/deferred/posPass.frag"); + posProg.addCallback(function() { + posProg.aVertexPosLoc = gl.getAttribLocation(posProg.ref(), "a_pos"); + + posProg.uModelViewLoc = gl.getUniformLocation(posProg.ref(), "u_modelview"); + posProg.uMVPLoc = gl.getUniformLocation(posProg.ref(), "u_mvp"); + }); + + CIS565WEBGLCORE.registerAsyncObj(gl, posProg); + + normProg = CIS565WEBGLCORE.createShaderProgram(); + normProg.loadShader(gl, "assets/shader/deferred/normPass.vert", "assets/shader/deferred/normPass.frag"); + normProg.addCallback(function() { + normProg.aVertexPosLoc = gl.getAttribLocation(normProg.ref(), "a_pos"); + normProg.aVertexNormalLoc = gl.getAttribLocation(normProg.ref(), "a_normal"); + + normProg.uMVPLoc = gl.getUniformLocation(normProg.ref(), "u_mvp"); + normProg.uNormalMatLoc = gl.getUniformLocation(normProg.ref(), "u_normalMat"); + }); + + CIS565WEBGLCORE.registerAsyncObj(gl, normProg); + + colorProg = CIS565WEBGLCORE.createShaderProgram(); + colorProg.loadShader(gl, "assets/shader/deferred/colorPass.vert", "assets/shader/deferred/colorPass.frag"); + colorProg.addCallback(function(){ + colorProg.aVertexPosLoc = gl.getAttribLocation(colorProg.ref(), "a_pos"); + + colorProg.uMVPLoc = gl.getUniformLocation(colorProg.ref(), "u_mvp"); + }); + + CIS565WEBGLCORE.registerAsyncObj(gl, colorProg); + } + + // Create shader program for diagnostic + diagProg = CIS565WEBGLCORE.createShaderProgram(); + diagProg.loadShader(gl, "assets/shader/deferred/quad.vert", "assets/shader/deferred/diagnostic.frag"); + diagProg.addCallback( function() { + diagProg.aVertexPosLoc = gl.getAttribLocation( diagProg.ref(), "a_pos" ); + diagProg.aVertexTexcoordLoc = gl.getAttribLocation( diagProg.ref(), "a_texcoord" ); + + diagProg.uPosSamplerLoc = gl.getUniformLocation( diagProg.ref(), "u_positionTex"); + diagProg.uNormalSamplerLoc = gl.getUniformLocation( diagProg.ref(), "u_normalTex"); + diagProg.uColorSamplerLoc = gl.getUniformLocation( diagProg.ref(), "u_colorTex"); + diagProg.uDepthSamplerLoc = gl.getUniformLocation( diagProg.ref(), "u_depthTex"); + + diagProg.uZNearLoc = gl.getUniformLocation( diagProg.ref(), "u_zNear" ); + diagProg.uZFarLoc = gl.getUniformLocation( diagProg.ref(), "u_zFar" ); + diagProg.uDisplayTypeLoc = gl.getUniformLocation( diagProg.ref(), "u_displayType" ); + }); + CIS565WEBGLCORE.registerAsyncObj(gl, diagProg); + + // Create shader program for shade + shadeProg = CIS565WEBGLCORE.createShaderProgram(); + shadeProg.loadShader(gl, "assets/shader/deferred/quad.vert", "assets/shader/deferred/diffuse.frag"); + shadeProg.addCallback( function() { + shadeProg.aVertexPosLoc = gl.getAttribLocation( shadeProg.ref(), "a_pos" ); + shadeProg.aVertexTexcoordLoc = gl.getAttribLocation( shadeProg.ref(), "a_texcoord" ); + + shadeProg.uPosSamplerLoc = gl.getUniformLocation( shadeProg.ref(), "u_positionTex"); + shadeProg.uNormalSamplerLoc = gl.getUniformLocation( shadeProg.ref(), "u_normalTex"); + shadeProg.uColorSamplerLoc = gl.getUniformLocation( shadeProg.ref(), "u_colorTex"); + shadeProg.uDepthSamplerLoc = gl.getUniformLocation( shadeProg.ref(), "u_depthTex"); + + shadeProg.uZNearLoc = gl.getUniformLocation( shadeProg.ref(), "u_zNear" ); + shadeProg.uZFarLoc = gl.getUniformLocation( shadeProg.ref(), "u_zFar" ); + shadeProg.uDisplayTypeLoc = gl.getUniformLocation( shadeProg.ref(), "u_displayType" ); + shadeProg.uLightLoc = gl.getUniformLocation(shadeProg.ref(), "u_Light"); + }); + CIS565WEBGLCORE.registerAsyncObj(gl, shadeProg); + + // Create shader program for post-process + postProg = CIS565WEBGLCORE.createShaderProgram(); + postProg.loadShader(gl, "assets/shader/deferred/quad.vert", "assets/shader/deferred/post.frag"); + postProg.addCallback( function() { + postProg.aVertexPosLoc = gl.getAttribLocation( postProg.ref(), "a_pos" ); + postProg.aVertexTexcoordLoc = gl.getAttribLocation( postProg.ref(), "a_texcoord" ); + + postProg.uShadeSamplerLoc = gl.getUniformLocation( postProg.ref(), "u_shadeTex"); + postProg.aVertexTexcoordLoc = gl.getAttribLocation(postProg.ref(), "a_texcoord"); + + postProg.uShadeSamplerLoc = gl.getUniformLocation( postProg.ref(), "u_shadeTex"); + postProg.uPosSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_positionTex"); + postProg.uNormalSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_normalTex"); + postProg.uColorSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_colorTex"); + postProg.uDepthSamplerLoc = gl.getUniformLocation(postProg.ref(), "u_depthTex"); + + + postProg.uZNearLoc = gl.getUniformLocation(postProg.ref(), "u_zNear"); + postProg.uZFarLoc = gl.getUniformLocation(postProg.ref(), "u_zFar"); + postProg.uWidthLoc = gl.getUniformLocation(postProg.ref(), "u_width"); + postProg.uHeightLoc = gl.getUniformLocation(postProg.ref(), "u_height"); + postProg.uDisplayTypeLoc = gl.getUniformLocation(postProg.ref(), "u_displayType"); + postProg.uKernelLoc = gl.getUniformLocation(postProg.ref(), "u_kernel"); + }); + CIS565WEBGLCORE.registerAsyncObj(gl, postProg); +}; + +var initFramebuffer = function () { + fbo = CIS565WEBGLCORE.createFBO(); + if (!fbo.initialize(gl, canvas.width, canvas.height)) { + console.log("FBO Initialization failed"); + return; + } +}; +var initKernel = function () { + + for (var i = 0; i < kernelSize ; i++) { + var x = Math.random() * 2.0 - 1.0; + var y = Math.random() * 2.0 - 1.0; + var z = Math.random(); + //Normalize and rescale + var len = x * x + y * y + z * z; + var scale1 = Math.random(); + var scale2 = (i * 1.0) / (kernelSize * 1.0); + if (len > 0) { + len = 1 / Math.sqrt(len); + x = x * len * scale1 * (0.1 + 0.9 * scale2); + y = y * len * scale1 * (0.1 + 0.9 * scale2); + x = z * len * scale1 * (0.1 + 0.9 * scale2); + } + kernel = kernel.concat([x, y, z]); + } + +}; \ No newline at end of file diff --git a/js/screenQuad.js b/js/screenQuad.js index dde90a5..e7e6025 100644 --- a/js/screenQuad.js +++ b/js/screenQuad.js @@ -8,10 +8,10 @@ var screenQuad ={ -1.0, 1.0, 0.0 ], texcoords:[ - 1.0, 0.0, 0.0, 0.0, - 0.0, 1.0, - 1.0, 1.0 + 1.0, 0.0, + 1.0, 1.0, + 0.0, 1.0 ], indices: [ 0, 1, 3, diff --git a/screenshots/bloom.jpg b/screenshots/bloom.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6cc75d1d82fde59391ac14656ee4e5d623e9cda GIT binary patch literal 36225 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 literal 0 HcmV?d00001 diff --git a/screenshots/diffuseSpec.jpg b/screenshots/diffuseSpec.jpg new file mode 100644 index 0000000000000000000000000000000000000000..8bd6dcb9820852442b676099220bdc5f3968a1d6 GIT binary patch literal 30672 zcmeFYbx>T<7be&Q4G>5O5F8TREx1PT5P~}dcXw@qyGtNQ(8fEs)403SxH~l7K+`P0 znXTEat(mD=`E&1kr>d*_zOMVdSNELneCNEUg{O7EYk6roX#ffe3gGAS0eD&hd;y@o zc=4a}d7wR?=ve6JXlUrzn3xz?xY)S3IM_HiukZ+7zruTihlBH)`1PB&ghWI{xcKi# zhzUsu2#E;)^Cu{%&)1-#zeGoWN%#uq72*Hw@zeo$gN0Ir0z*al0C@2R1@#TeQxAX| z06;;1UhRJt{J#&B7td?Nz{Glqjq^OA?ls^A3M%RgG}QmB`gykB^X~w(H|PZKxj$jN zRW`!>;6%vtDzJ6DnOj&|S-ZHpxqEnec?SiDgocGjL?$LBr=+I+PS428FDNVm6_=D& z*VNY4H#9aicXjvl_Vo`84oywZ%+Ad(EJ8Onx3+h7_x2AC&o3^ou5WJPclZCng#tkR zudx0bvi}RNH_y0UprN6nVg3gf$_tNYLw$pW{+=6y;FB_@k<;4`Jio9AKPTi?b-twK zRe=#1J5OR0(|z2aKmQN3|AOp)4Orm+BV_*#*#ChG3cy80d471PZvYa22aZUlzc2oO z`=8tc){%B`gdgne_+h~AiuDNL|x`Mja zhp*9vtpZ$Ab7ALC?vHt782SXTF$^|fY4=g!q&R@dC!{M_%dtnBzQ90B%42r;?fZ9+ zRIo$vY6V|^T2!&HyTtN7rl<_0)H2jsm1Q2PSgu~*oQOyA+R;0Z^_+oe)dD*|>0YxD zIhgZ$9j<&{_O-3G<$)my*rGT?|ADJruMs*nCizwS>-Q0?_dl+;sfx`x)1_@(I(r>W z{X*myv>|Q+78XA2DHUZ`66#FlKtj)9Ntjr$2aanmo(7F4LM<;I34U;S##Z^uoy)s zKR*=}*ke&HnWsyKaB7*cG5YQ-AJ%b!Hji`e8wPZz+S6q`IdW+pqnXG<2MTs8ySJy2 ztQ$a_CqSoKEl%6#ujkYOeC#YeU*#1G58tc~m=1I$y9T58ZM8TF0qleZuJQjf^=mth zXJD5?)T4vas=m}uXk#dgO?V(AQmdwsBQTJXUo$Q;f68&#kid0miA004h&+;{>?&ds#862EZ z-{8P7YOqC2W4nA?T64U%Q@qey@Bx77R8{8z2f!SNKV|LC2|d8cXaHMbcfkssiY-g+wwFdU>i zZ>yrrqkE_mJ^!XW;57+r6-t0P=~1AcqVpZmwrJlI09R?E1ZEOd=ehvmsk1NPCI2F+A^rNzr z2Q?d^vJ~L|8`Hz@=Zq#Z>=+M)=0x$yw$I=ARp~6S4HKq$;5jBRJM{hz==mUmZr4}< zl|OF$tWK9p_`tn4P;ERIBZyVa}eQdqDr z9#js99&H=^6|5oXNK82&*Npi{+f^K`oJZaGdC@XiVfhE9fxt% z)JC|U{ey2*3GGqK$d9|LKJLg|fdG!lrQxOq%dm5~6p8Roq}y_k@~Zf-6&`P&gg7gc zi4Dl!ChK=B*%0WRgxRp2tHVVb1a*wPLaLeiET?TLM?OA!w#^vT!f7u+G(u@KcJ%7D z|FX0{TZZP+pev1M_tf7(m=&KEEUh2uY!C2d}pr7kVU%?0pR-}frSmBOLqYyKquu3I9oAgv;2KaO|pOdl9r zp2|w=>S$mrDISR@kqvyWTTc9yrGw{RrwR|&ZD9;CpSn7BBo`qrfQ8lgv0O-{YBm*i z-?2&LrM6c+tzfg}F||G!@y-VK2_PT4B;DHTs|y|GIbCe~ae}#gMJc0`g{mOxC@rAy zzALtS`2qXN4xM;)d8y>?2QF%Kjw+(89~|!Kxx$)LVCKaqx>^$>Cs;V^aqK9!9;3ga z1`S3F&@R-&cL~av2-`S0NclR?Md!CDz)k%N^==-h$k5vGTO)N&W@T#qxcbj+Q{mwg zV32mKRSK4)Q$JZW&z46Pn{H2{H~1Q5W2rXPbWDEZ*TC3|DnV4nY#4Qnn?#s6&t$7o z-tdmKj{cBdZ|cv)pNPr!IThu3rXj4M8ocZ4wC0#jhO9S5Xmi2#=iv8iO zLJm&=f~|DUkMMqk&n=I%MAIRpd&U>yveL2TH5DQQmiAZg73KPOSz?GuKK-D)I^29X z?_cIu|Huk|d4BdBBQS$BwI}}^H^A)TRHn=vr?urjt-}Ope%nZMQdvZd6 zM6I7-z&;Is2H5D{q8paD+=qDL(A^K8EY0*{1{O$3(O#1fnW;$Vw7>B6ft{V8tk2H5 znm@2Vwl7?>GibizOD>?oR~%TNrvqRBVCCgyH)=LMO76Njqh8rsusEp7+#iH!&i?qf zyhzq!*#CQi3obt7Z5HLojyiNF<4t=n2s5Hmt=&5GMA` zE!M^bPd+xEG_bAG%v6vX#prO?F7<5l3Ys6WgkXr;#`Axbw`Mus&krjx(5s!iy6szo zigY{y0JRA&n|2B#Gt|Remi4%ahGUf@sO3^=Sp4Gpk>CMI`f$}dv-5^6k0^-M6Cm7a z)j)a}U>+tnakA#B;q*2UMR0i!&>D<;f+SK`->GnQAV4V3G0a}ueZD|$`W5-Sf6qB>zkg~g?c6$7n!q;|>I>S7DA zIR2>7saM-MFPcV0bQQpf-=sK6NH^MZHgwAPe5G_Q!2&7{s`qiI$r~5tqK=JPP}Vbt zGKVrCjGs@%`@;Jw`Oj+W3>7P>NOl&XdCp~M)=todQ=w<_>Y-n%Ej zeMzT*Y^G_GrVF2WSr%4z+qpYSv#Y$+(};1c7N z#PkK&0{>$UUA@I_(;S&fQPoEzlfy1#9hk1R9{0mfWR_v53bVMdzIiFwrQhV>dy9TK ziN}%tfpZJ*Hi%CnN7UP1ra8$?z>VQ>s-K!`(?!}U_iUHfe&0uMe|>Wd3c zDDng=Mx7Y1xoT>#^Y)}U3U>GH05Md)0QRHR>o?o8eEvpi|3*ipOA0{u1oh&FL4n~8 z9M_bjqFHj`R)k!y$CgiNOhQF{7dvSa2CZ-ZX-9&r-E#6J=r`?gvbmLr(z;GSqj-8` zU&G$VpskSlOZsYG9Gf})RiirNo=he@6I-58K^`;axF_du@(0}D8S7)-IDJKK?0b~S^+P-010kEz*yCsMiQ__> z?Hs5A5}VUw6U#wV2v%i2U3-w1y1Lh+sr>tN`K-f6lKfWw2X$6|`okD8gJl2CtGAsQ zkQg`SIy-~S^{A5Q+jnX9<6^_&`kBOo#y#6DZtU4!M0=Y~$*^(TwlRnyoup%4G>APArSSFoyndb~zc_WRt`Cz2cL3FAj63dZt=mKY z^sN`QHXGbcZSuDRTkiCi zZEO!k@=)%os5K%umj3ub)i0E)U*UI+GETwaI(^##P`^%N8PAhUBU90uQy zrqg{IF51TWs=!)eGr`_l7n`7(xs4WeQ}W67qrT2x*Sp2bB2w>p%5ZbqVqZxK3J!vr zCZl^*E97~K&ZxEyYC6)mM8ES?a#;oLiu#>R%tW!g7I(Uv8|5q6x%_O^TRSc2^T(qu zYtt!IHRyY+{=Tt2u-Wg(r>EZ7lHfpy`J!HoxBm!nl)*@JGL=z=Ha^0=;PsQ6ps4;} zH|N?xc`1fL(aoQ7o(2-hF7iWmoAnot?BCFS0LAT<7_!a#ZNUOcl9?^w2~ebfBpOzI zcU(9uNQJ}?OEr?x?ABCtYpzi$^@hM1(|1#}f27(+jnB2TNm^yNvWwoN9=%>?ig50q zaGGnRl)gQ=Rb&bkaTR4#WK7d8Il5bGum46 zt6(W}?5FSh_#18aDy1a~ZS}3WwR;R?P$xVb{(^a@+v)a&zYM;$enV;weH|c=nka6L ziVAkQ(5A`@2oylI=c@h@Lg z=5`@N7Q%p9ZI`6TTigu!E63mWjWTBCKEO1SEy17= z9t4uZ?H#>AlI{~=Y50Uw-{+$TuqF&a$g2%2zwxJrXI$Fc^A1yR!-J3p#~)tS@RU~C zzk5$oC2qk(U`DUzAM7d93l)eH@H<0t;o)WoZIF z5y2leA)&;#W%Oh5#85{Za)oU2YfHhO!xiCDPP@bWQ?jTRhf~){rEq!g|_x{)M}@ zot+XC%Gv|5cmhDzI;p8oQlUdz4>&!;b*2+_%*y5|t<|S?0}>FaB}N(y6rO>AO27KH zo|%-%J3D!oLJsXRuDnR*^#5f)@3P7b5822GtN(n|lW`In^Hzqgs$%&t@f$6mF-H6< z*wnZ9Yuj%LV}Wh)~g4_{N7O3>(xv1{@vTiiqALI6>|sWZr9 zPARBBzhUJoHjR z*>sXq?;HKhnOEbmSB>z)ul1(`c78fOuH-03;BPzuPAGHObSleFD86-le)*b%G$H;) zVHOGTj`T8hL_4dmGCbrQqLbLg2uOy-ELpPUIyZL`6YDrJs+} zUcBG>BKVRzMD{Y~R2IAGBvEqQcJ}0*e~jh(m|J2Kx^j#)o-f1VA_PE=tg_y0@s`Ie zkK<;b;jVvr=!R$|o}j#wQ*5mBs-3G`TjlIZN*V_=&jH(4sAfV(;*NL*5fbBq^r4{=ymP)Lfopi5XYkzm5N)!{jrCWz0$4EtuakG1pqYMP^GcV1b$kaK_N zGRf8NS0g!>P)Bvl?@=?ilvQVXb|?MeqH2>*0872W?Pw!I?7xB1?i@nf7cyI#wzW|< z)ah?sbJqNyH)h3rosAUUs*>T3L5ko+7kIhf;7s#5dw|>O+s=z8d1ZMFAbZ3Xe4hY+ z_B{5CV{_Jzl&Yy$<@PRcGrjRvbN2XDZoA)U{H;b2wtx#c*h_TtE$GVBqhU_-aX`C| zQV9RFjqAg1FkDSYx>J_i9<9AjYHxM)$J5c`&;}gcGpX9JP6hXE3Z~m>^m{beKclsT zoFvSyk%QT)0-(wfgE{yZ{w87!8c1Tr*y0_a;GPV`u5 z2tR-Nj_rv%&t$KKJ=))(lxYxmDZqZc!l+Cdcw(1B%C(I6-Pyk?>g|+Kz1`ER^|~9b zgwbgAk$fOs!?H&=&2hP7+n=<8U7Ymet_owqgwxI*P8{gE+#;+fWrGzd5pif#lc~4H z!4k%LTKk+p8y=W#yOYV&KdY<*QFL~Vghuh6Vp1ySoI$Je@5pQ&=wwjVNzDzNmg9D= z4nP}i72cx(PNX#V&a6dXGPk|Y6>n!6 zY@~!@2^|=;Qc~E8&acLeIT2oRyzAl5j=cezDDpR!HeKWL}bpGCcvP zz0)KoPJO?4zYnmDaWwlK9ZWrQkM{&fFGCW;OraN#c^3S0lIwz`R(M`G_m@WtmvKsU z^`Tu*5g*TWgqbMmOUlPkLCW^lS;{-Al&Pag>hJLhx3aVgd(<=Y)Ihfz5%VNKgA`^H zSU4!f%%jZqL3AvIqFw&uR5UFSDDCdx@|v^Pfo^YGFHUvpZ_C`39|?8s3f*)}M=$l5 zvv4Kmm!;iaC*iEI5+LzlWwz(%kc;VU6-?3M4o0#2SrL7_i~X3?rqE-T zSut9`U6GY~uqQz8l1;fx*_Rt_iM>{uT8ok$FQWyg%p_>ri@6J&1p*tdL{Ht*!M7Ny z3+r*-=lTkgf!9&sQ-;$441BSE|Lc^uhwY@v!)RbEF$3Nv@`e$7O8CINrkGxRD6I~D z%8d`ua5l~YwXdmC`Jr_#r@p9t*){Q6S0W42GyeBhh4>cCIqA>D)@eNRMVISkYEFT&ikn zuHhTU*Mi3`eDT41Y~mgO;RZ*WQtx}Nn^LF}Bh7@^$QL3#ZjvD@N&`=T7$cGy5IvI! z?=)B!C#sM|F#J;LG((Yhm@fz0h6)W5r1q~VU8=ll6eHiVkC`T!q70XEpflk8ucKI( zUO%q~?&sKc=6X{NYv@_yRj!F844m#EKCeyl38MJ(SNAsH{rK;E;As;9Z=$Hh!?$&F zalaU^vuIIztDUx+xIYRDKyUU*kNkgj%RtxbJ4xEV9lPyd)C@IXOIiNe^ezOPfOzbs zzzmA?EXUa`s&llTQUKFs-`i$4`;{#`CcuC_)USL$RSOtU%-g#CP8Hlquw&Fre7&Ab zz_=QnNuAOmq{1Ctq ziTu}e`jV=L`*9TaPF+#iHI~8=l zbT{(z21e3xBB*8qskBW7$3CL34!hB+*=>D`ezGWMxhs;Xlna|FFW`S>66_MT_*hSX zF8l{6cq&@W15s?%&Jl4=+ok9lAJ89B#hN|`Ox_|9L~)-_D2o@fIW~kdTLk>jhJWdI zKO%JLWwo6DJ|&S(^S-D7JIBL1rDZ-5>{sk>=JT$ghP0dM4H_^DNPyT{MGi1 z5X>h4SYOn|By?}J-Mme^%kJxAH%q5cm7wKVp2yt%-ESZ)W(7Dd`=9H~DL-7}k;&@$ zRE=L$wAZC<+Duy&RWpR)f3}f7AgX|ewHY_KC>)4gCvX@9DUbM zTtoTP|69iJub2ec-t|&hl;Q`4S<_+kMY>o##Q8<+HcGaS%NXk-#2g<53cuW(O%@Jm zYuo(;>SoAwv2*@Tj}syZ*r=lIlW#!7z!bxmc(8%OO=RIEm|>!<#iUh*e1skzbJL2Ff@XHvK9J}vg1;LsEI zFZ6?$E|gITOOW}T9o?Z%q{Zy(#B-B9+`&J1FnIiIT)Hx>56M!^^e1{+7T&}wN#b83 zSZ$@$&(wUV?3L2SZ?%MhZxQ#9wCztOAVt@;mWTq|Um+B@SL21Q#NtF2PB%?oe?}k2 zC0&hwx}TS$u);=*A$skqPVyq1aTT-8_v+l`PpzwjahKyMIc{>9xW_H$(S22xK&~pfHvke^@g^l#ygFW)*icj_sIgnEvWKG0bd~ zwR5I$zi$jroRG6k3c1S{WQ=PD-op{2E@&o^XFCyQF$tmSp;$MSREH}SyPnHN6YCH2 z{=|h_!wYu?G+oZ>>RYu9*M_?g+CrFWd4k>xo`YJ8c0AzthQ6R+C4Sd!-?(}Vjy zlAo~Ee9)PHT&lh)ieY?>$<(+|0ex)M9;!?Q|(H#O3pVBwRYf*YE4yQcsZ` z#PXtW=?8W-|D8GPKH~j;K=o(f-z8{Ji2otm$q5DblS(BBjp~&~AM10y$;Kz7r;4_T z1T9wb{<=~@@0o7b%reA8eciJf;{o08WnAqWt1D7P!=tC^N`k)^P=C~ToFCZcQzTx= zO|*>vx?dP>K=|wAin7!J^+;7~oIUT~#oBFTMKm!5g~vhM!)|TBkzQ7hiNb73_Ioq|TK@m0f!$%C%*Ik%seMaxzYKwobu+i+MAyq>0eV zUz=v^Xq8#!`AiQKGLEvWg(2RK9NWDLPU@sEV&|cYvIpodF8}p;(;{)Q?pnqsS#d_t zYGxv?6G2+jh>{361-`k{lrTtU5gyLUAqwx3;r!G6X@#@vzR*5`onXI~AMw)2sT}^$ zb#IoP%aGs$EQRXm9=;AVfXXqbYipu^SIg=~qfRS6GW=I}7xX{~*G0Z{fJ(?_<<~Y= zP1x_i@T*hBM}7BR3ms)yeOI>n@8(pX&Gw-2)v4$2K)OJu-rDXO&YT}^7=ZLT5E zAN82>B5&eG0{^GYOe{D{OMd)gJ}pg~b+rvKg&AJUbWVBEHM~>+0D;^=Bu0|`!1=TI zf!_&){Xvdpb2r4Y$}~@(gqGnp*yJ!CAJZqqucx>W3{V!wMDc?1(Tu=KEr9<~G*u0a`V=-hA zvNW3d20jIbwNVtUq$WRQuW3Pd_w#?j@^{}%DNHMYl8!5$bKS#fHlSND9Ia~T$9SUyTa zik!!SHYs(!IQh8o>dE&;0Y*HP82t^glO`qM$-`J?q-nuyX*(1ZWLDbjN01J(tSm!gfPQzw5gigl*n67e zZ8H3weK^0C&zBjP9Xr}Iz}$Y-P2-NW7`QJB?^z!7@A?O8Qq)&=?4cgoOjY>SHN<@A zPc3~B;LIH>1g51_nNZeOniSN;DvU_+dQ1V89o-t9(H9pUWF*h1_;nkwEI$hSyva`% zBC^sG4rIwtaQbY<{y} zsf@3foR6BX4QFFtr4b#HIc8set(nOth+lqz8XNM9CKf~y6g{6I_ZGCOB4R;wD1pkS zMW6iy_}s#BJ{}eRAwkoIYolK@DF8$P3qD_)>Q%j|J#D6b7r$kINw`5@aCKJ!QNJX^ z=QE!`-T|q1^kpK%smIMLTtKYLUAlkVRP<4x{+ zkECv++_YXhvRfL7((l#POcT8UVm3;jVY|7THna8r8mzvrQ+>uydo8rH^l$wk(<#Yd zcq1;%-GDgE<6T-|gn$wWx_30wo@M7=w#(Iv1LYKbjeaT--uytlidB8cc-19i@32s- zn1fN#G9=B<=}&~+Oj0$Mdy(w=>Cb+^`9BLVm3_E&U@=?II?Bwv!Z~BF;ua^lbw>Q3 zvAWA|8U^;)G%BBsbW;ZI!naaQer13*vVXY#LR~Fyw;yD^CSi3a9vG zI?UK`d$4(D1ZmVhn5TUFY1XmYsFxY*d4GklR-P*duMjl|&?>r$&VT3I5O#sefi6W4 zWQd|RbrmNIJ20IQF~61?o)vr0OnFS!@p(uW#?TfWz>DIMRs0aeAxpf(;c`t|8u z0*UgK9@M-5i(ie2P8kP!KNI$Ca^5MW79=em-DWr7oA_1Y#b`lRRVVKRPj1Wn8FTfP z*}j4`u1Yy}#Czv)D5g{b{M4lMaN!>qbiPAsE@`#7gn9}La$!-AnKjCH%j*44n zk>31aej7)2_+MtK%&YzK6e6ZhA}+?(0|-s3_>W208{1Kct`~gm$zI2Z+xbx)BRxjp zh9bSjtR{Y8jY(FQG8>WmN$n*DbC8kyzDOppDPrb1Z~^{D#=0H13c7ZO1gzoDox|D1prE-5&$5gIkpSm z`ApTvsd)$NlrV)|hU{b*18d9rZ(Uc@7Kb4PKoNIyztGHE9vWKg6|v_c{Ol0?2_Q?R z(bCLkgtkvgvK{>^koTz1>6CTawb4Ca-n#+y00~TSh7hq&UJa86$|f*yGf`rh(LL9q zQLn+VUB=XND*IOw1&v|MpvMRm5Hw0fY0m;K0H?sqakaB2O#Dnm0yOMu6DIG-aOHWo zdP1@%G}tdu;U&4-41RT3WJx@xL7s;>Jb1_>M;&%?uUQhCKbAcIWgTw#@xD-QV;e~i z0$(@el(ddb37HF-tQN243cShg0kSl{;@5T0LHAn9{56j_H?TEi6^GVRYJIG;$bvUB zBZ8g``M&-MikaI5|T~zHAKLL)63(sQ$5{EXj?D+p6;xV@)U4!F$y=eD!UOlaEooIlledtlYxn+c6?(B1t{mW%2xje?pd{FdWn}EecFMao~5nsy%(>w zm(y~}8UzMQSmXr5v^*L5Rc1cPUDI$t=~+{wp8%!9_#4b)tNC8r6u*v`hIGCnF5F5c zt=+u+%~m87Qcu+*u~w~g^P~%Z-|n+Y0~Zr~W!!F-($l2UM%cNc2tIB_N2Aus?d|Sm z?tFJKfD}NVnbvV#L+tWv6sAV@*mNb6{QdR zcHf|{lvkh)V2S9<;qvg{VTlH*9;BI1-7^kg^_!Tk|9k>uB8iSA>cYBf)*;lohsvL; z7LR_UH%Mz5HGYYZ{DDG2vXCfbVUjdJ_2Ieef?~_3PiAtXtk`+3a9Yj7wQQ>R;GOYB zAO9dnzKQAfwg*z?ysSGH{6g8uj`#YI&qOEFTE?P;Xec$#aU(4wl3BEKUq+`ELmHoE z#-%{EC4p1SQk43y}z&vH!_QiiKii!0W6Y-!X1iH6aD>)@U!y62) z`~Yr!!2`N{s^W7&oe=pHRt@>dB+V1QCji>zGB4N9pA-g)No`n}7O1xn$vrnwv?i z)wKMy{FK0|Ns_9Slh=Gn%5qt%#PoHqAKix%)hh|Y9*tB9BH%9Y7sS#mVJq?@CLK#` z_5x9ri{+d%$@HNWrQNDQy&ydz6##o*c?T^2ZSjceKc zOxfVXFvSo@{QaRJ2ExN=(@scyCO$QGbF$EKZkL$Easj;F5ie;bO|wOGAF=@^IPmA5 zu&v7?yJ7$Gt@Sx~+I-uM7yR=Aizcw(1=fG+w72O7+{Pu&gAbF4mYhL^N4N99ITD)8 zE?rqxou-8>8#=HZ6=uq7%fEb`7s&+p?-=SPcxg!y64fV8G(*?CR-|Oa-b%b`Ld{{C zc?SmhYbN`GY1~@ZxD;=H=s*NY z6(${^H;3tCU?NdovDPv=TjiN;t`cxL8L@{HJN2p{wM#Z~kx$JP{I@xy34WC;Dtc!S z1Jsmp0~r?O+cHRMX7XhBPu1E%=KP5|_n{HfmEF|WN+C|;sPoT-0kTuOOc7dUbS=}@ zp0q?8##TOzMg50&96zgOrqlXrfqR#4Ri`yBUaFbCBrHQe3gk{WGf<3RtAq`q{e#e4 z@ZiheL|%f!g)Qr5O`26Nmcg`Qq>lI-ff5w+-*t7@TnlGl@M-ql?tW?oPcc7g!}c{V zcV@Inx~TZd+)(dJ-DbC@`Y`s}oTQTzJ#es@Zo;+*{!0Bio)D#@nAfPg^WpuB^h})0W?a_0(ZiieQml;lI$}b;B zfTwXgN-KKoIhrJ8456$t2!kJmSyk9yABtcNjz_K$}Pt?@e;i^!gEn3fd;p< zFoNTCYMlG`Zx(_=FIajP<+8M}VXeiWUFU4d*4O&AiUBnMlv_#(@PWRRqm%5UY%B=3 zfJ^)us=X=$e9>y#)WP<5yW~ou!qcr@Blw|J*PL>g|`JB~aeb0HRb{0Es=dqf44i@g!iCgc?Z!zPJNYW7u-nDbwuXJ(z)?odQl&gC^#^gl*ca+EV9t?_Kkck<_-L)vw^%Ho zI#o{G-H~4Bx^WAv*6v}@H!gm>hKTW6UdbT}0U5Dz(Iq1e!vHxvXMmHRqlxsjCC7cY zed5Nd)ybsQq}ZNYi>2knQzloelB{&xGI@-?{mPpB?(cgo0i(=^NyMpJEi!@3$1&Nu zOOvv^lkalT!8UC~$OlCD)dRehyM%qVO!kKK;#5QB`;UZ&4nj8rB^sEL{8hd32O_ao zUa`;gFJ&6;psgVG`V%9&U3i}UqWL%9rf1DI7QsZfJs%fcIF`Hxxqbw8Qx`by3K@Y! z`81O0OZDF#rX}gzgIW>_KiHW|QwoY-U#}grZdK^)@a13*+R*n!S%xCXr^ckpT3CJy z4TS)+rBEGMnY-;p{69rnG=8Fu*KZOe>96x^dBFD6eGuso-fg4v;8rX(#CkibCq>5v z9y;ik5Unl|qGgW75;`7CMm7<>TSex;_C`IER9_)0)l;-q8mJVml6>xq*Dju4P>N)| zGJ*H}kFu!#^SH1lzy>=kwkh|Sx|$)UtFnj37a_x!w5BT!w1O+ofNp#F$dNa_Ex$CUUv3_wUgO892l{3RXP-M9OgE$p)VhNF9G z2wdqmlSZ%E#e=e{;muLa!(H0Sq37|bu%^bcJZQ%FA9R4f_#D5H z+zDWUS}CEYIrbx~GW~(WBFxz_S)-w0t@Kf$S=4-W<+ikXMBLOUUX$|ql?uJshC=>7 z?!A)yr#-o(T~B~&mxFtx+oA%8r$Kg|!6sp2c>jkmX%d>bTI)u|;ilo=>&%<5$-mkE z7Lh;($GR!Fl3Guc#rw10jrWNX;Jp*A`|RIPUl6IM+{U9=T$@H+yp+q^L^amXfs-JE zLfO7H5o*-n3SHJdX+>?2c}pdzI5s72bA{_LgW^2TAwyK*IR~S3#8i=%7CRL2)6wd{ zLksW_r5>weKg(^}(0%ndw?02!JQn<%hF#yycAKj)hW>$1Yxw)Mj&n}uU7U0Z4qw9E zE20#w4=0;kgx?MgI0I4jiPnND7;tXPZS;Qr^c{}N6?*j1KtBsRtA&l6dnIsc(~5+B z+nd36W`(NnZwerkMhcV7g+70Ox#$*KwzqRwZoLSPg|4G;{B(8--S!7=MJ@*Zl%xqP z=fM)y%&89DR!g0qC$@V?*A^O^@I*9gsCF^Jcw1if@L7GVdeeMb3v^#gI3f=IW6GEf z8H@VUX!p)_iUn`+&_AfnDb;|frKy%Xh{EhUFNsk9CUA^}l!Ho>?N~of!z9sW=svS4 zM?^x>S59W3|M0w}{>%X`@oPdK{)e?5%2N>6{wOm6%qcWkULAFRD2#79$qw3Q$&J6C z&eOK1HnrAo15^$uhlr_&yS(|m$sL#K>@a9|OMuDZ%;Mv)HWZG9+fOlb(@X&l%~oQw zNHO>>@+hIRQag204x8-5**dEleCE~uL@_!Ou~8b{f@}?Mv`ZA7`^SMTh+}VoAf%vY zO((xQF~5Muc#$?JjWW7EA$*{7ClcM*+2d^s-Gxh1jxucR#9j0F+@=}-w)Ln&*r9Tz zN&FUhYY-jgu97q{B4zE&uGP%=yW40{iH$YUBk!HUD9Lvtbp&CJWfcAnQsUTf$zaW~41U>lUI=9T+Zaa~YOqk>J zTy17+z2P1_`*IT>`YbGYv`ouP*}4|568zaSZ%U!q+f%&&lak;Tn7YI}8PK0A^xdEJ zNK~&KH7Kv?>o7+!5Kr_VwS}PaEUADiOu^-aK1HE)&`e z*o2u()5;DOHg%WYNX9qW%#%Z~MpNf#VIsi~4QIY$XeZ6@_>aHKqO#t(mn*dKpN?eh z>r0MZky1Z8^tEBBhbX=kmwEfE_vhNjeAj01;r>+BIo*U2Bb77ICm;X0N+CLTQdDN= z{kfNEE&A9V{|R6St0a`S^6fs+p6TlHuD}~%0ETx%d2iF6g<{~{@|FB>lV2tLC`rpb zo36PYMJr-j;$pf|^9Ylj|+|(g?0}wiVQo@St#-@ zk4(w-uEZ*k#i$p!ssC4mN`;i@ZrPMmp_$^7E>w#H>jG+e{$Ysw`8tifeDa*%>yGNY zWQeqB_Xl97Sq|R5(}AR(-uiOF%mpn~(Apa8e%~g0=#6sNy$m6vLyQQX4Wq%1@M~@ zNU|}#Tod_vJlKG6D%yi< zB$Zhdwo03SO@yN2T|I)Xd--R`TBL zR}%=g{J}!cAcgwba%#q*rfB{?7EN!e;&ooY;TK$(l#_|$6F?`IMQ*X?P|!vQ**VH= z0(FkI&(K#@^e0Ee4U$KUIPvD)RkntV@oG^oRj_yR^t}0wp(%u#X)#~n$9=waqd12o zvQV3V?)7AoJ8*BO3ck$l)pYTPb9|BvV)*f5oBy+KOS|3d)ylC&3~&t!bwV&JNbSU zwE^VxLjClgNcW$Ksql1^cHqzb{=s;Y7vBH~iyg6hD&g~K61ptntgX`jt+w}yYbyG> zMS~y+N|7QUB`Qq_UFjuap(-HKtI~TF2sI)If)wc;L`rC(L+FSIp@-gkDAEZvfe>%* zeLmm!`|dga^RV-<_sV|Qd+jyn9CM7Z?kIlH{JCCoFmQ6p=0{}~p~CuZBz9@7sjNl6 zJ}K{EIOM~~Kfu|p{?65qS^T<)*Y4Z-N2q2>a=j)_}cW;EbadBF@cP%et zhTbnhcsp&PPg@i&ZZG8WX-1}Jsy{n%UF%~UjWt#OjaivpMLq% z$Xv@!Aa|eEg?CJ!RoA=iIR|ZgHLWZ&2Ak~v1B9>_wlZu_quk_O7_y@8PQ?BLXws@r zHB{yNCA}ZmbcGBtKHBy7p71j%ZU`NFQMXn3B5K6AIW6)mCLQ&;@94ZtnZQe~z%ORjEc{U=)=bhSR~ooSTabl)rWWo%k{s z7kS3-<`2g1JCMe#TI$Hj9#r!<0l4XoP=b#=Tg`tjd-pS~W2H^rvg7S`HU|z-Lm4*R z6?Dm3%ePTSE^6QPuwjx+78DGuHI^6m=bTRS1M}^N@-D9gY1c@YmZ~LWpuV?jHw4xp zugrqn=<+$gy*xYn2M~Ha0HhG8sh^m~OiA}pSXjIcpjtxI){m{t({l^>H~4{d{4dI9 zTh%i??zk4jYJ|jNZEFiB{*)UHR4WX9csvEiw9)D$JJ$+Z>{V-;U0xqrw7hWLGSppT zT7tGNPz~m68leu^5eT>blIwZ}w&Ox6v44PoQ;7omV&_-^Tv#>jQciZm%kXON9M{C_ zPW+IA!n%54`tM>^?$aB2BlRp5-m?{9k9MZ!go;-cDHu5k}B=SlDWuXGm?R}=^*I3CCFdsY;16lOYVEt0mn{GWjV^H zoHa@rc|$^;N>tL~k1kcne7B&-D??&+$%06q9ouTT8e{w~iJ5`hZxuVn;4goyg2 zEtZ8;x@u|u;~D!8>-1d4mn30lOr)M2jp1P?eu^b84&>o?rH9Q4`0Sp=sEoDug0^xvX? zEQ2%oVK=ASu!FzEMqWL@>l|?mB(65KDp_(H=C_V``Ec`SSij64(iT;+eI_oJn9R4i z6$%ee$vHkG*zH4Hxd*(OC9_LVVEOw?=-RKIk2 z`ih)U1@sv3k%hb&d>3M1^8cA(%t_|`G*@Fu7dm4zK*WDfAie2M$*Rw~{XGaIB@tR8 zk*{D3k$;*$M3#Mdh)w5R)|p%hV6$%R;4T%Xu)C$U(!>`MpA)1|W<{V|W^}j*v`={K zHX*!;h}_8r?NwHO)k+gb#qj_ z$q7Dgq$Za37y56I%bM+$lY`BGYfQj2u0>(50#eveNIe*L@AbQUMtd7JGga=pac@I& z)f=e&UEz6#PVql2j0>)4-VW=He-GAY0VU*1%da(Fp&~BM@3t5&LG61Q=Wq5>3In#b zfFSl*lH1cMXxG_YZmDi{q%z zs+$e&srOEd`!#5VKMH6rxX#ah>cu6+_ih%w`&EHhhlsjJ=0q3z6@oTaacoy@aaeuF zK8Y-xKE%d+$6aL&^kY~+?i=JF)%jc2N3LS{2R%4XsWfQCN^1XcyHPOkh)R;cH31(f zHEUu;4U^^$h8nwL*Z!*Bsd~P>z^hB}1NT=|oZ^H$mxC1*KU}{upf;9ebad*EeQcW^ zOJkZdrM}44vE}X7nO)xz>kVMpt1x%Lcex%D|nKz2}X#YHLPZ-XA}F@AsruYy?X? z(7kAI;G+8Z04JA8+OC*vNz2;S2tdpkgnVvA?;>|IFy=IFhK_OL{1{$N$yk2RfeOSw zKqxLdel4AEK_ZkkrhI*lJIX#%nvdku#V(N#|F9j={*XFSDq#6gux2*x zmI;zt-@iD=#(9e*!u3gMMT^rv04YYccOIx-=v(P<-X-FDcgyuIBI}ymDapm|>iM%6 zcWb`yum|pPG|9s|oB^ncq@$5HQceiy z8)n>@TuU75_NX-F=CD|HZyHcjwR}F8qk&z?o&?o@tO!B56O(@NiLmKF_or54TYb}O z(f#lV>-@i|yFRW95aioT*Phrr-pAuI_!FaP*~zm}sN{h@jc7j`mCEb)9=hR%pUBl# zNQ@;j43u*TSRmTuN>;bNMm`t5$#T)Z(^qNWb__Bx()j2}{)d3s#`;fn*Vp-c)uS*k zIFoqrOPd1VPEq*KvB#VJn%02!3oK&NNq9}Bv&imgh2M~+W3WKY`0-Drr`bLw=o6xS z&G;7?U7x6?91!yhN@ktb34`>i11_Dx=byyY!*E5bH#By%U0jO?q zJwVInI0{&b&baX63TK1M|8{Q3OnpqMs=JQiJKq6Gw)C)FN+txcZb>RKVrah!Ep30` zcV!L?_Bfu>Bq5;)3}0=A*1vqaYtHn-$=iu1%56=HUtR8QmK)%+yF!3feL!^JkrP|D zf1SVgw`XLYL(-s)UMWBrof^lj{m|)U^2XKl;qtc3qAUx|<>|cBv{>@r8>gJ&(oH?( z`ZYdvt?+zGM=EQ~ zk-#@Wa-Fi*-EFw9^JY*x^@d{*+6q^mcY{u8N!8R-Q>L=D!>*F3qgTNiQMXsF*)&Yc z8!)axtRu7GaH@6wXPv$XIfN{}KzM}ezy%#q8RZj&NZ&9R7H^GBEYUX|zI-Lu@DK26 zv8~zcFWzf~dC#R3n-IWZ(&IDsm%tf0lPw&CF<)fjB1!sW856sg74a}wn-mm>ke6C* zd-D4}Q%X#}kqT@(#RKPV@<;mZ#IYZDf|^x=8u3@6@Kq2mGld2vo08*2kXEzy_$|Ds1r^TQXXgxOPjvh71q%F6J%5<~IOUSIb3#(Dl7NI7BDHx7>Db8+l28kukQt3R}zR=CFPluVSd%P1dwJy z<#FP^zimE9{Tao^H7@>HAH_qZ{#cLi#$Zb~vYXm-9bZj9kyv@SB_94^I^|&Men;}9 ztbDsDNRl?{WT8!Zwc0BxU>7>P75Ueqn&?W^#)Z1lYsiD+GJUSwk1CU+x7-rAcH^^W z|BA(W!HFME{ZSX;OB}w~42qdMb9@|Y5f)Wb2y9|9_|_O+x3Cdc<~vuAaFwWWBwc{z zJHma$mRocx0-G8l-`-ess2bE?jX(B<=@dJ2dszMPE-vOZ0itC7j_*bG}kR)<4*6 z;rID_)`%3?r#~1R(GekuO@xMINBa~Hs=)B16do-j@54T|+6(=_@Meq8_l`&>EbSYS zA|FXgFGa)aKY>MACnosP%GT zm|x%$ETC&aN~0=0CW0k^AuanlQEM(gx*Y(9yAy8I z-d~P2_Ke|{98{z6sC~Z@3vE{+o!&L?RYvby2*tu>HJ|?2FY0)5QNc7&g$o0rtDjCB z$|+xNa|pLVIgu1=79?GaV25#`&Kaxx6G*e-=NIr& zBL{0#DJW;9K6`5!#brroFMOYuT6Sy-Bqa00@PnzKcB3?Bo7cp#mX6~^itf4rkF%cI z#O5~#pi-)}p`ap@4*b@zmao(9tQ0(TpyS=|K;OJqEBl_4KOUI4&wfvFfrVz#bbvmd zm(#MC9E2~qGk@03*^-Z2hsvJOMqf(J)%_K=dT|1Jen6uI6t4giRXNg?l1*;=7I!Fp zK^;l#O)R+7L#yLQ?V*%<{fs+OrK3ck&<{sJw_^2zA-eOqWOPE7LD4x;%K0|N%6F@% z=W0j6KhC(r&G|x84}dOFxal>u)1xP#zQ6UTxmfsh115=wfn5L-X94%uo96o+sA(ke z)wJF}m}f@lL-q4LWfg-!4+4|_(ws?S*!N-xO8Jhc;~Ty%pI^7jJ_#3or(i?&|A_zO?-81O{&?svFo|Plxo=;Et84;H1E_S#I$<1wdzbMGGy2= z+HQNCLb_XHbL*H?dErKgT-m_fK-~!I&`9=h;|~e zrSnOknBD3k*E{Twi;R@!`OU_W@`Et_cu>(je(PwzxJS8ZY3sVwU)Uy?6Ze*>up(Vc zH^&RAj;2D3ApSjSuMeFHjgT%3s?T&btN`~O>e^K>`WsA)4Qwle(#>*1(O9HtrG9Xa zX?y_B_*P4>n@Y1P{sQ}fNUB-JEJyF>wPNGe3|qOJHuGHA-sm>BK>oyRg8IuJMMjk- z*_=e6QnYnTu4q?Ld#Ojct10nrHbq#F#)L3emm;iuo@al(##z@+yqD6f)AOz555bwY ziRNvu{KX1ysig25V&`V|^dFVN_47SVQS-FpJ3j9aQrk}E2)^Q z`#zZcZa91Ku(HynWK^2XjJoSlQG5|78WPG%YiGeKBVutZ-_m7KL)61nQU$^G%qhmU zu9O#v-UF)B*!e7%lYzY}q?||2ldL=boZs@c+u6n+j@QK~PHT*>qLLkKsJa;aERl|n zi3pe`h0YuEpDp>CbXXmgLH+JEG2ycvb#r*fAhE?+oAh7xIsgBhKLXDo&$ULO4&XbK zmZIC{660&2qMbdMQzhTt1q)f-%LJuP`k7-7+EnuzcYK zNAp5?yy%@QG2#Oke}44*GbKTIt)=&De(0TBst4EI$#?6Z?=&oTh@qgpKD8XH=Nj#0 zoSpc)ljt3+73?Wa`@Cu0Zmw)*r${d1lX}%s4@t&?qy0epgs41m3WXaEzm4n@w&r<2p95vu7>Qw>9A0_rhl86i{VfXT70xHo-h8oHQxhL_ zj7?q)40^DrtD!Q+%-+@D+M>*bF|PR~4sch@z0Z_g^?%|62R-Vz9Ysxeu+pRo7{fWq zP{I4A`omJazfvMxpKm3ERy^CPa0+-czHBaL&Vq08?)$84>B~>LIX{#0?0`f*I=Lcc zs)vpHkJE{-&lM6=`5cn`oUv&KNRxDmv~WtZAWMV-27cEIx)M3UzQ-Juf+CcmyPR z8GH^3?x$Z^sO*LU?O}t1o>8WwAZ(R{2(wE6s4Ys)Vm3nvVt;B)b3 za1aISen_0VD)@_kz6fjOmmx>3f)Lsds#t^LbHZESv* zQU#u(1p3i1>+CNDCug%D4gzJ+lFf<)8xVC921P|yX&3|IPhmIsaf3_QGJ`} z&v0R4)x7?Gb><^)du#=LvKcWH28)ECFf+{k8z|tRYkL4b`w9fi0@HNZc6{xiuLs22 zdqGUp)t+mWLSiX}z5=rKbwWZ^nzILY18L;BIAW%nESIFFAK;cQ zOyu?lo+_~m{ZLeW9sZufEX4VcM>oznPE1C5huSdX^7d_`3Rm0Mr8!0io(z4e_+nPU zKSuySR>oAaz{g;wlN~=SK3GJ|r&6vW-dN>ecYSd=9p~T0n=d~R^F~k5X+>+d$Q?>w z{w$&Aud?S0DP-_9zb{AFszeKjGtLoXeQpz+^Tcw8@`kXDlmM#@|3Uw2;Oc^S3i7 zz2h;iITq%ALBE<-YJC*hm~fYws#NWFF-O_m@j&M`sT-j6GCgmBrrZ&n&kbq8JMLux zPCFNPA6am?zRCNxmWMQ(clnWnqTsTQBXuzisN(g_`s>hvIpyS%Ve~_ z5ftiJy!CaVp9bm}oZsk6c_v@_g|9JUcC&4Cdxo6r22L%iRI7sTg7sd?EKKaGCHt&S2)^l$i7ob z{ql?p*HZ{$u1NIv3q5?imGiSnBywzPHR4;uwbCK@RAX8<(ZkfY+hZe82J->vQJ2{o z5WImcDZTCkb2;B=7f7-=5g7vJymXZ&=4_E>I$z2Y2gJX7H3>;Qu+bwG>rLK_8B z2dZjs^DSZDZ*_J<>azk445CAb>>IH0IhweB8CG8bzj_P%ZocWPj%z!)i?udDq)Z^> zX$PgpDfWZmUo~vUab=Z#*Bd(0l~dqZ7||p@PY+rgX=m{THyq!jmrnMi(SY=jx;G}YM^8%|l*u5h{v=qXK4DbvL{LErA&%!j4b@0CaYiua z!v*shBn6gYqc7oUdV+zI(xmb?C@a4w*11AojluQ>N;S$5YyP?d)jeOM?lRt_$_J3l zP^C)Tu`4c7kvP6XhzlJx^sKIYgB3&X5{nwBb5In>FL-?Y@alPW&my*m1qzu?3k@}c ze(`-()BvG(f|?;;$8kZa>y-lXhy%O=Ot- ztNh&t2S!AO&AP!z!AjaJ+I@%R1|{p;EV>|~Xo9cr@j$K0nqf3}s(3OFKbmkg+41h& zmMDyC1}ES6@7jg`yVu%@a;4AlPWtb5gUQC|UFKroZ5s`^$z+|_~0Tq#qJ$CvtuuvG`|v>7@w$6>DpE8tYEaKwT_6a0{@v-x1t zW!d9~3(_=;xFtXbJTTeMI4n2*nmSqMJU#m{4tC*m$>EOqQb<4Q?k+<4^-z-` zVDhezNalfklj7i(w;q47=?$fU6!dcLu5M%>oZ97CTQ8sRk1T@4SnW33>7%4mrpUFl zz(t-+A9;j+kL)?2z6R;RTkPZ^c&Rs5{ovMs$BqoJAMq3C_zIV!XhrMtrHlpx4XTsJ zpO(c(E9bMUm69}u?06VBZF2_;DY>F%Xuo7kZbd4xU|;A@*j_2;LGx!9O^50Y29sBn zeSQ;rDSQADBIg|9yQdfA;(H~HwR|l6D8s}^cr}~LeTX5m#1s^Ap*gMGxpfDY$8;cZ z@ze&TE`AV?FH6@pboTZK+4pNS44o2lNSDp<4&}IOI=LOnZ*!D}0oid5Nyy?e`+BM)Pb~ zga?OE?-Ib@nU2#+jAL3+f7w5$Dq6$lgb2AMH8QY=n`w2cos~Cp%erS%_Q|ilhOmXN z#z$k!Sp2t6M>7jQj4JWrpvJTG`r-?x+n}C}Y{IFVVm-iL>T1GtKB7jlaqX5+2STAvis^w0S_7iB*MS(0Ll@6=IHDxoZz zDtn_T!^30i_XpIh)Z;N^3|mqjC|x?Z356`&-SOm4KK0$K1vBbiCnf=v{{g6J+lgTN z+$8bixD%*WP%vMA+wA?~8=5JcWeUrb*)#5vN8qYp~$$q^yu z2eXKv7Of~^KEZ~m?M<{DYTWXj^U8{L{_BNYVmgOVdVN5zLnS<1Xg_34?SsH=Ac7Qe z>a>xffB)|;EdRy-Xn^QEWGC?JOLyoh#{~2=LmdxQgAn8XT>hWsqsU;CqFnox)Es_M zrd+<`iLK(;fLn(&*mrm__R1qZE3VHlvp$euM|9)nI;Xj{8O~YWEEB9jpme9(QTCtq zDgyClJ4RF0{P|JB@?gc_MwA&*CXf6?q2n|{3N*$E+K#H6@==5R3spvWh|KqR%j}E_ z_VF%^`8$>6(yvovR(1}oy4Bhg{R+@)z0#C%MGsaM!Ub@+=Bbd9kTAO%Un-l3M~@eB zyrd#Wrt4=-mCc9mO1+P&5+=^~ZaDR|SwftMyEAj?+N7L$! z78_4)BySb}0uJ6EbkKwVvz;L6EM|YOIQ92HcTH$oxr)n=jEmykTe*pw(3WmJjW;G& zXV+Rq9V@x_W9loB3d*bpmfqx^XBzi%e^QH|XE=wed%4)Y zN^+t#$RQzK%c8{lT*j&GwvXM3#r>YyjMS4?;O@#1BK&ry_4)s(O8;LNZpllX6M4#s zmYCe@Cg`>^6V4PycK`atb@SPP5Z}4W^uBA+F>K<%MM#L*3Ml^8J&~!G1=td<-1iIT zk@cN+WA3yvXHcTF4Lo@LEp$Bg#+!ctIs8ut2bk1U2eGN0Ryj-3ya-b~Jl4P?N2tZKXq-M>k@c;}Se=$;=w5t-4Fotmh<&T@$J|`? zMUBBrlzG3nyPrH&z1sz9thSX4K5MEJP{b;^i1hQh<1E(5x>y5)K`SX@`R7a%5)5h$ zPA7(jbEh11$`sA*QK=>Y(_i3QPT7pXVgYZt%;!``e~_s;emO~uCfjJ-(Q3TtY3qo5 zI#67}6Gd1|n7{TD4jiv0+dh5oMK_()8r@Q-Xz9U@g+P`gLmteJ!WffWSbEUuKTy#5)y zGkK~eH7c zjmI|8yuO*M;)TLW_U& ztDmNdMx_7d_pMLAtE4bHt4mW*MVe?)s)xlLJ>5dPbsb)@5dDp>`C~iY@9rKWubM=j z;7~CuDdNeR7{tOdj%a~v-ihn|57)aS$1ptONAyZKQJiDN2)umbZIP9U8S8MA_g!gw zvL+H*A9rV0%&*zC#QA(|Rky}Eu!eWzJqO#Ja-}YToJkxTW?X^pwuk>cC^sYWIy;fU$h ze%+6rO;fYxoq|0Q@vp<&+jhTUfM0|^e%p=F?~DGsnUasi-i{tXo0EVuyfR)!;gf9dzq1oeImqt!bS?Lq_yW81ghJkiX+=r~fahJ>a z#|x;jR&&>fU>E%arO!Mu&lYHl(JXO@e>ja^=#XQR`dRkT8!vF4B-J0F<+%&=a9m z4B65*WVq0NuzseheSTquwVU~P#zq_FBkB2yx1*w~XuhB3PkaI^kY!kkUKUs^y;ds` zK8_dSuc~gTZGXx71hGCNFOM@LN2-oj_{|DaW3X ze&X;t5crK^##y;=J3$?2Iu7NJD7bzuy1sCX|j!6PX3k5wp7jo?AZ#N#;w4suql^{S*g;s$JPw$ z%VMM(u~N?7(!cs180-!OE?zMQ4}*`2@SIBzr-&hm3G#W4l_y*yCzbNl{fIR~?HPD|~PIrkts-Ez7X`yL*A{SPpJAx5eeRKkBcUQw+Pc=2pU zZas2rAT1Vq4<|7rRhtIe1Xb(Ev=5*A*g!YsZM;6~sgV^U9bWM!C8xV*ZTD$X7d69a zqXXX&t8nBId@1)n#0`1>JUNhuxC7b)A^{B_0y?ObT%|#aIK1-79Y0Ar(cL zik(%+Y}DB_M2vmFj*Az-B@1^_&#<|Xm1|$Q!QB1n+s6seWZGix2qE{FGY^YOV`3TD zb` znwkD&`Ydl>!Ux{mV_~R!eXP=ZQ*&LOA)5Ve+_|I0ZH1dX^^VZ{KwrEJF7f6Rxo*UZ zzmT#Lf#!A2Y_GZY(+=Xp{17OUsMVwaWPp?|o&EekHvPmzgVFQ-zTK;k+r`p6FZj8* z+;^mj{)L|nW$l2Bs=b}a2uwVYiLVz{v)z`yGlG_v*DK^ugs!7v?V71z6aa5a$3*jA zB)rxNgd^T|%uywte)sozE|K#WmWsUJ4sWrWCF&SmEUS+(+4Fc5I5^nU%2m{VEAHhH z63;NO(h~~nbAuK*2J@tEh~I8|_b!?H)dT8JCTWTGo$@6zR>Pv?-v}r;w?~&=dv?ce7twNgYD$Uy`o#jEI*pOjtRH)N z<#hYDy>q#XrQw4HM6?RKSTV_4#`glB004IYe9A;?pHmk*eZM0Ne(kq_4;fLs5$J%) z#y1!nbiHvL)(z{o(64u5{PiHP#?;T=WRpc(O`4C4G|u|S7tcWC%YAK44>UpxiF8Sh z8Ar;Okp&E-6}ogZ$5SK&0ab!E()}y06z)Ty@iRa3%U#72U%UxM||1hYs>67C$Q>YvRMRvhss^&s8SLm5dT bdF<`i|8PF=e*sVbzk$*JkMBwHZ}$HI+`CK1 literal 0 HcmV?d00001 diff --git a/screenshots/toon.jpg b/screenshots/toon.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1af02a6ba6f885ec2b17dbacb187549ea5d2e6c1 GIT binary patch literal 37512 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 R-wsm$d7%1>Z}5-Fe*(YtmInX; literal 0 HcmV?d00001 From a62f86ed6420070d2e4c40fde7997c1ab20dc8ee Mon Sep 17 00:00:00 2001 From: XJMa Date: Wed, 12 Nov 2014 21:03:11 -0500 Subject: [PATCH 02/14] still has normal bug, add stats --- assets/shader/deferred/diagnostic.frag | 2 +- assets/shader/deferred/diagnostic.frag.bak | 40 ++++++ assets/shader/deferred/diffuse.frag | 8 +- assets/shader/deferred/diffuse.frag.bak | 10 +- assets/shader/deferred/normPass.frag | 2 +- assets/shader/deferred/normPass.frag.bak | 7 + assets/shader/deferred/post.frag | 33 ++++- assets/shader/deferred/post.frag.bak | 35 ++++- index.html | 5 +- index.html.bak | 32 +++++ js/core/fbo-util.js | 1 + js/core/fbo-util.js.bak | 10 +- js/ext/Stats.js | 149 +++++++++++++++++++++ js/ext/stats.min.js | 6 + js/main.js | 30 ++++- js/main.js.bak | 30 ++++- 16 files changed, 367 insertions(+), 33 deletions(-) create mode 100644 assets/shader/deferred/diagnostic.frag.bak create mode 100644 assets/shader/deferred/normPass.frag.bak create mode 100644 index.html.bak create mode 100644 js/ext/Stats.js create mode 100644 js/ext/stats.min.js diff --git a/assets/shader/deferred/diagnostic.frag b/assets/shader/deferred/diagnostic.frag index d47a5e9..e3eda38 100644 --- a/assets/shader/deferred/diagnostic.frag +++ b/assets/shader/deferred/diagnostic.frag @@ -34,7 +34,7 @@ void main() else if( u_displayType == DISPLAY_COLOR ) gl_FragColor = color; else if( u_displayType == DISPLAY_NORMAL ) - gl_FragColor = vec4( normal, 1 ); + gl_FragColor = vec4( normalize(normal), 1 ); else gl_FragColor = vec4( position, 1 ); } diff --git a/assets/shader/deferred/diagnostic.frag.bak b/assets/shader/deferred/diagnostic.frag.bak new file mode 100644 index 0000000..d47a5e9 --- /dev/null +++ b/assets/shader/deferred/diagnostic.frag.bak @@ -0,0 +1,40 @@ +precision highp float; + +#define DISPLAY_POS 1 +#define DISPLAY_NORMAL 2 +#define DISPLAY_COLOR 3 +#define DISPLAY_DEPTH 4 + +uniform sampler2D u_positionTex; +uniform sampler2D u_normalTex; +uniform sampler2D u_colorTex; +uniform sampler2D u_depthTex; + +uniform float u_zFar; +uniform float u_zNear; +uniform int u_displayType; + +varying vec2 v_texcoord; + +float linearizeDepth( float exp_depth, float near, float far ){ + return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); +} + +void main() +{ + vec3 normal = texture2D( u_normalTex, v_texcoord ).xyz; + vec3 position = texture2D( u_positionTex, v_texcoord ).xyz; + vec4 color = texture2D( u_colorTex, v_texcoord ); + float depth = texture2D( u_depthTex, v_texcoord ).x; + + depth = linearizeDepth( depth, u_zNear, u_zFar ); + + if( u_displayType == DISPLAY_DEPTH ) + gl_FragColor = vec4( depth, depth, depth, 1 ); + else if( u_displayType == DISPLAY_COLOR ) + gl_FragColor = color; + else if( u_displayType == DISPLAY_NORMAL ) + gl_FragColor = vec4( normal, 1 ); + else + gl_FragColor = vec4( position, 1 ); +} diff --git a/assets/shader/deferred/diffuse.frag b/assets/shader/deferred/diffuse.frag index 6792c89..eaff765 100644 --- a/assets/shader/deferred/diffuse.frag +++ b/assets/shader/deferred/diffuse.frag @@ -40,10 +40,10 @@ void main() float depth = texture2D( u_depthTex, v_texcoord ).x; depth = linearizeDepth( depth, u_zNear, u_zFar ); - if (depth > 0.99 && u_displayType == 6) { - gl_FragColor = vec4(vec3(0.8), 1.0); + if (depth > 0.99) { + gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); } else { - gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(0.7), 1.0); + gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); } -} +} \ No newline at end of file diff --git a/assets/shader/deferred/diffuse.frag.bak b/assets/shader/deferred/diffuse.frag.bak index d9d8446..fd3f36d 100644 --- a/assets/shader/deferred/diffuse.frag.bak +++ b/assets/shader/deferred/diffuse.frag.bak @@ -15,7 +15,9 @@ varying vec2 v_texcoord; float linearizeDepth( float exp_depth, float near, float far ){ return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } - +float SSAO(){ + +} void main() { @@ -26,7 +28,7 @@ void main() vec3 position = texture2D(u_positionTex, v_texcoord).xyz; vec3 lightDir = normalize(u_Light.xyz - position); vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; - float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0); float specular = 0.0; @@ -40,8 +42,8 @@ void main() float depth = texture2D( u_depthTex, v_texcoord ).x; depth = linearizeDepth( depth, u_zNear, u_zFar ); - if (depth > 0.99) { - gl_FragColor = vec4(vec3(u_displayType == 6 ? 0.0 : 0.8), 1.0); + if (depth > 0.99 && u_displayType == 6) { + gl_FragColor = vec4(vec3(0.8), 1.0); } else { gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(0.7), 1.0); } diff --git a/assets/shader/deferred/normPass.frag b/assets/shader/deferred/normPass.frag index b41d6ed..5df3879 100644 --- a/assets/shader/deferred/normPass.frag +++ b/assets/shader/deferred/normPass.frag @@ -3,5 +3,5 @@ precision highp float; varying vec3 v_normal; void main(void){ - gl_FragColor = vec4(v_normal, 1.0); + gl_FragColor = vec4(normalize(v_normal), 1.0); } diff --git a/assets/shader/deferred/normPass.frag.bak b/assets/shader/deferred/normPass.frag.bak new file mode 100644 index 0000000..b41d6ed --- /dev/null +++ b/assets/shader/deferred/normPass.frag.bak @@ -0,0 +1,7 @@ +precision highp float; + +varying vec3 v_normal; + +void main(void){ + gl_FragColor = vec4(v_normal, 1.0); +} diff --git a/assets/shader/deferred/post.frag b/assets/shader/deferred/post.frag index 2eb65c1..8d565b9 100644 --- a/assets/shader/deferred/post.frag +++ b/assets/shader/deferred/post.frag @@ -22,7 +22,7 @@ varying vec2 v_texcoord; #define DISPLAY_BLOOM 6 #define DISPLAY_BLINN 5 #define DISPLAY_SSAO 8 -#define DISPLAY_BLUR 9 +#define DISPLAY_DOF 9 float w = 1.0 / float(u_width); float h = 1.0 / float(u_height); @@ -100,6 +100,33 @@ vec4 SSAO(vec3 color) { return vec4(vec3(occlusion), 1.0); } +vec4 Blur() { + float depth = texture2D(u_depthTex, v_texcoord).r; + depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); + vec2 texelSize = vec2(1.0/w, 1.0/h); + vec3 result = vec3(0.0,0.0,0.0); + + const int uBlurSize = 10; + int depB = int(float(uBlurSize) * depth); + + vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); + for (int i = 0; i < uBlurSize; ++i) { + if(i<=depB) + { + for (int j = 0; j < uBlurSize; ++j) { + if(j<=depB) + { + vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; + result += texture2D(u_shadeTex, v_texcoord + offset).rgb; + } + } + } + + } + + vec4 fResult = vec4(result / float(uBlurSize * uBlurSize),1.0); + return fResult; +} void main() { // Currently acts as a pass filter that immmediately renders the shaded texture @@ -114,5 +141,7 @@ void main() gl_FragColor = toonShader(color, 3); if(u_displayType == DISPLAY_SSAO) gl_FragColor = SSAO(color); + if(u_displayType == DISPLAY_DOF) + gl_FragColor = vec4(color, 1.0);; -} +} \ No newline at end of file diff --git a/assets/shader/deferred/post.frag.bak b/assets/shader/deferred/post.frag.bak index ed7d8af..28efdf0 100644 --- a/assets/shader/deferred/post.frag.bak +++ b/assets/shader/deferred/post.frag.bak @@ -22,7 +22,7 @@ varying vec2 v_texcoord; #define DISPLAY_BLOOM 6 #define DISPLAY_BLINN 5 #define DISPLAY_SSAO 8 -#define DISPLAY_BLUR 9 +#define DISPLAY_DOF 9 float w = 1.0 / float(u_width); float h = 1.0 / float(u_height); @@ -100,6 +100,33 @@ vec4 SSAO(vec3 color) { return vec4(vec3(occlusion), 1.0); } +vec4 Blur() { + float depth = texture2D(u_depthTex, v_texcoord).r; + depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); + vec2 texelSize = vec2(1.0/w, 1.0/h); + vec3 result = vec3(0.0,0.0,0.0); + + const int uBlurSize = 10; + int depB = int(float(uBlurSize) * depth); + + vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); + for (int i = 0; i < uBlurSize; ++i) { + if(i<=depB) + { + for (int j = 0; j < uBlurSize; ++j) { + if(j<=depB) + { + vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; + result += texture2D(u_shadeTex, v_texcoord + offset).rgb; + } + } + } + + } + + vec4 fResult = vec4(result / float(uBlurSize * uBlurSize),1.0); + return fResult; +} void main() { // Currently acts as a pass filter that immmediately renders the shaded texture @@ -113,6 +140,8 @@ void main() if(u_displayType == DISPLAY_TOON) gl_FragColor = toonShader(color, 3); if(u_displayType == DISPLAY_SSAO) - gl_FragColor = SSAO; + gl_FragColor = SSAO(color); + if(u_displayType == DISPLAY_DOF) + gl_FragColor = Blur(); -} +} \ No newline at end of file diff --git a/index.html b/index.html index dd0ffef..dc8b9b7 100644 --- a/index.html +++ b/index.html @@ -12,9 +12,8 @@ - - + + diff --git a/index.html.bak b/index.html.bak new file mode 100644 index 0000000..7697d31 --- /dev/null +++ b/index.html.bak @@ -0,0 +1,32 @@ + + + + CIS 565 WebGL Deferred Shader + + +

+ + WebGL unavailable. + + + + + + + + + + + + + + + + + + + + + diff --git a/js/core/fbo-util.js b/js/core/fbo-util.js index 42abe4c..42ce974 100644 --- a/js/core/fbo-util.js +++ b/js/core/fbo-util.js @@ -125,6 +125,7 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Normal fbo[FBO_GBUFFER_NORMAL] = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); diff --git a/js/core/fbo-util.js.bak b/js/core/fbo-util.js.bak index e943f68..58a96b3 100644 --- a/js/core/fbo-util.js.bak +++ b/js/core/fbo-util.js.bak @@ -124,10 +124,10 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Normal fbo[FBO_GBUFFER_NORMAL] = gl.createFramebuffer(); - + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); - + FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); if (FBOstatus !== gl.FRAMEBUFFER_COMPLETE) { console.log("GBuffer Normal FBO incomplete! Init failed!"); @@ -139,6 +139,7 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Color fbo[FBO_GBUFFER_COLOR] = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_COLOR]); + gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[2], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); @@ -196,5 +197,4 @@ CIS565WEBGLCORE.createFBO = function(){ } } }; -}; - +}; \ No newline at end of file diff --git a/js/ext/Stats.js b/js/ext/Stats.js new file mode 100644 index 0000000..90b2a27 --- /dev/null +++ b/js/ext/Stats.js @@ -0,0 +1,149 @@ +/** + * @author mrdoob / http://mrdoob.com/ + */ + +var Stats = function () { + + var startTime = Date.now(), prevTime = startTime; + var ms = 0, msMin = Infinity, msMax = 0; + var fps = 0, fpsMin = Infinity, fpsMax = 0; + var frames = 0, mode = 0; + + var container = document.createElement( 'div' ); + container.id = 'stats'; + container.addEventListener( 'mousedown', function ( event ) { event.preventDefault(); setMode( ++ mode % 2 ) }, false ); + container.style.cssText = 'width:80px;opacity:0.9;cursor:pointer'; + + var fpsDiv = document.createElement( 'div' ); + fpsDiv.id = 'fps'; + fpsDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#002'; + container.appendChild( fpsDiv ); + + var fpsText = document.createElement( 'div' ); + fpsText.id = 'fpsText'; + fpsText.style.cssText = 'color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + fpsText.innerHTML = 'FPS'; + fpsDiv.appendChild( fpsText ); + + var fpsGraph = document.createElement( 'div' ); + fpsGraph.id = 'fpsGraph'; + fpsGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0ff'; + fpsDiv.appendChild( fpsGraph ); + + while ( fpsGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#113'; + fpsGraph.appendChild( bar ); + + } + + var msDiv = document.createElement( 'div' ); + msDiv.id = 'ms'; + msDiv.style.cssText = 'padding:0 0 3px 3px;text-align:left;background-color:#020;display:none'; + container.appendChild( msDiv ); + + var msText = document.createElement( 'div' ); + msText.id = 'msText'; + msText.style.cssText = 'color:#0f0;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px'; + msText.innerHTML = 'MS'; + msDiv.appendChild( msText ); + + var msGraph = document.createElement( 'div' ); + msGraph.id = 'msGraph'; + msGraph.style.cssText = 'position:relative;width:74px;height:30px;background-color:#0f0'; + msDiv.appendChild( msGraph ); + + while ( msGraph.children.length < 74 ) { + + var bar = document.createElement( 'span' ); + bar.style.cssText = 'width:1px;height:30px;float:left;background-color:#131'; + msGraph.appendChild( bar ); + + } + + var setMode = function ( value ) { + + mode = value; + + switch ( mode ) { + + case 0: + fpsDiv.style.display = 'block'; + msDiv.style.display = 'none'; + break; + case 1: + fpsDiv.style.display = 'none'; + msDiv.style.display = 'block'; + break; + } + + }; + + var updateGraph = function ( dom, value ) { + + var child = dom.appendChild( dom.firstChild ); + child.style.height = value + 'px'; + + }; + + return { + + REVISION: 12, + + domElement: container, + + setMode: setMode, + + begin: function () { + + startTime = Date.now(); + + }, + + end: function () { + + var time = Date.now(); + + ms = time - startTime; + msMin = Math.min( msMin, ms ); + msMax = Math.max( msMax, ms ); + + msText.textContent = ms + ' MS (' + msMin + '-' + msMax + ')'; + updateGraph( msGraph, Math.min( 30, 30 - ( ms / 200 ) * 30 ) ); + + frames ++; + + if ( time > prevTime + 1000 ) { + + fps = Math.round( ( frames * 1000 ) / ( time - prevTime ) ); + fpsMin = Math.min( fpsMin, fps ); + fpsMax = Math.max( fpsMax, fps ); + + fpsText.textContent = fps + ' FPS (' + fpsMin + '-' + fpsMax + ')'; + updateGraph( fpsGraph, Math.min( 30, 30 - ( fps / 100 ) * 30 ) ); + + prevTime = time; + frames = 0; + + } + + return time; + + }, + + update: function () { + + startTime = this.end(); + + } + + } + +}; + +if ( typeof module === 'object' ) { + + module.exports = Stats; + +} \ No newline at end of file diff --git a/js/ext/stats.min.js b/js/ext/stats.min.js new file mode 100644 index 0000000..52539f4 --- /dev/null +++ b/js/ext/stats.min.js @@ -0,0 +1,6 @@ +// stats.js - http://github.com/mrdoob/stats.js +var Stats=function(){var l=Date.now(),m=l,g=0,n=Infinity,o=0,h=0,p=Infinity,q=0,r=0,s=0,f=document.createElement("div");f.id="stats";f.addEventListener("mousedown",function(b){b.preventDefault();t(++s%2)},!1);f.style.cssText="width:80px;opacity:0.9;cursor:pointer";var a=document.createElement("div");a.id="fps";a.style.cssText="padding:0 0 3px 3px;text-align:left;background-color:#002";f.appendChild(a);var i=document.createElement("div");i.id="fpsText";i.style.cssText="color:#0ff;font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px"; +i.innerHTML="FPS";a.appendChild(i);var c=document.createElement("div");c.id="fpsGraph";c.style.cssText="position:relative;width:74px;height:30px;background-color:#0ff";for(a.appendChild(c);74>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"); +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= +"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= +a+"px",m=b,r=0);return b},update:function(){l=this.end()}}};"object"===typeof module&&(module.exports=Stats); diff --git a/js/main.js b/js/main.js index 7c06886..79b9e39 100644 --- a/js/main.js +++ b/js/main.js @@ -34,10 +34,10 @@ var isDiagnostic = true; var zNear = 20; var zFar = 2000; var texToDisplay = 1; - +var stats; var main = function (canvasId, messageId) { var canvas; - + stats = initStats(); // Initialize WebGL initGL(canvasId, messageId); @@ -67,6 +67,8 @@ var renderLoop = function () { }; var render = function () { + if (stats) + stats.update(); if (fbo.isMultipleTargets()) { renderPass(); } else { @@ -373,11 +375,11 @@ var initCamera = function () { break; case 53: isDiagnostic = false; - texToDisplay = 5;//bloom + texToDisplay = 5;//blinn break; case 54: isDiagnostic = false; - texToDisplay = 6; + texToDisplay = 6;//bloom break; case 55: isDiagnostic = false; @@ -387,6 +389,10 @@ var initCamera = function () { isDiagnostic = false; texToDisplay = 8; break; + case 57: + isDiagnostic = false; + texToDisplay = 9; + break; } } }; @@ -573,4 +579,18 @@ var initKernel = function () { kernel = kernel.concat([x, y, z]); } -}; \ No newline at end of file +}; +function initStats() { + stats = new Stats(); + stats.setMode(0); // 0: fps, 1: ms + + // Align top-left + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + + document.body.appendChild(stats.domElement); + + + return stats; +} \ No newline at end of file diff --git a/js/main.js.bak b/js/main.js.bak index ed06bc9..5b6c987 100644 --- a/js/main.js.bak +++ b/js/main.js.bak @@ -34,10 +34,10 @@ var isDiagnostic = true; var zNear = 20; var zFar = 2000; var texToDisplay = 1; - +var stats; var main = function (canvasId, messageId) { var canvas; - + stats = initStats(); // Initialize WebGL initGL(canvasId, messageId); @@ -67,6 +67,8 @@ var renderLoop = function () { }; var render = function () { + if (stats) + stats.update(); if (fbo.isMultipleTargets()) { renderPass(); } else { @@ -373,11 +375,11 @@ var initCamera = function () { break; case 53: isDiagnostic = false; - texToDisplay = 5; + texToDisplay = 5;//blinn break; case 54: isDiagnostic = false; - texToDisplay = 6; + texToDisplay = 6;//bloom break; case 55: isDiagnostic = false; @@ -387,6 +389,10 @@ var initCamera = function () { isDiagnostic = false; texToDisplay = 8; break; + case 56: + isDiagnostic = false; + texToDisplay = 9; + break; } } }; @@ -573,4 +579,18 @@ var initKernel = function () { kernel = kernel.concat([x, y, z]); } -}; \ No newline at end of file +}; +function initStats() { + stats = new Stats(); + stats.setMode(0); // 0: fps, 1: ms + + // Align top-left + stats.domElement.style.position = 'absolute'; + stats.domElement.style.left = '0px'; + stats.domElement.style.top = '0px'; + + document.body.appendChild(stats.domElement); + + + return stats; +} \ No newline at end of file From 38978a881dfac9685bf53509700c92bb7ac7c6f4 Mon Sep 17 00:00:00 2001 From: XJMa Date: Wed, 12 Nov 2014 21:10:18 -0500 Subject: [PATCH 03/14] dof not work --- assets/shader/deferred/diffuse.frag | 2 +- assets/shader/deferred/diffuse.frag.bak | 14 ++++++-------- assets/shader/deferred/post.frag | 8 +++----- 3 files changed, 10 insertions(+), 14 deletions(-) diff --git a/assets/shader/deferred/diffuse.frag b/assets/shader/deferred/diffuse.frag index eaff765..38a5da6 100644 --- a/assets/shader/deferred/diffuse.frag +++ b/assets/shader/deferred/diffuse.frag @@ -41,7 +41,7 @@ void main() depth = linearizeDepth( depth, u_zNear, u_zFar ); if (depth > 0.99) { - gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); + gl_FragColor = vec4(vec3(u_displayType == 5 ? 0.0 : 0.8), 1.0); } else { gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); } diff --git a/assets/shader/deferred/diffuse.frag.bak b/assets/shader/deferred/diffuse.frag.bak index fd3f36d..eaff765 100644 --- a/assets/shader/deferred/diffuse.frag.bak +++ b/assets/shader/deferred/diffuse.frag.bak @@ -15,9 +15,7 @@ varying vec2 v_texcoord; float linearizeDepth( float exp_depth, float near, float far ){ return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } -float SSAO(){ - -} + void main() { @@ -28,7 +26,7 @@ void main() vec3 position = texture2D(u_positionTex, v_texcoord).xyz; vec3 lightDir = normalize(u_Light.xyz - position); vec3 diffuseColor = texture2D(u_colorTex, v_texcoord).rgb; - float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0); + float diffuseTerm = clamp(abs(dot(normalize(normal), normalize(lightDir))), 0.0, 1.0);//max(dot(lightDir,normal), 0.0); float specular = 0.0; @@ -42,10 +40,10 @@ void main() float depth = texture2D( u_depthTex, v_texcoord ).x; depth = linearizeDepth( depth, u_zNear, u_zFar ); - if (depth > 0.99 && u_displayType == 6) { - gl_FragColor = vec4(vec3(0.8), 1.0); + if (depth > 0.99) { + gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); } else { - gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(0.7), 1.0); + gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); } -} +} \ No newline at end of file diff --git a/assets/shader/deferred/post.frag b/assets/shader/deferred/post.frag index 8d565b9..e137796 100644 --- a/assets/shader/deferred/post.frag +++ b/assets/shader/deferred/post.frag @@ -111,11 +111,9 @@ vec4 Blur() { vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); for (int i = 0; i < uBlurSize; ++i) { - if(i<=depB) - { + if(i<=depB) { for (int j = 0; j < uBlurSize; ++j) { - if(j<=depB) - { + if(j<=depB){ vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; result += texture2D(u_shadeTex, v_texcoord + offset).rgb; } @@ -142,6 +140,6 @@ void main() if(u_displayType == DISPLAY_SSAO) gl_FragColor = SSAO(color); if(u_displayType == DISPLAY_DOF) - gl_FragColor = vec4(color, 1.0);; + gl_FragColor = Blur(); } \ No newline at end of file From 02be30ca44e30580630a5e3db01e1a19a36ccef1 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 00:55:46 -0500 Subject: [PATCH 04/14] add ssao --- assets/shader/deferred/post.frag | 76 +++++++++++++++---------- assets/shader/deferred/post.frag.bak | 80 ++++++++++++++++----------- js/core/fbo-util.js | 6 +- js/core/fbo-util.js.bak | 8 +-- js/main.js | 25 +++------ js/main.js.bak | 25 +++------ screenshots/SSAO.jpg | Bin 0 -> 23379 bytes screenshots/bug1.jpg | Bin 0 -> 16455 bytes 8 files changed, 115 insertions(+), 105 deletions(-) create mode 100644 screenshots/SSAO.jpg create mode 100644 screenshots/bug1.jpg diff --git a/assets/shader/deferred/post.frag b/assets/shader/deferred/post.frag index e137796..3f42c7a 100644 --- a/assets/shader/deferred/post.frag +++ b/assets/shader/deferred/post.frag @@ -16,7 +16,7 @@ uniform vec3 u_kernel[100]; varying vec2 v_texcoord; - +#define SAMPLEKERNEL_SIZE 80 #define KERNEL_SIZE 25 // has to be an odd number #define DISPLAY_TOON 7 #define DISPLAY_BLOOM 6 @@ -29,7 +29,12 @@ float h = 1.0 / float(u_height); float linearizeDepth( float exp_depth, float near, float far ){ return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } - +float hash( float n ){ //Borrowed from voltage + return fract(sin(n)*43758.5453); +} +float rand(float co){ + return fract(sin(dot(vec2(co,co) ,vec2(12.9898,78.233))) * 43758.5453); +} float gaussian2d(int x,int y,int n) { float sigma = 2.0; float fx = float(x) - (float(n) - 1.0) / 2.0; @@ -71,42 +76,53 @@ vec4 toonShader(vec3 color, int numColors) { // Sharpen the edges return abs(detectEdge()) > 0.005 ? vec4(vec3(0.0), 1.0) : vec4(p_color, 1.0); } - vec4 SSAO(vec3 color) { - float occlusion = 0.0; - - vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; - vec3 position = texture2D(u_positionTex, v_texcoord).xyz; - - vec3 rvec = vec3(1.0, 0.0, 0.0); - vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); - vec3 bitangent = cross(normal, tangent); - mat3 tbn = mat3(tangent, bitangent, normal); - - float thisDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); - - for (int i = 0; i < 100; i++) { - vec3 sample = tbn * u_kernel[i]; - vec2 offset = sample.xy; - offset.x *= w; - offset.y *= h; - - float sampleDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord + offset.xy).x, u_zNear, u_zFar); - - occlusion += (thisDepth - sample.z > sampleDepth) ? 1.0 : 0.0; - } - - occlusion = 1.0 - (occlusion / float(100)); - - return vec4(vec3(occlusion), 1.0); + float radius = 0.1; + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + float depth = texture2D(u_depthTex, v_texcoord).r; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + float occlusion = 0.0; + vec3 origin = vec3(position.x, position.y, depth); + + for(int i = 0; i < SAMPLEKERNEL_SIZE; ++i){ + + vec3 rvec = normalize(u_kernel[i]); + vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + vec3 kernelv = vec3(rand(position.x),rand(position.y),(rand(position.z)+1.0) / 2.0); + kernelv = normalize(kernelv); + float scale = float(i) / float(SAMPLEKERNEL_SIZE); + scale = mix(0.1, 1.0, scale * scale); + kernelv = kernelv * scale ; + + vec3 sample = tbn * kernelv; + float sampleDepth = texture2D(u_depthTex, v_texcoord + vec2(sample.x,sample.y)* radius).r; + sampleDepth = linearizeDepth( sampleDepth, u_zNear, u_zFar ); + + float samplez = origin.z - (sample * radius).z / 2.0; + + //rangeCheck helps to prevent erroneous occlusion between large depth discontinuities: + float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0; + + if(sampleDepth <= samplez) + occlusion += 1.0 * rangeCheck; + } + + occlusion = 1.0 - (occlusion / float(SAMPLEKERNEL_SIZE)); + + return vec4(vec3(occlusion), 1.0); } + vec4 Blur() { float depth = texture2D(u_depthTex, v_texcoord).r; depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); vec2 texelSize = vec2(1.0/w, 1.0/h); vec3 result = vec3(0.0,0.0,0.0); - const int uBlurSize = 10; + const int uBlurSize = 5; int depB = int(float(uBlurSize) * depth); vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); diff --git a/assets/shader/deferred/post.frag.bak b/assets/shader/deferred/post.frag.bak index 28efdf0..552da7d 100644 --- a/assets/shader/deferred/post.frag.bak +++ b/assets/shader/deferred/post.frag.bak @@ -16,7 +16,7 @@ uniform vec3 u_kernel[100]; varying vec2 v_texcoord; - +#define SAMPLEKERNEL_SIZE 80 #define KERNEL_SIZE 25 // has to be an odd number #define DISPLAY_TOON 7 #define DISPLAY_BLOOM 6 @@ -29,7 +29,12 @@ float h = 1.0 / float(u_height); float linearizeDepth( float exp_depth, float near, float far ){ return ( 2.0 * near ) / ( far + near - exp_depth * ( far - near ) ); } - +float hash( float n ){ //Borrowed from voltage + return fract(sin(n)*43758.5453); +} +float rand(float co){ + return fract(sin(dot(vec2(co,co) ,vec2(12.9898,78.233))) * 43758.5453); +} float gaussian2d(int x,int y,int n) { float sigma = 2.0; float fx = float(x) - (float(n) - 1.0) / 2.0; @@ -71,35 +76,46 @@ vec4 toonShader(vec3 color, int numColors) { // Sharpen the edges return abs(detectEdge()) > 0.005 ? vec4(vec3(0.0), 1.0) : vec4(p_color, 1.0); } - vec4 SSAO(vec3 color) { - float occlusion = 0.0; - - vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; - vec3 position = texture2D(u_positionTex, v_texcoord).xyz; - - vec3 rvec = vec3(1.0, 0.0, 0.0); - vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); - vec3 bitangent = cross(normal, tangent); - mat3 tbn = mat3(tangent, bitangent, normal); - - float thisDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord).x, u_zNear, u_zFar); - - for (int i = 0; i < 100; i++) { - vec3 sample = tbn * u_kernel[i]; - vec2 offset = sample.xy; - offset.x *= w; - offset.y *= h; - - float sampleDepth = linearizeDepth(texture2D(u_depthTex, v_texcoord + offset.xy).x, u_zNear, u_zFar); - - occlusion += (thisDepth - sample.z > sampleDepth) ? 1.0 : 0.0; - } - - occlusion = 1.0 - (occlusion / float(100)); - - return vec4(vec3(occlusion), 1.0); + float radius = 0.1; + vec3 normal = texture2D(u_normalTex, v_texcoord).xyz; + vec3 position = texture2D(u_positionTex, v_texcoord).xyz; + float depth = texture2D(u_depthTex, v_texcoord).r; + depth = linearizeDepth( depth, u_zNear, u_zFar ); + float occlusion = 0.0; + vec3 origin = vec3(position.x, position.y, depth); + + for(int i = 0; i < SAMPLEKERNEL_SIZE; ++i){ + + vec3 rvec = normalize(u_kernel[i]); + vec3 tangent = normalize(rvec - normal * dot(rvec, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + vec3 kernelv = vec3(rand(position.x),rand(position.y),(rand(position.z)+1.0) / 2.0); + kernelv = normalize(kernelv); + float scale = float(i) / float(SAMPLEKERNEL_SIZE); + scale = mix(0.1, 1.0, scale * scale); + kernelv = kernelv * scale ; + + vec3 sample = tbn * kernelv; + float sampleDepth = texture2D(u_depthTex, v_texcoord + vec2(sample.x,sample.y)* radius).r; + sampleDepth = linearizeDepth( sampleDepth, u_zNear, u_zFar ); + + float samplez = origin.z - (sample * radius).z / 2.0; + + //rangeCheck helps to prevent erroneous occlusion between large depth discontinuities: + float rangeCheck = abs(origin.z - sampleDepth) < radius ? 1.0 : 0.0; + + if(sampleDepth <= samplez) + occlusion += 1.0 * rangeCheck; + } + + occlusion = 1.0 - (occlusion / float(SAMPLEKERNEL_SIZE)); + + return vec4(vec3(occlusion), 1.0); } + vec4 Blur() { float depth = texture2D(u_depthTex, v_texcoord).r; depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); @@ -111,11 +127,9 @@ vec4 Blur() { vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); for (int i = 0; i < uBlurSize; ++i) { - if(i<=depB) - { + if(true) { for (int j = 0; j < uBlurSize; ++j) { - if(j<=depB) - { + if(true){ vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; result += texture2D(u_shadeTex, v_texcoord + offset).rgb; } diff --git a/js/core/fbo-util.js b/js/core/fbo-util.js index 42ce974..e377550 100644 --- a/js/core/fbo-util.js +++ b/js/core/fbo-util.js @@ -125,7 +125,7 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Normal fbo[FBO_GBUFFER_NORMAL] = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); - + //gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);//add gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); @@ -139,6 +139,7 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Color fbo[FBO_GBUFFER_COLOR] = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_COLOR]); + //gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);//add gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[2], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); @@ -196,5 +197,4 @@ CIS565WEBGLCORE.createFBO = function(){ } } }; -}; - +}; \ No newline at end of file diff --git a/js/core/fbo-util.js.bak b/js/core/fbo-util.js.bak index 58a96b3..01acbe7 100644 --- a/js/core/fbo-util.js.bak +++ b/js/core/fbo-util.js.bak @@ -1,4 +1,4 @@ -//A wrapper for creating framebuffer objects +or creating framebuffer objects //CIS565WEBGLCORE is a core function interface var CIS565WEBGLCORE = CIS565WEBGLCORE || {}; @@ -124,8 +124,8 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Normal fbo[FBO_GBUFFER_NORMAL] = gl.createFramebuffer(); - gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); + gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_NORMAL]); + //gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);//add gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[1], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); @@ -139,7 +139,7 @@ CIS565WEBGLCORE.createFBO = function(){ // Set up GBuffer Color fbo[FBO_GBUFFER_COLOR] = gl.createFramebuffer(); gl.bindFramebuffer(gl.FRAMEBUFFER, fbo[FBO_GBUFFER_COLOR]); - gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0); + //gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, depthTex, 0);//add gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.COLOR_ATTACHMENT0, gl.TEXTURE_2D, textures[2], 0); FBOstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER); diff --git a/js/main.js b/js/main.js index 79b9e39..de1b997 100644 --- a/js/main.js +++ b/js/main.js @@ -162,7 +162,7 @@ var renderMulti = function () { fbo.bind(gl, FBO_GBUFFER_POSITION); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.clear(gl.COLOR_BUFFER_BIT);//add + //gl.clear(gl.COLOR_BUFFER_BIT);//add gl.enable(gl.DEPTH_TEST); gl.useProgram(posProg.ref()); @@ -561,24 +561,13 @@ var initFramebuffer = function () { } }; var initKernel = function () { - - for (var i = 0; i < kernelSize ; i++) { - var x = Math.random() * 2.0 - 1.0; - var y = Math.random() * 2.0 - 1.0; - var z = Math.random(); - //Normalize and rescale - var len = x * x + y * y + z * z; - var scale1 = Math.random(); - var scale2 = (i * 1.0) / (kernelSize * 1.0); - if (len > 0) { - len = 1 / Math.sqrt(len); - x = x * len * scale1 * (0.1 + 0.9 * scale2); - y = y * len * scale1 * (0.1 + 0.9 * scale2); - x = z * len * scale1 * (0.1 + 0.9 * scale2); - } - kernel = kernel.concat([x, y, z]); - } + for (var i = 0; i < kernelSize; i++) { + var x = Math.random() * 2.0 - 1.0; + var y = Math.random() * 2.0 - 1.0; + var z = Math.random(); + kernel[i] = [Math.random() * x, Math.random() * y, Math.random() * z]; + } }; function initStats() { stats = new Stats(); diff --git a/js/main.js.bak b/js/main.js.bak index 5b6c987..972ff86 100644 --- a/js/main.js.bak +++ b/js/main.js.bak @@ -162,7 +162,7 @@ var renderMulti = function () { fbo.bind(gl, FBO_GBUFFER_POSITION); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); - gl.clear(gl.COLOR_BUFFER_BIT);//add + //gl.clear(gl.COLOR_BUFFER_BIT);//add gl.enable(gl.DEPTH_TEST); gl.useProgram(posProg.ref()); @@ -389,7 +389,7 @@ var initCamera = function () { isDiagnostic = false; texToDisplay = 8; break; - case 56: + case 57: isDiagnostic = false; texToDisplay = 9; break; @@ -561,22 +561,13 @@ var initFramebuffer = function () { } }; var initKernel = function () { - - for (var i = 0; i < kernelSize ; i++) { - var x = Math.random() * 2.0 - 1.0; - var y = Math.random() * 2.0 - 1.0; - var z = Math.random(); - //Normalize and rescale - var len = x * x + y * y + z * z; - var scale1 = Math.random(); - var scale2 = (i * 1.0) / (kernelSize * 1.0); - if (len > 0) { - len = 1 / Math.sqrt(len); - x = x * len * scale1 * (0.1 + 0.9 * scale2); - y = y * len * scale1 * (0.1 + 0.9 * scale2); - x = z * len * scale1 * (0.1 + 0.9 * scale2); + for (var i = 0; i < kernelSize; i++) { + var x = Math.random() * 2.0 - 1.0; + var y = Math.random() * 2.0 - 1.0; + var z = Math.random(); + + kernel[i] = [Math.random() * x, Math.random() * y, Math.random() * z]; } - kernel = kernel.concat([x, y, z]); } }; diff --git a/screenshots/SSAO.jpg b/screenshots/SSAO.jpg new file mode 100644 index 0000000000000000000000000000000000000000..f65e1de257135b38f28d067b432345f4cb375d65 GIT binary patch literal 23379 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 literal 0 HcmV?d00001 diff --git a/screenshots/bug1.jpg b/screenshots/bug1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..a6e0c757403e8923cbdbb6f1637876418a105ac8 GIT binary patch literal 16455 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 literal 0 HcmV?d00001 From ef1bf7032fd54b53a1a8141c2567023bd419976c Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 01:24:47 -0500 Subject: [PATCH 05/14] Update README.md --- README.md | 153 ++++++------------------------------------------------ 1 file changed, 16 insertions(+), 137 deletions(-) diff --git a/README.md b/README.md index da4c7e1..d0f2498 100644 --- a/README.md +++ b/README.md @@ -3,37 +3,14 @@ CIS565: Project 6 -- Deferred Shader ------------------------------------------------------------------------------- Fall 2014 ------------------------------------------------------------------------------- -Due Wed, 11/12/2014 at Noon -------------------------------------------------------------------------------- - -------------------------------------------------------------------------------- -NOTE: -------------------------------------------------------------------------------- -This project requires any graphics card with support for a modern OpenGL -pipeline. Any AMD, NVIDIA, or Intel card from the past few years should work -fine, and every machine in the SIG Lab and Moore 100 is capable of running -this project. -This project also requires a WebGL capable browser. The project is known to -have issues with Chrome on windows, but Firefox seems to run it fine. ------------------------------------------------------------------------------- INTRODUCTION: ------------------------------------------------------------------------------- -In this project, you will get introduced to the basics of deferred shading. You will write GLSL and OpenGL code to perform various tasks in a deferred lighting pipeline such as creating and writing to a G-Buffer. +In this project, i write GLSL and OpenGL code to perform various tasks in a deferred lighting pipeline such as creating and writing to a G-Buffer. This project requires a graphic card support for deferred shader pipeline. -------------------------------------------------------------------------------- -CONTENTS: -------------------------------------------------------------------------------- -The Project5 root directory contains the following subdirectories: - -* js/ contains the javascript files, including external libraries, necessary. -* assets/ contains the textures that will be used in the second half of the - assignment. -* resources/ contains the screenshots found in this readme file. - - This Readme file edited as described above in the README section. ------------------------------------------------------------------------------- OVERVIEW: @@ -69,89 +46,34 @@ WASDRF - Movement (along w the arrow keys) * 2 - Normals * 3 - Color * 4 - Depth +* 5 - Blinn-Phong shading +* 6 - bloom shading +* 7 - Toon shading +* 8 - SSAO * 0 - Full deferred pipeline There are also mouse controls for camera rotation. ------------------------------------------------------------------------------- -REQUIREMENTS: +Blinn-Phong: ------------------------------------------------------------------------------- -In this project, you are given code for: -* Loading .obj file -* Deferred shading pipeline -* GBuffer pass - -You are required to implement: -* Either of the following effects - * Bloom - * "Toon" Shading (with basic silhouetting) -* Screen Space Ambient Occlusion -* Diffuse and Blinn-Phong shading +The diffuse and specular shader is implemented in lighting passes and accumulates stage that writes the result to P-buffer. -**NOTE**: Implementing separable convolution will require another link in your pipeline and will count as an extra feature if you do performance analysis with a standard one-pass 2D convolution. The overhead of rendering and reading from a texture _may_ offset the extra computations for smaller 2D kernels. - -You must implement two of the following extras: -* The effect you did not choose above -* Compare performance to a normal forward renderer with - * No optimizations - * Coarse sort geometry front-to-back for early-z - * Z-prepass for early-z -* Optimize g-buffer format, e.g., pack things together, quantize, reconstruct z from normal x and y (because it is normalized), etc. - * Must be accompanied with a performance analysis to count -* Additional lighting and pre/post processing effects! (email first please, if they are good you may add multiple). ------------------------------------------------------------------------------- -RUNNING THE CODE: +Bloom ------------------------------------------------------------------------------- - -Since the code attempts to access files that are local to your computer, you -will either need to: - -* Run your browser under modified security settings, or -* Create a simple local server that serves the files - - -FIREFOX: change ``strict_origin_policy`` to false in about:config - -CHROME: run with the following argument : `--allow-file-access-from-files` - -(You can do this on OSX by running Chrome from /Applications/Google -Chrome/Contents/MacOS with `open -a "Google Chrome" --args ---allow-file-access-from-files`) - -* To check if you have set the flag properly, you can open chrome://version and - check under the flags - -RUNNING A SIMPLE SERVER: - -If you have Python installed, you can simply run a simple HTTP server off your -machine from the root directory of this repository with the following command: - -`python -m SimpleHTTPServer` +Bloom is a post processing effects. Normally, Bloom effects is implemented with a texture that specify the glow source and then blur the glow source. But here I just treat the whole object as a glow source. I use a gaussian convolution on color from G-buffer. ------------------------------------------------------------------------------- -RESOURCES: +"Toon" Shading (with basic silhouetting) ------------------------------------------------------------------------------- - -The following are articles and resources that have been chosen to help give you -a sense of each of the effects: - -* Bloom : [GPU Gems](http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html) -* Screen Space Ambient Occlusion : [Floored - Article](http://floored.com/blog/2013/ssao-screen-space-ambient-occlusion.html) - +Toon shading is a non-photorealistic rendering technique that is used to achieve a cartoonish or hand-drawn appearance of three-dimensional models. To make is cartoonish we don't want many color in the final rendering so I round the colors in the scene to a certain color set. Basic silhouetting is achieved by compare the depth of the object with the background to get the edge. ------------------------------------------------------------------------------- -README +Screen Space Ambient Occlusion ------------------------------------------------------------------------------- -All students must replace or augment the contents of this Readme.md in a clear -manner with the following: - -* A brief description of the project and the specific features you implemented. -* At least one screenshot of your project running. -* A 30 second or longer video of your project running. To create the video you - can use [Open Broadcaster Software](http://obsproject.com) -* A performance evaluation (described in detail below). +Ambient occlusion is an approximation of the amount by which a point on a surface is occluded by the surrounding geometry. To achieve this I sample a random position within a hemisphere, oriented along the surface normal at that pixel. Then project the sample position into screen space to get its depth on depth buffer. If the depth buffer value is smaller than sample position's depth, then occlusion accumulates. ------------------------------------------------------------------------------- PERFORMANCE EVALUATION @@ -168,55 +90,12 @@ Each student should provide no more than a one page summary of their optimizations along with tables and or graphs to visually explain any performance differences. -------------------------------------------------------------------------------- -THIRD PARTY CODE POLICY -------------------------------------------------------------------------------- -* Use of any third-party code must be approved by asking on the Google groups. - If it is approved, all students are welcome to use it. Generally, we approve - use of third-party code that is not a core part of the project. For example, - for the ray tracer, we would approve using a third-party library for loading - models, but would not approve copying and pasting a CUDA function for doing - refraction. -* Third-party code must be credited in README.md. -* Using third-party code without its approval, including using another - student's code, is an academic integrity violation, and will result in you - receiving an F for the semester. - -------------------------------------------------------------------------------- -SELF-GRADING -------------------------------------------------------------------------------- -* On the submission date, email your grade, on a scale of 0 to 100, to Harmony, - harmoli+cis565@seas.upenn.edu, with a one paragraph explanation. Be concise and - realistic. Recall that we reserve 30 points as a sanity check to adjust your - grade. Your actual grade will be (0.7 * your grade) + (0.3 * our grade). We - hope to only use this in extreme cases when your grade does not realistically - reflect your work - it is either too high or too low. In most cases, we plan - to give you the exact grade you suggest. -* Projects are not weighted evenly, e.g., Project 0 doesn't count as much as - the path tracer. We will determine the weighting at the end of the semester - based on the size of each project. - - ---- -SUBMISSION ---- -As with the previous projects, you should fork this project and work inside of -your fork. Upon completion, commit your finished project back to your fork, and -make a pull request to the master repository. You should include a README.md -file in the root directory detailing the following - -* A brief description of the project and specific features you implemented -* At least one screenshot of your project running. -* A link to a video of your project running. -* Instructions for building and running your project if they differ from the - base code. -* A performance writeup as detailed above. -* A list of all third-party code used. -* This Readme file edited as described above in the README section. --- -ACKNOWLEDGEMENTS +Reference --- +BLOOM: http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html +SSAO: http://john-chapman-graphics.blogspot.co.uk/2013/01/ssao-tutorial.html Many thanks to Cheng-Tso Lin, whose framework for CIS700 we used for this assignment. From d23ee7b84001c750b754f83a2f99c8c353dd4cb5 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 01:25:21 -0500 Subject: [PATCH 06/14] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index d0f2498..c425f50 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,7 @@ Bloom is a post processing effects. Normally, Bloom effects is implemented with ------------------------------------------------------------------------------- "Toon" Shading (with basic silhouetting) ------------------------------------------------------------------------------- + Toon shading is a non-photorealistic rendering technique that is used to achieve a cartoonish or hand-drawn appearance of three-dimensional models. To make is cartoonish we don't want many color in the final rendering so I round the colors in the scene to a certain color set. Basic silhouetting is achieved by compare the depth of the object with the background to get the edge. ------------------------------------------------------------------------------- Screen Space Ambient Occlusion @@ -95,6 +96,7 @@ performance differences. Reference --- BLOOM: http://http.developer.nvidia.com/GPUGems/gpugems_ch21.html + SSAO: http://john-chapman-graphics.blogspot.co.uk/2013/01/ssao-tutorial.html Many thanks to Cheng-Tso Lin, whose framework for CIS700 we used for this From 9f2060e0bde3694413300723babfaedc19c11cd7 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 01:25:45 -0500 Subject: [PATCH 07/14] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index c425f50..0894f6e 100644 --- a/README.md +++ b/README.md @@ -71,6 +71,7 @@ Bloom is a post processing effects. Normally, Bloom effects is implemented with ------------------------------------------------------------------------------- Toon shading is a non-photorealistic rendering technique that is used to achieve a cartoonish or hand-drawn appearance of three-dimensional models. To make is cartoonish we don't want many color in the final rendering so I round the colors in the scene to a certain color set. Basic silhouetting is achieved by compare the depth of the object with the background to get the edge. + ------------------------------------------------------------------------------- Screen Space Ambient Occlusion ------------------------------------------------------------------------------- From b47738c3abbde7f2ef2771dc1400bd2b83809606 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 10:17:49 -0500 Subject: [PATCH 08/14] Update README.md --- README.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 0894f6e..36f3eab 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,15 @@ CIS565: Project 6 -- Deferred Shader Fall 2014 ------------------------------------------------------------------------------- +[Youtube](https://www.youtube.com/watch?v=ggUH_oqFYuo&feature=youtu.be) +[Live Demo]() ------------------------------------------------------------------------------- INTRODUCTION: ------------------------------------------------------------------------------- In this project, i write GLSL and OpenGL code to perform various tasks in a deferred lighting pipeline such as creating and writing to a G-Buffer. This project requires a graphic card support for deferred shader pipeline. - +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/diffuseSpec.jpg) ------------------------------------------------------------------------------- OVERVIEW: @@ -59,24 +61,24 @@ Blinn-Phong: ------------------------------------------------------------------------------- The diffuse and specular shader is implemented in lighting passes and accumulates stage that writes the result to P-buffer. - +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/diffuseSpec.jpg) ------------------------------------------------------------------------------- Bloom ------------------------------------------------------------------------------- Bloom is a post processing effects. Normally, Bloom effects is implemented with a texture that specify the glow source and then blur the glow source. But here I just treat the whole object as a glow source. I use a gaussian convolution on color from G-buffer. - +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/bloom.jpg) ------------------------------------------------------------------------------- "Toon" Shading (with basic silhouetting) ------------------------------------------------------------------------------- Toon shading is a non-photorealistic rendering technique that is used to achieve a cartoonish or hand-drawn appearance of three-dimensional models. To make is cartoonish we don't want many color in the final rendering so I round the colors in the scene to a certain color set. Basic silhouetting is achieved by compare the depth of the object with the background to get the edge. - +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/toon.jpg) ------------------------------------------------------------------------------- Screen Space Ambient Occlusion ------------------------------------------------------------------------------- Ambient occlusion is an approximation of the amount by which a point on a surface is occluded by the surrounding geometry. To achieve this I sample a random position within a hemisphere, oriented along the surface normal at that pixel. Then project the sample position into screen space to get its depth on depth buffer. If the depth buffer value is smaller than sample position's depth, then occlusion accumulates. - +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/SSAO.jpg) ------------------------------------------------------------------------------- PERFORMANCE EVALUATION ------------------------------------------------------------------------------- From e1605c8bc210ce873647ddfd419538bf6877742a Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 11:55:37 -0500 Subject: [PATCH 09/14] peformance analysis --- assets/shader/deferred/diffuse.frag | 1 + assets/shader/deferred/diffuse.frag.bak | 5 +++-- assets/shader/deferred/post.frag | 17 +++++++++++------ assets/shader/deferred/post.frag.bak | 15 ++++++++++----- 4 files changed, 25 insertions(+), 13 deletions(-) diff --git a/assets/shader/deferred/diffuse.frag b/assets/shader/deferred/diffuse.frag index 38a5da6..91a1c6c 100644 --- a/assets/shader/deferred/diffuse.frag +++ b/assets/shader/deferred/diffuse.frag @@ -44,6 +44,7 @@ void main() gl_FragColor = vec4(vec3(u_displayType == 5 ? 0.0 : 0.8), 1.0); } else { gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); + //gl_FragColor = vec4(normal, 1.0); } } \ No newline at end of file diff --git a/assets/shader/deferred/diffuse.frag.bak b/assets/shader/deferred/diffuse.frag.bak index eaff765..a6b70ff 100644 --- a/assets/shader/deferred/diffuse.frag.bak +++ b/assets/shader/deferred/diffuse.frag.bak @@ -41,9 +41,10 @@ void main() depth = linearizeDepth( depth, u_zNear, u_zFar ); if (depth > 0.99) { - gl_FragColor = vec4(vec3(0.0), 1.0);//vec4(vec3(u_displayType == 5 ? 0.0 : 0.0), 1.0); + gl_FragColor = vec4(vec3(u_displayType == 5 ? 0.0 : 0.8), 1.0); } else { - gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); + //gl_FragColor = vec4(diffuseTerm*diffuseColor + specular*vec3(1.0), 1.0); + gl_FragColor = vec4(normal, 1.0); } } \ No newline at end of file diff --git a/assets/shader/deferred/post.frag b/assets/shader/deferred/post.frag index 3f42c7a..0739e7c 100644 --- a/assets/shader/deferred/post.frag +++ b/assets/shader/deferred/post.frag @@ -16,7 +16,7 @@ uniform vec3 u_kernel[100]; varying vec2 v_texcoord; -#define SAMPLEKERNEL_SIZE 80 +#define SAMPLEKERNEL_SIZE 60 #define KERNEL_SIZE 25 // has to be an odd number #define DISPLAY_TOON 7 #define DISPLAY_BLOOM 6 @@ -116,20 +116,20 @@ vec4 SSAO(vec3 color) { return vec4(vec3(occlusion), 1.0); } -vec4 Blur() { +vec4 Blur(vec3 color) { float depth = texture2D(u_depthTex, v_texcoord).r; depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); + if(depth < 0.99){ vec2 texelSize = vec2(1.0/w, 1.0/h); vec3 result = vec3(0.0,0.0,0.0); const int uBlurSize = 5; - int depB = int(float(uBlurSize) * depth); vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); for (int i = 0; i < uBlurSize; ++i) { - if(i<=depB) { + if(true) { for (int j = 0; j < uBlurSize; ++j) { - if(j<=depB){ + if(true){ vec2 offset = (hlim + vec2(float(i), float(j))) * texelSize; result += texture2D(u_shadeTex, v_texcoord + offset).rgb; } @@ -140,6 +140,11 @@ vec4 Blur() { vec4 fResult = vec4(result / float(uBlurSize * uBlurSize),1.0); return fResult; + } + else{ + return vec4(color, 1.0); + } + } void main() { @@ -156,6 +161,6 @@ void main() if(u_displayType == DISPLAY_SSAO) gl_FragColor = SSAO(color); if(u_displayType == DISPLAY_DOF) - gl_FragColor = Blur(); + gl_FragColor = Blur(color); } \ No newline at end of file diff --git a/assets/shader/deferred/post.frag.bak b/assets/shader/deferred/post.frag.bak index 552da7d..d2dd90d 100644 --- a/assets/shader/deferred/post.frag.bak +++ b/assets/shader/deferred/post.frag.bak @@ -16,7 +16,7 @@ uniform vec3 u_kernel[100]; varying vec2 v_texcoord; -#define SAMPLEKERNEL_SIZE 80 +#define SAMPLEKERNEL_SIZE 50 #define KERNEL_SIZE 25 // has to be an odd number #define DISPLAY_TOON 7 #define DISPLAY_BLOOM 6 @@ -116,14 +116,14 @@ vec4 SSAO(vec3 color) { return vec4(vec3(occlusion), 1.0); } -vec4 Blur() { +vec4 Blur(vec3 color) { float depth = texture2D(u_depthTex, v_texcoord).r; depth = 1.0 -linearizeDepth( depth, u_zNear, u_zFar ); + if(depth < 0.99){ vec2 texelSize = vec2(1.0/w, 1.0/h); vec3 result = vec3(0.0,0.0,0.0); - const int uBlurSize = 10; - int depB = int(float(uBlurSize) * depth); + const int uBlurSize = 5; vec2 hlim = vec2(float(-uBlurSize) * 0.5 + 0.5); for (int i = 0; i < uBlurSize; ++i) { @@ -140,6 +140,11 @@ vec4 Blur() { vec4 fResult = vec4(result / float(uBlurSize * uBlurSize),1.0); return fResult; + } + else{ + return vec4(color, 1.0); + } + } void main() { @@ -156,6 +161,6 @@ void main() if(u_displayType == DISPLAY_SSAO) gl_FragColor = SSAO(color); if(u_displayType == DISPLAY_DOF) - gl_FragColor = Blur(); + gl_FragColor = Blur(color); } \ No newline at end of file From 945360b0cd4eaddfd0a5bbf031a977579402f545 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 11:57:34 -0500 Subject: [PATCH 10/14] add pics --- performance.docx | Bin 0 -> 40826 bytes screenshots/performance1.jpg | Bin 0 -> 54318 bytes screenshots/performance2.jpg | Bin 0 -> 52686 bytes 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 performance.docx create mode 100644 screenshots/performance1.jpg create mode 100644 screenshots/performance2.jpg diff --git a/performance.docx b/performance.docx new file mode 100644 index 0000000000000000000000000000000000000000..a99f7bedcd95334f957983d430871416985ff665 GIT binary patch literal 40826 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 GCH@}-&-}6g literal 0 HcmV?d00001 diff --git a/screenshots/performance1.jpg b/screenshots/performance1.jpg new file mode 100644 index 0000000000000000000000000000000000000000..32c0926a6e4cda54db7e5a249bf8fcc492a8ce7a GIT binary patch literal 54318 zcmbrm3p`Y7`!~ME&_pp65n(Eo(m{m|7&AK1iJ_AsrczOogvepml%!H7o$L;bb_YtD z4x|%9PL*nGqZG=q#xXM(X2#5#|F!J>Jip)je18AW`+nZHz1w?cx!1k!!*yTR_xruB zMg3LXgp6D3vc?4=5D3T)_y&6AR%loe3F8rs@dV;{ zg1Qu;AP7P0@4k_L{tz^Xnp&esqqTL$K!dz-hz5a3)X*eqX=!Rg>p1v1qB&k`!qkP% zqx3y@lBR_kEIO8QbF{_s$Hfyj{3V`lv+MA2Z5>0SNya8KW?IggZDnh>c*)Xb_A6Gd za&cX~X6?pJo40sv-RAATd(Ym0zr3C^CRouT^E79AUH zusr4F<6>sQmd4>d^+Q`iQ&n9;K|7vFcp4flQs}31UB*5ko$0IZZ zb8m6yiaODEg9903Bo7(HuW@lG87n{nOVemmRg73v(XOFfb9P4@RSi3z& z3FK--2jNeMv=~q(%CJp@IHUZqTIH~Z)?49SlCVLcMz|exqgxDSsbU!A3T+98Z9kFQ z>~}L*ihuzVPov`TB4J~TjYLXXXKgYWh?;#{R_ zgd+IDp7oY{nv$hPsD*{Ogf+GK{zBYfW^bI*7KR-{$vWOVJKr0=*l>hj_4v)Of?)5w{6`8DMknw_3MTAkJFtwY zC_~&uH(B^_S+{*aZ7poTTI7WZD}eV1<|C|TTf4xq7=T-74+AwiE!$`tv1l;=2>pI) zTp}C_>MV^qYy9fbj4`Lus0>~REXtRRF@b5XBAwKTs1Ms=_*b-P90|H0tC7u!&&t6z z=^*qc5NC{8otEfa14{}GtJ!m{L|@7M@+z%M>xT(oap}L<^Q^+BhkivlGFCXj{_asK zm5ROrdltap;K30LcM?zDMc2Q7gxHBzC>0XAz7^p&DhN>{z9ZJ2p}mQHbc8Jb@U2axow-6&7yp<&=u0c(~%fV@f1mVr5xAub+Amg46{k}>MC zMSjaHjYapt-tp@yS9uS<52Mczg`A)GGL7Cwbs_WQO;wuf|LA%Prb}IB&K2Qv8n-Bt zk}C7i9DB7bw5JO*AQ0!rap3yIH0r3)owO2Vzp}{cNo$Jm`2ci@T4&OAey6SOXh72& zMezAydV@K9lCx|Ho>iEIlp<0}GjtxNF@DjwYJ{n5(zu=(@^%Q{205}RqPHOeK8iSR z*t1`nl<)is);Vma#tX0G0@|_Pa!ItF2GLP?&CI z4&rfn;oAL155#&Q2HeTcbe4v zHHRlx&CgGBhJwO4UbVL_6ZvYC?Xh3doArP%8?Ic z=uWzbM00;OU{v%n;=5|F3FTt^{;m;63_C~e?}d+ke{`bG*iq~f;fQTVyL}9|UjYwn zu{(-m4C+QS^)5EU}GPG;HCYwQ;_rg`tZ2ytbZ8ZlP- zO30-qD;iflZ1@&sQEXJlx|b;nb9*j~m_y9bjxaU^k5qH=PG*Pw(dvkK+lh}}!YKF1 zxGt)fm=UY|ni?r{Gocb&UH)+N71y?+iLb3A9(FiQlO4^H#gsWs5jUO|zFFe^J8`a2 z-5Hsm$nA6Z!j_{M)z)=;pMJ@!9GznwF^+5_8y;SORq~(qmUFaYCrT`P1kL<$K_-l| zc6<^`Xg`$Tq!sJ(CD;1fkKy&cHr?qf2d^b9DXY)Eo>cjLiC6V&^Dp*^3DUS}FxW4* zj$^Z@oFGcGu`x?@%L`fsZ02KHG4q@^>rU{mShK&By2BF#X~YALc6Xx|w|Z2+c5CL0 zR<3&{YYyAXi8SP^GASo3*ui$Q2EH4Q3+3DU+GgfCTfrDzm-m&cYV5dU_i@eaba&Bo_nVx%{4hUwx?xGl z`L5=z+k*;C#J{*9514^Xltrvk^wBizt05(q`a4f@oi3Y5?(S*sA(lB=);I<<-;*-P z4-+F|S{AmvQiW|zEVW-J+fUXG#EfdB%Vq@WdQa|1nKyrT^}N!Tmv&6RK2aW9zv0wX z4nCAvc1y*S!P~i#$dWEf;Q+mCmC+4=+PA`qtr+pDj-l zhnMws^Ip%(+OJT3Nu5{lEbrF#oH%#vdKQNWzR&H}aT?PV)=?x+`oF+I{z5C9XZiTB1hwZ?-`0e6G!=N@LtUg($nm}EX{*zYQu#Xbsq^EI;hOZKq+R_t>lxD~f%-nThTsY$Q* z9Br~rxOPWSLNQVXNFqyVH z0q!@OGRk97S+ls-Pck#9HHX9$m#0Wb&H1G?z2dNd+p^wf!Rb8(485pxw!_b^`Od_e z56qwXIecf#{S|i`i)|N27xH!l+pQ}iU(^{dI>MWzK@Gj`xqDruhp)~+9>^w&aYc{LM)yc$`Sq0@a;;EzRDX&u=kOv(# z#{{7m`VIi(D-5XONF6AJb!aHM9Z6`a+aRLQ#){$Jx726VSP7v`}C;_821rnFrb#$VsY9#v_G}%2&Zlis& zk!I*>`cVbMj(R18>+XHBRw)e+{xV^0zGW8>8fv>ncM$iUiiD3W!l-bm8GxBz5W$Zq z8L)_sRa&KfvOyw@_Qt>CJ>6)0%pl%QBmun2z1r0cpF8M=n`89S1oB>dq_;g-@pvd7 z#=;5vj3)gZ%rUy*#L#zLz{=o9skIqem)%9Yofu9B3Xr*0{>E);#9BgY^U&=4qYWqn zJe2;gNH4F_?7Z4l)I*<;(5;26%_8|RObgNy0!*NU6!muKKR6v1vAI3({g6 z7ydc!_Jt{0jQSOWrzMR&6iI8V3vdHnvV_}`->pXOD-TN=IjnI}bIP5lvZSNpUYDx* zu`60AvgLLR-Pk3Pgzp8s1e1~ylc!rd3=c=4Yiq*Y7WcKTH+;5c`d}_W5kG9MMv6UR zwVQgsmb{@n`SNObca#v*mP`DbYLldu-;G+7%%?7?!yZu#I3|Vo-}SBQ{4pcgYjgDgqBpGsG>FXr`?RL3{@@4FPB-QS zl=70fqYJg8jxfi_G6&hR1TkZ!EXvMpEmKdqGXJw3hp3z)vuQG;m3jvpv-6ECeYx|m zxvyQD)4VDMPyXsOLpQt@+73_7V$)`*kw9P$n4PRbQeWXT;f3!y{0mRwQ0_#j`SPZ( zIM0n->StSPS76~8r0Y^G*q9enIi@n^RuCExgc4HUy?l1OdHQKO%}tFQq}69PTPD$( zZla^n1B`D1AKJv%X5xG|ndSYbT)n95H~K-5jpT@=?A{4c2j`GY+nr3mOi2%Nx$wT; z+<>c+}u~&4$c&pKW_b& zn=@ZfIt60oJ4znojddblON}($HlHN>*wQ8zm6GdHn{v}Bvfw|Ro0&#kTMFk%?({3^ zuWAM^6^aLsJ=o`DcHDtGe(Ur|)@=S?;Vr3CX0)D~KbAhJQM}H&&bo=$eQ$Wfl2T4G zYhDOJW+-a5EJa7v2aS*NFDsm-+hRSw?9YeiGSNM1C`KdEiSvF$Qan}S#-w5)lSZee4jYUKD z&u;&)cr2C*;s@?NO5l$YaJN%mb!lGSgWh@1FA*aLKdA0GX~Z&BSNTn8D7ny7 zc3K(P67sF2S$F6J%O=VJ8{cRm+ZuT7tq?OS`eDlPsZ~1M-uLq54>hu5|DgXS@#Vq% zJqM@ikI9&kw~tNR`obL@@^`)oR3_8nRqi zI4bIK?1K5bzc_A|c$N5Re93d$HF-|`?AKO>lf`=3526l}$8}waK4Fra-5lfaeOCif z64OlUu@hUAaK-CsbtQwo63W>+VQWYk!-mxE9nSkKwtJs0T7(7(>Oz`&%b4H%G3{n?YcnmWwiF$w zM)o!)uq?&BW!B`6&FG}|^HCd1Yi**Y1zWGTzfy2Bu+H_=w6t6YX%J6m|FyCkK?hf+ zh8pL5U{hRJUnvx{>5so^s(Tt*osfoq(DY1c&sl%Y@3uZk_oO+-m{=&AIH zxstI?l)Ng|0yqCK@za%miN1v7b|n@RzWThgCU(w&3)|NT26_M9i8ubh6lhnAOL=JQS(Plll$I-j14Oew(ea&UgA z^N^d;0e#@S-wa)-;Pih)Hby8p?I<3^h5csK3b`sOL+2nrr3>PcqbgA2MpA7J>z1;g zZg897o;bqfzp!VoE?9#HWl+u_v==slCPk~YC9JB=Qo7>g`^39)Ff^t}&~HFkzN)Jo z1qjExhTOCg(Ne3Dxy=>QjIkYFG!A9CJ$elIXqDEY?29P^RxSkbfgu$F-Mweq?($1l zax>a#0$v{CcP>mOkwwCMe+&@rObCZ~ND%Vj8QlHIE0Z>nn8NIb)f)&>us{~w?xEG@ z-gr7=%wIglMkOC6cf<}TIne%ec*bG7c8B!;A)SLd10>@zWXh@yhwk52 zbYu*q{hu_&GU_mT!`EV3y!AdH!6-ijN*7l6ONY1U%UzMK`XTIc@br0nUAhVzq; zoBX#J^8${%2ZzCt?iE>J^&z0`OiT*{Im>{st#%o;0;G{zSn)W->g7-ciI=ZNd~h|~ z%P(VqN)zNEo5PhA41mobc;4=koAa6dpv!{B<)4Wwz&ry$v`P*rmX2qT%xO|7uGRu> zoNWN-HHLygci#BnVKm|9;mw})z95e80qKH#Z#W#SP7Rz)v{DfRHyF|ILrKG4BA+hX zW9X@_MmWOoe9sj89N_03&Ow@$bO`eYOSDJsZa#c?S??FNb?FObl!8#Jq^ZIQH%+hr zIhxM;BSUxe#p%)E6z5u{4QSI<&J{0k8?<(Lu3T!~1}EKVfWWe=XjX8sd=O5pR~Fbv zX;tAWa4tvelmf6*N7X{Qk-w*mEW*$`AkoJ}I&ybn=*Bb1^Q1fFl!#44`!Yr+X^-bxdOnLh(4G)_l1Hz&V za}kV1`#5AuRME(Jy&}lDOkr%4qg-(YiA`FIM7dBO!<-@GaKh{zuFifoOKcVNXOIZ( z;E{bZtFTSTtI0ws1@aAfiTL_nMkZNgS%^V^B|XX*b;<0>EW#b~L6iaFGI4abYf~U0 zfzVOWmofT%N^O4+ef%X-CJEQsJT&JPoGMm9QscxuihDWaCIl9JI2Y?EhI9bh2PuZ_ zVwi{(^04x&6dZF19R4{^2B|TW{(4+s{r%$YZI&zL;92uRp%=0R87U_ps}TM$Pc@$_p~DS?22 zxKB4|^Skx94dwQiAsbS{lpIi;!J*!qhWYcro*h*-;NQ0g)Th8?z$4GH$K}IgV8=V1eeCdp*jfvEr9Jb~CwX_7qvL`GqgzI=_Clka?()aXCsq?&x=(&E@%t zb3`L5=M3#?U_Fj4^XYB_DG7es2d11sbZ6 z`O#cBoZhQ#!`>CvJhCTuXZxXjPpcqH81LE{l4l(vyF7BIlCRoC*tw&cL_1i6npxHO ztC2aQ*mLKa(A$1=E>k0qeDf*tt&m`uYqAEeEbXQ}=>bplzJehdfbh7ZnlW?8vtp+z zHCTXcu}5i{9@;1D*U%)xS;O4!R|jc*kLmgkOc>5FBi}=6F-r<0y4FvvCMgR$m;FYc z^zh}O^a)9E)1?J95MUnpK0^l}A$cnNSW!U@D)tJ7#KIXQD!-cJ)3Xw7m|ss*M71g< z^a-QGk5vzOg8lBP4^Sfv14J_F68dwx2SX#Ql@oo?z8`G;_tmIq$P>2M7b3*`Sp>(lDKG)D>P-$R525QWR?vdJj|^AQ zWq=4n?i?wsVW{5pBx1inoLNwEJABDYPV1d|w6BG2u~@+f>Tzuke-$E9Jsyb-XG?A; z>;As=uaRm7huoGLz2pdo=hwrzK3#yg?5rk_`ftzIFpOVA=^M@h@n^>?Caf}Ki=1yK zuljw9Gptn~3jUA-*`ld|win+P)P&)U4x)XJG4#CFk@z(CclZkmXe! zr1+S>uc1G8vFGHPtZ8c8KP4U{4SeasAnNRMo0CANZg8QBtL8z!jF zo6Auoctc3NoX&?>a$g*%S~Yk_zyy<%;1?nJ2r)ce7j|Rt4)poY?ILl8&J&+@6_{}} z2RBb{V9vNS_ygHL-Vw#y{bQH{Jc7xFMNjdm|%?Bw?Dzf!xy z8a_RrTmGwhj?2b$jk#X=$4)LgNh>Kc^q4MYXlFj0-3YQo^fWv_67N*^9Ph5~CmV zyMMv)kxYH~_P%J;1`>>4@I3HE;UkFnT_YalbJ)Od$ck+jFV$dM5;HW|Gd5RP!cZWS zI{<|W%a2K+DHZZ#Cx*%Wb?oUnB*q6RAP0ssU2k;E4G5NadiT~d7GGq5JjXjQWa}Wa zkc?418=-)~3faf0!q8THh@&Q2WGPXBG-K3xvM0a->We`tPMXG_Cu@3WuD>1LhFaq_ z345FsgSLe6nulCvCP>0X)8zDYD0Z0m?Z4@=nsZ;YMPMk@OCVkmf+>gXVTYLg9gx>; zivdQ3qu95m$$U{Q4p#>ES*1{g^GFTFQzF2qUy=ww1kZ z5QC4Z0MfRd)M!}sdsBKFpemvkGUv06a7L%Bpm`&Aru2RlqXHrDOJk#cN~N-Y0LWuX zG{?`iR;YyGyQ~s8y=GB4@_`JU3D&%WWFhRUJ%EW;ano=-P|(jH1p7u=2qEZrjEb%N zgi;_7!CI#w0jmq*H~{-Pf3dADwe@pKfG$E{^W$Fa!D4ifzY?MZt?~ht77{pZ!2AZc zFAkXAI$`5QVP;54<}Y0CZKock0Xy@;Q?-8NQxdH@~Ymh}8hhr8z@+ihkBRe7-u! zqwKHYP*i~SSL60a1mXuAv7t)y{)@JOzjt^oX~!&$Jswm+pTdJ!WC`h94MHs!)Gi3` zjvhqp64G>HI1VjQhJqzJT!20Ez3)J-;EL#aV2(P7TQ-hl_c_;3W1Dykig5InhS+FE zOu`ZDSBP>PIgHO`69-iI4*FOtaxJhZD_7xT$SaKG5K5HiN1xK%QKdP3915Oqv~TEJ zQ**vZh#S}5?T76t)$FMeziAZQ%76gPv2RUjV&$aI=0pGUa-b<$mSH(0#;D~1KSwUq~QE)1n+h`p6_1_gJpoOaNDo&7Nk60fOoU@ z>=M9v>H-0Mt#hLozYmiAF00DpTybhLVy4ozI20()a%VveYTZXO0lQPE$nN}BOoKgZ z^@1=Npl4jJ*yA^P0CQEIK|Y?3R*`Yt{N~1s#k6A?V^YonoM3>DA+|#Td#@Tvde=nL zja|uguFA0uF>$@yAhReLY8+Q+x;55Q@;Ui&%jsC7+xvfL$>tnwdF!+=O1PrtwP#Sw zt*>2$M&kK7w;CGuT`N7`XjjO&@3xl^)JpE|kc2fEyqu2e518q%=ahP-&k)*XgQy-g z+$<^$p5_)%ociTuz*$F!h#ci&iC0qiIh!)`iG}<}`4UP(-nuU9aqXPAT@mvZeX|bs z_8?C^hi*Jp;fzt5=|-)TCK?0FLzrh-^c^TS6AA+!^PD^0;bx8NAW(qPe-Wo>$=D4j ziot6hCJnZ!P!*byF(zvPRUoH`en8b2e!jZo*O2G&M5htHwHC=uDl&tjg&w-$Uq{X3 z17klc8F6Acm^bixZqynl+Q|(9gxtQZMNWsWd z1pKhUzzE6Vi0I=}zH*AHG!LXz{Df*4V>4&4KSMBWaNr54j>!cTfP0y8RWy4g-C`7b zs)Z?KM0ks+Fyk;_Ehc{5(F&(iQ}4M`1x~sC*pX5{;(2e?W`YBJ_fxnEhsVnVsq^LCWnkHvUsE%Ce6hjA|>8_&mtH-T-Z^V5KiMRVL; zajyw9%GikaeB=cD0`H|yCS6Dr{6SqRXllpHKaZG+OeBtMv~O#-#INIlj~zy=t=M3yhgT^ zauU;)tW;Pgiew!v248+FBSV_M`xK=c&JN9y?IyO7>s-sY+Tol81*KR3`3XZ?Ub0I_ z-I&&Z3hKHFZ|jpzQ)EHz)wZZ*}xT)b~bWy+-T2Uz@Ep#)s-^m9rRTkp)AEIv@Sq?Q)=BX<$x#yfHQ8s1Q&OJ+OF*BcM{{xs8%TySV^!C8C zSK`km(bv(xe?XK zRVyjKg?Nag8vC3Bm-+-)xOsrmC3&_Q$ru~2Mzq5ySTu(sc4K^#MhBt#z{>7jq^pr% z?i?Ar!2)>weE83s*H0l_6V%A~TW8RA;uN^Dy#fsOc08t?H%vReFelcn6}w9%!2{RQ z{^NP1|9sp9j%=nHDVo^vK)Fr?ca4F&y#M!ik!6;YqJ?LJXcL|TWSD54P>=oG|G&MB ztB-x<5?E%~r%2k^V5Ny&!FJiV*uV&4OPXg0O90CMH!=5{Q;8}i=;o={jXlG>yvfJH zUb~Vmw;j0nQF*=0ocMmJ#V~nzj{enMv|JDrRQ$n&?eqK?_t1c0Yk%p1nMSGCyCSHW zv|Rq&Wk-YOhX`YrwXPQ?sgY-B?jv~|xw{wgraPr3ThDB7^id4u4)V8_?K=8`mBYAr z@j@+kiCt@D-!#9vCUST1l5|FI1d90_p`@|avTpeJ)Ncq?ox9tF3Vr>>UH7@QKiG3; z{CpOs;$(&vseb1GE*QlX*%J0x&wB}Jq>dkr1)st5%XNUFzkWufeN`M`sO&z!M4JF} ztgAej=_)MX$7Hk6J_BfW4)OM>+(Bz2XYN)bLSG+Fh+GHdzG2V&A6l-@zXB59TZ(&8 z*YI~ATw1t;xLeOVjRYzpRm#uLDKgs(?S!7JNhVHdY6Oi4!Mr^*lOhiswRcKOF8TNw z4C{U72;HAm@Qi93=YV0Ijmq2%?N#4FEB(VIjjQO&W?ij;(#f7HjRV@?0egQzjI6Xr zqF&}x!_yqmdN(hcB5Z`Vk9p-OTrI>3_hxfd%c?X~&+4&b_9%=4l?Tvutmg^bEC3bq zuYoV0+fHD8CaE-R)yVMheYEcr+o)#&T4m`DKYt0ddu|rNY2@eqY9u&<*>&#>vLX`c zy1h@04E1jYGMk22)(7+3^)uB-<3}$wawQx&+4ogiZHwPv$HcZ(5p_T22U{`}#t}@B zDib1`J#zMkPx4C_fQaZw3ysOIHrB4j=k)vUIi339Lzc&Pe3kb9y;8xjQm}IPB7_0E zg}A|u02M6n6khcVFfMD4Ys4%f0hn!lfZF1wdDCH^PvlRhsjT z9&lA28|Y5v7JTMggE451>ic?J4@Bn1?>=zo6w9H}?j^Or``cAz|0LC>&)O|5KpGtsmZfw8yy(_{h|Ve1CNEkLMXWoS2|u ztXvpJ`_e^G=8crCJn_S+|AgQrTt(1>2p9~HXG>Vq8ypQ*zVG!RGzONjuNJ2&YpjDA z%HrG~u1e`=K)6k{P{4h$(fcf=+t)^THGZ&mezpYHKntyhMl|-`L4|-o(0HHR`PYZ%ze3 z09hgl!awtcpc&W_D0LD5tpoTCYEf@R^v6%Bduwmnsw_Nft@%#|vd@KW ziJ7-dKitIqT*$oF*V6AqhEem<(z1QG743b#ZGOnS;9FP1<=AvVmiV{El|`=u!*+!} z-_9pXKkRXQrDY&vE~nzp;mE(!aeBrJ=$I?C;o;Bnre z+&#Xo$$#CZFOk?x!dJ}JM$bV9{ciBmA$+m*IoVj;6?<`hK_F8bo zi0qd+m`)ycXUu&gOt&GewY&HEgVm>{2|k_Tvoj{P+LR9p-9qgD_;q~S)N6|=wuei9 zPIoDH*<5q4*uo;78R_nsJ$mz9c}LBO{ma)ltkqxZc|jOpc&#s>Y}4j_?%|!5#=;vn zm*(<}i=V%@{%9*4nD(QPe-q;VD<{G4-TjsPCjMLHc|bNd&FTc*{qaRfEnnb7#WbI% z)Jl23)W~g5`5nT!ORC|mpsfqpFvozkEoV45G~uJgEju7x_cOT?<){5apmm9nyr(K!mt7%N!IA>MR1(+5c1S z#1|MK;*2BEBUIy)JP33x!~pTo7zcvO*x4QO5sC_Lg-n4&<_Y2#_Ov|~9bNcKBR0j5 zF$^8(hC2Lddju9dcqUEsRbv`{eROddLa&VPj`q-;?|@VC!Hm(S0bF-J_+wR;M)p}# z@G!J~AUUs+!@7#$3->2Z0fx<8!TpY;Xr>>bI&p!1WVOb3+9<$t(bgjOTW>1Kcmr^N zyy?*cP`?e7!PS)FiMas_Zc|Y3i9`B*snadU6x^969Wqt|L&LGYg96|?UQT9AYKL@= zDgNPy`3>MGRVJX#ymM-mQm~9bq7x8ce@O>Wn&qD91;v#0P&G)TR!jkg4G9U4t5LL5 z3I*#HsF~q}!{dv3ZjdYZz!lqs;ki)A<3%OE;G|?lrd)W7RkAwH$g>y3@+*rjM>)fpPg2Yuyd= zZ|`@>$!MlAEkAbry!_a`&~WSPnKZ)b%Ey;xw-_3}&f2wXW@cjPI`WxJmprS1z|Uvz z?+hKce?olb(b*-{7h7F!^mY6iD!$aZo*Q2pNm_o5 zVX)0Tol?d7-I8Z+diCLf-&69RKNJ4m`Sgi=*`b+M3(De{)N>|RKaE*f6gtE8^6=`` zdwaLtU1FQ#Wp(@MXxCY#8?OZX(VXR;Klosx)xiDDcf9mn?!>xQSDX`9UooqFw`WWG z#-t+uMWg3dUf61XmF0V)GE4a3zN6;PYaaJZ`2@e*+34PnpKon2=-g29Il;tfGPNe( z#x2yrcxL*xnb%w_!Y{o4*>x83;vh1c_H02@F+Jzc{?%gFG3c~A{0$PuR^966yM*0;8B`x4=UY| zr7jLoOJkN72g=lZQid`CnfaN@&+N<4oaL|^G~a#@5iA;U2X-}MR2BG{QQ$u+sIGl5 zQcCLIPIK3y(IDMckZRD2SR7=0lWjWi6t2~CkrN-d%4g6@Zt*>9SI9=uhzcB#EiqNO0K$nMz}qCx(l{I6OLj+j?YhW@^SimB3J#@D77fe8G%0l~$ywt)x& zU6#hNjThOW7n5}nzf<9@HEQG-+^@KG7b|PjWlf19HH}eDN%EDrzmm-32NdYHWR@jS z>NYkp&c1dK`;_xd93%EL1$~@cPI*c>>8MXCFCeWKM)QV5_oImx^lOSCuWqIc$F~?qI}A(eNgb(wvd{Q zN-RlV@RyW4Cd#PDaL}8X^xC1NVmR_-NO;=v{t}aC=NyTp-am6xxe_Gy;_=rJ)FOi3yW~MOrw!JFuUTmo4l1IuObcf4=*0CM#grD$E2u{>0ZqNp%5AuCotQ+ zw9t2x-!KQ)x6u^$-%#GfGH1`9+me9Fvz`_LsKR~T6RPX4IUSrdQ7T%Dp5GnK`QTwX z0Ap}Wh&ki$HqygJBK0D;VV2&}NTrdSauLqecC&y%R=M6Z*+T}cC)Tc_ ziwQZJ)jatvH8OIzj8){OIU%Z`S8C*@5jIHv^fnQJ7vREFbL2O$#Ccgo=1L5O(3G20 z#X=!Ha_j<04lMpYdv1p4E1H5E+?%o7Oj3f#OVqBOfF$0@U>t24001w zS<_?%&4H|GUEB$=djQV~%dT0LT9duInBpfai>Ur0?^OwcQdCFgQ1S9}>M=$y?`c-818bOY zF#PK4J*`wXWkf5}yzA-ymuZ=aO6sk;tv*oTYb?gj{kd%)Hn}SN)UO&{F)6-i?V=|a z9z2iOGkwvcDJM59a3-C1ozk}~L+|*!pl$N7?ENghM2$rLc2(wq9NpzDphyTaEieiJo0yh@Ff*@z3$z9%`@ zL|Lqu`{Bbnn}yS#PFOSjLKw3>Qfs+#@*DSIaryGW-pe~31x>#fT!i|qsLbzZpJzav z8fgzW(?`8-QN!9*c>S9kqlg)4ztFdk!sr`0dQ>hK0=MQeNJ3U9F$XHqa<3KUTtM^e%)z)h z^<2yW3!@wAdwqVw7`%I{f+86XgV&%@pxt%*6^|%=1~o@P8b64V#sd~;Eq3= z)-fpJsemdtdT3T=cb2veZ}gPXI`XNs-X6qFm2+Q>R575~rU(YOj6UIR_Wfhr9_k89 zjC(K|ZG4ZM-~aO7QcPGw6E7BVlu2}bU9U|<;>MBhY9!f%crrU%AH)voiU!rQ|FMOJ z8VTCZAJG5zEgI*%wovWntxzMxgQ%5~bJU1(>~l5JdI>@I?+?Isfzl&0K1{oG2@&4wOv|H=^ak$!77gmT?C(CX z@AOWr?a8Mp53}`$eKv!hnhy4SY~%vDC)f)+9-WAdYEUDR_sHoyw@rubs4FPa;jjv9 zC7kvT1a4g&Po*g(!*1eFC5+ksG7AQJlB{xqmp!`z3Fq#2-u546c))pZdjZ%QouvW& zZbCo&Yc(C{;|p`L^<3^{wPq?=cyfi01NbqWb&v5ajQaN$_S45b<7JO?1*_=6u7}lP zSd~^7Y}I(xy{uvM?#P|FgzcS*$C@yAM^JEj0Bo3LjMn32AJ)5WF)Ep;X_ITJJH`ipebr3*8GDb^4HpelyyrtD$jP!?-pf7f@3$V{M&jm`pFTlKKxdcz?IsbT7_PuDdLJB0o3`7vT)gx%d z7u$F7Eii9*RVd^^&?Ld&!#l;m!MYxTp+ZsH?Jsudb!_I#RGt#`jYIJ_kK+Qi7SZZn zrf$bOLx+QU@KzDWZtSo76W*!5u^Ra`2COdM$A?_cZx+G+_6J!<*MX_~~phU4!759!NBIE*_-S8W1+! zU9+q+Oo%7M4L9DznWhLRnHe2k4~F-d+8Ge-96=I-agZUi(K^Yg$X|{o02GUB+{yib zp3cx1Kr;kbE9EUkoI&p342E8$JQWm$UT$p?yphPkjSle!)*audMm8>Wbp= z)a=oyrX-2k6=?v|m|=btzb2U27PI8~Z$7A*eue2Hnoz1A8U~)RKdu(mrt7bOZ>kUT zUt=Q7`PVRB%2N9$wEDyMs7j!W|2V`1W8$si1`I8D*+0yclm6){2{ehF5uzblAI%DM zyF2svTVw4cYCaM+Sr)dh9qYWGgYl zyvRfASP`+BJ=2p-v%xD)(n!WqjN4YFd1g_ljC%)R&?jUR3BsU;94};zGH&wFY}&C4 zDn3lJ)1x877#FDF0ZGd-f2$6-_~7!c27e0=u<2*|_$tB|_LQCRJ#}mo7Z)15kZApq~w#Q<8CI#och0Quo*GGTxwfNXYF>o3JwC|m>UZ#d+dIRHs9c3_-=602+Wx`q&&*#(FVf`RRt0iUaA{vr0oZ$iGDMY?NlRK zE{V+c3Zfc$XY__LxK6A_u=|G+#T4VU4a_pXA!W@~5mRw(*K5HZUY1h`ceqi$L^&>F znHnho>WCKW5dVKK>GxG)IxaR3l$wJ{Emt8l7Mh|^AEp3w_|X^+t`R2YbdYbg@)h56 zU`Wni)X0E6jn+b$AIDZEg7NcH1sxjI;dr$)WYl+rLkWlB`NL4d4~PtT6#q*x7Ip$k zNeuQR=F>!yGGwds6>})?=v+k4&;~b_e2HMCX9KSVsL=Q9>7IbBannm@Plw_Liby$> zp*@T6{uL)b8o9&FC{fn;7s3V&l$-`!vMET z@`t&=Z!eg$^e4NQ3%~8e#dza@;O9r+m{m|y1$QMc+u5aBg=}Q8K_bM}FG?>3g#sMN zeQ(esJ+6=gMZKQ9RZk9$HeAeyUs?bq?o*ng<44|5(IzqX3c5H@pS*_>Bc{E*26(#b zH@{^T(aGJN>ZJr7%MaKL7aEv`Nlmdo*;XD}rz*aK3(Y}T1F@Q_ORVuhjZb;U9H(ZL ziHQ|_afa233#G&O#nk2qyhNv{os%uUR# z`aM@(_=Th zI(Ej>D$G1~k(l%5l^L^n==fYGqlV>tF*!j^^wT}HyJ-eCqVGQ5oU53b z|Gt;k#;K8T(Z1l<2g`p7`Wmafut5GW0Is3q=$bh5+dr}VA9d&swD*9_i8^1j3mbB8 zL#zDPC{RIf9y*U(z2|oILl*N#$9Mj(g|}hU`E^L{OW#k;!`~fvs!_J-4FHLI5FfYs zqUrLNk(iz>7jqlT7^8isp?~ z{nQ~h_N7iC{Mx4^E5__&Dz5|5O$~e34^jj}K5_v_H0|kpRCE`PTzoLV&5MB=p?4bC zq^p^L8a)5LLO}ns$)4)p^opyQ3a;$>K3`P4m~E!I3Za(@9V;h~Jcu;7N1z-Kf;tj> z2ki?+mhSVKr$*-4Dx(1x|2-64t5Ez(zw~qNEm*nd86tS(y9k0=d@~TL#SJ+2wky z5prvk|9sG~+{*I|Xv7~d92tp4-2ApC*so03oSVvCAoIiyum6v^H;;$v|NF*ADs4U*dj`E3+Eg^}iR7hEpkTFLIm93JLVk&Bqn2PL7_N1~?RAVgJGh-dIoX`Cl z>Uvz)_u9Vq{k!k$@%v|nGv}N+^M1eHul4zQzBT{G*-<^w-anJN`rPKO6v58~aN5>H zD#Gx?o%8#AcbgAi_=WUuPZl6OlRtu#isHO8u~DeH78k|9sYJ*)jO8a#C-Zy><16aP zlu;f4Xb2{`?{(c18h*=%(6_Ds<&9;V^+6;NsmQ=6km+}|)sEIM`}JF_&Vfg~xi@+| z&sPktg4nKY_!8~~*Sgto_`(m$m`t6(T!2{2*`)U1A>l_~fjrXToqP$jCnWs(H0;Da zHC%GS^mid1Jn|#}lKV7>8gQKi+NGKVzG2H`A-K9527I~SHEzKpBoN?1kROdA2BsSZ z2m=?s1xHUU>czmG3BUn9S-h|0;-*h zOWJzNr)KyQVUN<0-pIo3BNgV2!wr1^nPkl=e(}YPzPxAye-GzrT3`J6zFikH*wPi`b?l;4YM+Uh zmtWqwyGb1mCRX{G4b3CJ`g`q))h?F&{OMmtK3lJf^Q!a8?&BA$iCjXMFuZGBX7Nc8 zm$Eq)BH(Y!YartvArGKOs8<2I*VKUH zb49+P8gxUDymK~)V)O4DM5{j;VTpd65CH2`0D_ca^8(CODlwe@%>8C^7kJlhP|5EQ z6|_A`Ip2;WS+N~oP7vUkpU^FW^-0y(Q#Y8=mng3od0G@T0;RvBYH#UH0LUJq%voKc z!h3{~T1Kh_TO7>h5rD$zuz4Y5$Hu{!XC>fgP_nP!4=(sU!vCSX_uDO+U+ZaJgs2EH zbBE19p9))yjJ4MFZ=7`rcVD*`G*|DeeKN;q+>8y6#5PvrA(uBF(u8wF74S!J4;Oh( zdpzLy%#vQATM@KU=vzYp4N#=uSI>6;?Z&JE?{gl7)(B0S5Kmv=p*X2Qua;{@}5NUDRQ`9lb;tL78K;Nu;Wlf+&__ygY< zh(J5pf+ZJ)^>~Z8D`JlUeXZIp*CTj>fTp#^IUBXHLC`JpC@Fwx{Vn!M(jJqL<5ycd zg!RIQWP~|EXdpmKUpXI%23%zwxYj_UfU_Uwuyc_?jb7lz6A{Q^HN+7;vKac6z;Onz z+3`BDPXjU`Rg%T@31ikI_F|j!RA)tlTLkAV=7Xr&-(gL0=|Z8( zLkK@zW?h*6e0CoLWFB&|2k3riVxx#U&a6bk$=ix`l&AA{h3_$~kSj4o5 z$qfIAxWJY16P{QTN9O_HWA23lLOG0$l{>W`Laf8lF9gU_+@*xd67w6a9;Khwb^JPdvPyac z6(B4S*=-jjLX;9B#zMd*DFidAUQjGK9Zu}?0Q*vfFQ!Zwdk#;_X;Z~$wyf9rtZ$Z- zG?+rS@DUw!I$is6Dtbnd&D2O{RG;vZpc!o`OwB&hp~iLojl)eRW&h~C?ckU_?po#C zetI5~%Siwym%p3VjU@N*>j;yupoei-B3IMof#7^Sgn72;WDaQy#S8sT>{6OKfF*9F z{s3M^Iz)N%8wJSoM+=Q=rvcNOCJj6JpZ-N~O9Ja)Fje)IQuW8NDb*AA6^R0&Q zl~b}1#wj`m_d;rae}Z`f_Xh56kb+qGj6kay5da5JJNpjqH+Z3iM`Z!i&Kjtc z$O={3bYJCDkljV(-~mux`}7uTSdAl}gVxR{uMNFqO3e*i`geS-u$A;mkW%fOOSqT6 z|9v44YvIb`(giq>Ft}(l@&gHj9vo``xMOob5FbtpqLjjn*fM68$$uinNs2aHP(hDX z&`EPxBs*p_96S~VGHUT#a!J1xqw3QEPBt(AnEo3z$1~*Z;v2ANB_1L2lFhoRoSOEh z^w79U(#E3ZJa^3AD(>m=rY-(wdR%6i;z7=Op3Wrt+E2)$*hcCnN_U{a8 zag^tHury4~rTChxxR4=rl^a=!8Vxj`ApBfG>d&{V?2>44k{X*(RujfpUbN;LxgJ0U z9_9Jx0+=mj^!a?7`bAk2 zPIFT`OxGFZv&G*W4I+t4)YpY7sI0v4{Pe;o5CG*^zB+7$pTp z`kpE6yrEz&G^$v-d_!Rk`=K8GqoGH?pk z96am5I`=)SXl9Oq&%AEK;0wo|-rOB>Vw;_Qj@3<>peo2IYA(d~J1zvC3-d5!+&mch zOx6T00wK)*^k!Uw_$bQv)z|Cv<_8ByDxOF`ush~cLc2u##~TdE`5@L zJlBn6jM_m`Gar&J4IL-LjlR(PTBE-cMi*v*lkln>Rire|DGN9HhBWv92w!*@8DH^e z;U@W86b{ztPZ(R6HQg+EWPj#E!q|61nqbyYp_sq`+z7+uP7Lvp$HS2#`q&C+lAXUi zCK!A0d>k_D4%v^u)Rp7x_K!s-33gj{B5-f`;8i)!?3Sx00#8BY&S?uk8&3pn*q@o` z74Upw;F&$1$PJIj#X8l$fd`VHk1Y~@`&*oOcOP+-_Z)41(*}j8Mv)zrPo)Tb@nHv` zz|)pZ7+d~m{t_J}Uq1PaT!iot_+WcH&fp$Y*VB6@Ex9yz20!bO z=s$c7Ze$DyE}_O@cMgDwXK>nu!d6l|e>t?0wg%z<;p%zc)zZb`}0k z$UmI(kKZs$Bii^K;cO!W27B&)r~gtO){~t2x6f_B?mH|x6dwk`qi~D_<%PZf?zRhl z>TJAC{`&kpgsH2A-)%A5Tt4;|OyLC>o9SO$_9z^EDRYw zhBY+P4m%64vY#*_fbg(A9cCzQnTXw!dQZ4WJ`6j4@dssM#UuDGg<0gb=PW$GWV#1f zs#RH0OrT-ei~f{82L}E)v|X_KRuFzm20GFSF`m)#m`bjD5<_?w1_}(ehiB+>1}));4s0t}s+9PZk%R;xQF+WDtO4aW=R~emL&CUdt7Q5YKVdvnCz1d>eM5X{e z#Ee5KJ}*8x_(#KZouXTBVc@@xl4@H9h3J&S=oJ$BX&uLd zfhB*JZlyPvfwy{F`UlK_p8;e#pc#IjqDP3Qvn+Ogf5~6mxgxB?xgWGx>J-wq=Wm=EBKXGOK{}IQ zFgyjhj9=fuf$w84L%qnc{6y4Zc|#;U4CZ`!-0%Y2+i9ab=c=~kFr`MA%U`~f&J?C; z-z3Zv8qyCzioZ>`SpxY<(E3LUSGmkOr0rCT332$VHCF@32zbcm>E031)RGy0GzYA? zg6$xRu!Z$zJ%wpBgxfX((Hsa-jA}LrGrY5wYawPEMKVPeKBQ0yK(@OKSK>K;LJ%Ny zOBc$$hAITOvWTA0xc~>2@QVVOHsaW$c`F}tQQeKRG6Ea004N`5T8&jghm@^Ho{%0Q z6`WKd0@IFr8R%(aan>^)fT}sfQRVk^Mo%eg(y}6?G9pZDc=P{)L*O z!p><~++NT5AS}Bec=i)*p$J>gW;+L&KNWV(N^120Z!pB~Vme}-7!1m4?+P7YdH2$j zVmt!d-2)gEU+qA1AvK}{S7cb|?;Y|U4V;SzW#*T^)*y1=H0TL^2$ih=e2x|bLWu+T zaS-!LzRRrCaW0+>PNQ6OiZDEmE+s&F^~S2{P}soCAkZGpxk?=d;UHkZbaO{A$aNYe zbilzdG2L)s;YvZ9Cqn6g!J%vthzLbw(PY0arLlI7F)kJlea^iuq63V1*!v<4_GD>3 zvXZ3DW|Tw|{1S#SIlBSE+ydneQ}^KxG*r5yCDO1IsA@(BydC`Y*&3tR70oyE`5HW_A`1UNrBN=y^?=E<#PP;rIE1#vimb9Id%1m=BC+}vh@zb>p74j0zhLL>9c2hIugC%Wc@e(Oc=<#R$@^UR>2?xdXOIv@Xc&w_{ z6KTJdh<^>l9M5NQebxzsSeIF2&bJZ=xGWc3A<}Dww(KN4o)v^ViVpIKzD%4Gx{I`) zBW(O`Oh^i_<(_(Hj%{;aG?@fJ6$L7y{S+}lC%?Nh4AS~EXWQ# zIikI!PbLY_w#sS-uMVRRwa;Xkq_HuS#`9AV^nyB>VExvx;80#dS_B2ZKmDy=VX+qy z2CyFse}DQrC-nqWX*9^T$DH4vBO36}v(FhUldN@|Ta2ax(Rj>)YYwjBb1oI~5L+3O z9#a|Iu-?{TEVrM+kI6^B`+lM7S-B@9iNJSVJG;G0bPC8iK6rWq zYvvfvV)l!#?c?AOqi1k|LY(*?xcG9>0qCWViHZ3Uv?OS)cTBXIF#U$x5wu!Sfl}S4 zCz;zRPp=lD-JZ|ddPn5w!`p!_v*v0kjtGlk^Nx-}Pbi3`Lk)C#qp+a7KkFbgD7`w= zXpTYSaXHQ!ZeyhC>^$jj?QC z*Z5#q>7_Y|G0v-c#j0Mtwy{~EaL(ht^_`o0WNhXfmhb;?C~DriwL!b)y$ztnO&)Px zM;Mu1myhSNAb)g8*>Rv-(0tFol7IOEpa>?Iy)HOcv(_wIdxf+n>9&-lwd5Y&^Bd2TMD%bA{>B~fgO`4dxdGR~ zq$-x?eRqR~LNG*Kov@113y%L%ghgq%*v*H=S2|H+Gb%P2JwUXhji?l&w}imqwY-)o zv1!w!j_sSIjfkVS0tfiP9|=E@G})Pt=hEL7jRUYkXtE+_J|=b%!Dn7QeK&)dk7sT< z^_A!AP&GG$b+lsj`8tw8O{=o9`@2#<{K|dbRA+xSyC{+`dhud9a)%tl7}e*!Kt>J@ zokP1QgQ$gICHgaS@(|jUFht`=2N2{9UwmWYW2xuG_$6wIorKYD;s>#DNmq6Io28RS ze6`W{1S6<>Z3Ylp+UI#AnZv}X*{%pbWExp!om(t#lTe9_%&SMI76MbTa(e1{JwbB< zxVuXt6=|4=#oXaAD=W6fb!~N~o9L50DSoMCvKpadeYDKNT}m6@ihCc-svqnsxXm!h zP27DbCohufr~GTf{$MpvW2Te)V1|gj`o}kX8h*1y+3CZB8)jV{Ft$uu@a4l8YwW3w zMTk)aGy-i+er%R}$I9z!!V}KuqH|laMWQ~sRU|(@#nCp~nd!Rm!^@Q8E}skcH$?Q9 zNhF2rd_>jUSM_vLxdZKu;h+Y)b`k19lJBfHwFRpi@7qVDp=EQyJ>|-HGO*>RDf}Kp z`ET5{h7$UiC96sXtGD?K2V)FfP?4WUgsrzkVe9QmXtezZ1+|AvXuYL|puJ+c zaO5l~)5PR(o+RH>=5szPj1_Z;z^mCuV2k9?vCyNSfJ5%XeY8rkb><+RwG!a(UHuA9 zla3);x`gBbQP?nK_@`c=qeV&BY7J>Pr#ckshZ#^*?9oKaT$x0!NDZC!U@QEevQcmy z)9_00WfJJa>gJg5*guW}o)f991f)J%li1Qz4F0&TadSlyvqq8=T`FCGH(a&Xm!qV3%6T@S1&A}N!r|=N7+v6(n_3_Wqtp&jN{rv z)N9pVUkc{<*WGz);Pc9-|9Jn0JJbcXlf%>URmgfD>eIOoBH6_z#glb{>i3x!s95b=eo=9qUF-yl&6o?eazo$had zmHVE&q96v_$}uS=uVp)?zT|I>Gi7}aN#%rDMpX~h|1diBq;FC-w)Kc1a(U=jK-`tq zeU~@nT-k8*MU~oq6WVg;cG|LTJNM)9C${quZC(4z<(&g1QA*omUF(W#Q>I(_t<9FC zZnU}$?fg{fop-2G{#!rN%RkG0e9e)|3&rn>B$dj97F)(1)t#l5WGi)K*`^Q4kMAFf ze@3;`%GrC~(zIg9Me}aWao6sBKGEizOV?rRciHL#TeS6*2>0gBR8LLGJ}~&N%wj7s zBcDg}=$XIJo5 zeNF3{H!R1eXC9=Fef^=b^_N4fnmcwhqRx;TE#YTUMlz2%Hu`a{hCDRuw0_ z#@LeFtj$Fd)XWgMY)K=>boujP%C0{owjO=szMbEx%SKi+zqLunbb96%9<{-L39w_j z9HZ_f9xk;k*S~F@u|R57O3Rfv^^L7(jJ)@4(rv`bh;pyIXr$FO9u&xL_4qq7_Y=R^-7>48L^Zq{a+Vq;P2P+)emr=J-pk%kFUVm&|OXIG|w5j{k+ zWpgJU9_+M5WgYgJOUvetZ&BCvNE0}J4Nyt#U?2TPw@#_4sd6igiy!G`RW!NlA$dY< zjpQ_DXEP^e-~;W&HF4@0>DZ*(sZ4 z$oQp^X|l#UQa2wyqDnic)~VGH{G4g}eA8#i^RC8Twr)eGVPX1XwPz)?O;{!rySEg` zoYtw*TIm0vsVH}WOXT0$T?Zf_XJl^DlHb5t(-83~4H_xV7J`lVue}Ld500Ywkc5HQ zbxaHh3x+&^xFhnPa1Hja#O7D={<>{0WIhq~A9QX3Qo94<|IC#HY<@9s-%hU%kIVWI z__0WD2A1OUPh4pH3A!#?1xfZR*c$JmkiJfEpnB{L|J_>l{Kuti#jFW0TF5pprd=yq z$2s0{pRb5^;1?#W;~5N;a?d^e5woc|bCpV&&6E00%UZS6uR9O-Pt<)r-7qxqExC&D zU`cX4=1}G@p&F|C@U&rcN@!a6I{~q^vG{Y^0qvrUkjD|!!JZ>g@~LmP{IyQ^%(l1t z*2}v*-9(PO*>l)z?FY~M6$=vA-j$Xa65QEtNnOC^7H!B{PGTq76sljkv!S!v#x^*d z^u}GOI=SiBE3f7EtH1D7K?~9~H#bCnf#5Ri(W6BsZMp9E z)KbreoPT6**J_s{Q}Z$F=HB%4n$C;fdCrrwn5!KY+C%RHGTQ}H@=2r1aJ|vy&xAk= zYXE(ZO(;yqc6HardKwABiX-vb821(!3T`=2Lyb)!R4XA(wlnRu7_|A}vMU7(dTPrm z!A=a+iVf4>IU}qj@*+sk-?n58v|u6Pgdg+rkF!+Xj|WcYp@_DAfJ-l$#0;x{AT=t5@C!Y3g}Y$Lst|hOet;|W z=Us!c2jZVd>FCkwVYKV_+sc|p0<*s`#Sj}0QH?S^F~NOvcs3Y z2B*_BU?Dz6a=tFhzd>zm6N;`_+TiXM!|9di<|y?U3v?^sozi+>2M$=0|*OOyd?*c5SJ{(tXZuF<`I-R zo-i8H!OXaBa3<2jZVlhU{ME<4EvHpzZ^&ow+*`zNSU{ST-t9?UTJ%jP0m)Y`%it>T zOwgN11-&-9UyVk}SAQV-@^KRbQ}nTz$8T7}*T(Vq0r}0D z4eMh{xl7y1Q~KQ}YWFP5*08a0mCCEpwmP$<+$+Vk+J8Aks`2jp=Z8O$vRL2Ssub@N z)vLFCO%&UZ7H;`*bVXC*(z%aqo*I=zZXWO66_ub>Dn7rVq$6Q|hqHm^{o}6}m*&0Q zXdMv_3Qg@<+Quwj;%La@zN?*_5yR)TNlfSO)p|1*h@nW*vQyK$*Z3VI~>0> zJ*DKWmWlo$7I2iElTjDwn}+JBJ&RHJDQ;y@NN;dzdD&)Br_h`ku*#svw$`a>@XohB zTH4_+>j(FV=f-^0`nqwOvQ<>(@_*w)yq1ID7O>>wx$@eU-`++cIBi zMZ8jHyeY2~xmH$!c-N-4yel_r;p~h>X7ZAy_F|hK-YXC%8OUmdNMC-Oc+_!g>18%E(^7Oq79`&jN$10RS zmaif*?lSL3MScMqHjJDYJE~ZF+d{O7UEBxAfo#~#X8xXAKYZl4_GGnk zhxqc{)>mAAea?jS{^m&8r0-fKYgOCNrPn!^(xufZ4}MItogZTpxu~RYmuCE(zUKI_ zH`FlC$BNOhu7{{*SKe(g&rZ{0tWuhdl-o zO*m(|lF+b_Imc*s$elULnLliRuL zp=)D`!QBlJeYPbXuQ(ApexnD=j(hnTzh&Kfp*oo~OlxqP5-+;ApY%hH zHIVrhYiaYClK<)Un(xt(c1m8iCMm$4r;>g2!PdY)^B2FRsa_Cwr)ZcX)>HHy)xd6_ zaml-w)}ef`W?$Xer;X}LeKN{JPbAa-@+uguQL8WTJo2^GexLgH@$tr2U%%Mx(dq7D z9Vu3;s*EmgA1PQxA|H1+{_37ziglW$QF4!F1J!Kt)l&!GUTSh)u!5ws;t=f`?O?)! zW&JiO^Gs4j^`-8gc;5a>je4!zHYReHMb{C79VrKsd$bG^HmkYr^+|0>)wC?Fr5-Fi zbEnVhVP}&oDpBC|((z35a@oJceV+^{Gppabzl{_#Q}w%iWC(dRMs4&lIXph(@F}t? z+_aWqS#aOdaihbAkYqNSWL?UUU9rV%-AZfA%J$`MdoOs(L@C8=ITMW4RTwS|PTP_{ ztLXJUbE$iV3->OGiRh8p`y@Iog!ye$#$G1u@zvn8%az)13O0A1AKaaIBH`5dqIG2_ zQVh3MAgN8BrN`tqWHugpJm0|ceESc7$@{D9R;@@)de&@nP+w_NvCYAGtAqB;mW=0W zmh8B3$NtXAJ4qYAR#=|tdlnn-cBnb>OteU|m-~)_Q_CBl)SIdeHkco3sH47cnwcc( zV8PQ#^qq7z5qza&N-TqQ(Es~*bdcL^=?@(0ty+1qT&Lzh6uJv@;r>+_CopFa$AeLo zLGyK7>33%yf_1ma&YmE+)7w@R;%k;`Z=9G7AcN2Jczo}{47t}6!aYZz@yOPr09EPQ zI4db}`+xx2l@_G?Pl!U(7NoWtNSkiObcVgFm>mp3HI?byfsayKYp}~Z%B?R)nHb?` z>f3QPByj54vZ-HNFEY&(19Jj)p%?A1JCBwAHgf3W88au!v2PQS%+%`-DPwxz6X;9} zpvbf`Q#5+UaVyI!3QOa}nb&te*vlIEi<^~Pf26J0qsRQ*+1DjookLCf>h0Uib$=9Z zrDjF-&;R9gWBx&dk=J%v)>l9+*eEVf`Kk4gDz{v6Z<_I2|4mIVx{*`HUZo9pHrh`3 zcP&1Q_3p^{?>h zkpB z_4(szYfG*7AMR^ueMWJ1zT`M3JKi8SYsn}xCwP7NPoEt(R^;8Tv`PtolB6xaGfKXY zH2bmKMZ$a#IBHNuho}3K!!LF5OC5^{cau`h7R1_$vt_n=gj7F{Z!R^^+|*FgbUsu` z)TU5Yvs>#}YR9WXUQ*k0v~`vD2<#=pOY4#x^3L4p>Tj^>{JqM3zxtc}f>_CKW|b3M zJx0Vhbu(gxcc(x28)t;XgQF`F_v)$-H1_tbm|93K=LA|&`l z^@M+g;4H~O0U&x%%)4Ht*!VMO!}T13B}Wtpa2z8#k$Z4XEY76@-774zYc3NZt6o88 z&Ct;31Xc()F_lizTKCO~Jhy8$hm;O(r;a8QBhaZbhd0~MAQ+n7F<;VR9PzgZq z_nWYYODK5%L}^i>k@vmJl#$I}>kkW}ZHrie)!H_PJ0X6Wg=Lz8qH)@`3 zr`ci$PEuG4&mVr+V8$|Z5An>T0Of7|8)u*sM(G6a2%9~Tn$Jh3 z7V~WB1JFTaIj9XN29C&(tuF^1QGDqqJ5YLyqtT(0mDZ^vwzsiBKk2ZmtXbUiuYP$y zfB9$DEafOO#1j=4<~SVj|Z+zB){jU%K%6I%T1@W)G*zEd+7gx#B_**({Ejqk%SsnIWo(+0JbT$jr5El6f( z3wCnr+#GhBM^*U5^KnTCTrR&q1XPe0GrBeIAx7 zB;6?MzTkHyYHcE6Q<7Lsd_kQ>wY++s=2>m0m}3bGq6cF8Pn?Kd(LoOQJsq}viKR7w zD+wa|&dkV~2^!i0K3&ome++sATGNN2a?xd8Op(lclY;5o-9wZT)$MWz^c{%JLY$yKe8trNW9QWFvn(@J>E6 z{Ny`&Qni13zx0iXI>C)i>YC_Qe;e0mTE)Q%R*z&mYbsH7-N!c{?rHljFx>7JaW~Q0 z?CPsGiT>A1>dNaX+MS}4#+&4}ylRv?Q$4UGbUB**vS!P>I_CP|jzz)JMCB4^JF=6x zba033F`ZB^?aw|**Db2HapH5%hmxkW zK_=I1andJ@QR^J58aKITCw4K`u|Kzfwf@ z&ar{>CX&}*zGz7q<=Z|ul{08&r@tjiR`Y1;#-up!cBlJ>b8PY}I!ERHV!3IhUi&03 zukJnHlDg$A)87Q<~XdxMt!nDAKGK`7PIXs5hj$hG2ahN)jXjblb3;N=S^f zOnpWJj#jpmDvqG$5yrGG<bwv zWyHE4IK67&fo31+yPY3KR!bf>A|$L`d3|;8$NSnZeynWm2kgcezEoG?;PqX^#-lt|q&X0|px#xVp{ppQLi|XEkWd~_i zQa3_(JY8XMwaHd`(cFbAuY0Fd4fc&3@0RyBY&A~Mb*=18(5S@l_54#E2?nrJwY(@> zF}S7UFZz+TYoLf$qemq zqkr6fR6X&;h#X~M=m)>(&7TKcUSFzYKCAcH!C-Lw^>}yG^%8#_c zYVV2x0(ISK1kiEjCy` zs!q8q*K?M*EPzW1BlpzPJuw$U+Z;_^ zPhp=%dcHV~bdJ!vN4a`0D{U=`H3pj2Z1HP@1;4_?rK6j`$tqwi^0GA*c4c zuSvVe6o6_pS{j<^!F=avmFq{1ypcTwcf%8-@@%>UwC*o7;}4H=u?$brgP^gG72ECRIOy|Uo&i2?chuK z5$q4}FHW7$5A@Q)^if{z? z66|))&Fj34%C!d}1v@_4dizPcGi8f3&YjDxxcJa{F8xkXOd2x2_Om%=>;S;#%dZ9d z#?HC|^yuz+bi~Z#Z=BEf=vy6i_&`XhJs2qT?7eU)tDqMSJ&~F zK-*bLZ2H`EzIAE!@F$k@N3V?Rxn6EVr81B&dufmO|8@Yau{!}O8wCs&ezbC6VLoL4 z#eQo=$fXnVF)&CNl6eK~e1pjjDFjN!>GfNXv4CG;s$au(i!7x8~$Ptnn zfUZ?rJ`tNSXS4od%KU@8NC+u-vFM@t^PU94H!7wQiPp6@_Cqsq7B(7$dGy96x?3O87<(Y3D6(-qIUJG~`q~lnarwM(oXUuS^ z%tGGC%XOQ!#=VG=xc}U#Wzbd|?b__B`!}K8N@q-OvxMH)`@kJM5lcKt{T~`@qlc6&*moxGOza zg?8@G%})GMB~}{v#4q`3Rt5Xl&=b7}S-)f|yly=_X*+IS7q872$bxRo0H_OjO!*T& zQucp%Na?=_?)J;wJF~FT!GfAv+S>q(E{H`B@n#19C-MK*fZ}KF49Oq_Y98E@Ul`Ph z?(xsvf6Cd;KCiZDSg{mOSo@^={(Zhu>gE^E6*9K3#)&9A#F>(4s~WOrd&Oj4U^=cr zFIq;7=u)n@Q7cFa>Ej)w5O?m+oPA&T-gRT2tTPAB<(+;Uy+Ltw>(Wo;Lae*STTc=_*$>S@&|EA6qP>1ZC>7mFh&VU^L!^udAl6{ihD59P8e zI9)c8Exu*Q@@Ss5CBJsmf#*`9sKpzo%)cG$_)0?_6sY{~=3k{>W=W`*WKo;)NYdsd}6eEaD z2|yoxClf|K1Z~!he(8kmIda!rp!uDQj8p@HaC1>~D)c2Cxhpnv>_gB_OwKj$AwfoW zVa?72beR|b80X}W!d+jLkC`$-^0ejRG{WSx0Jql)WrDzXmD;Iz)0K9^$^B%iU=|E`WT4wf(pde~_^p}f zPsx-KBcxZFgm$T1!j%skVP$b5q(n_sU_JM!Dsdn>QVk*t2f6yg2Tp@wcVJ zT)g>vi#f8m+snHy_M#F1Z3)t?V6_bS&^Q|HlOeiGIVwz~gt*|0?mW(YCNTn&<-C1X z#lAS&aXNubEK%*FnzG*qJ96H>ByEew0yPdq>lf5fIc|zqKU1_PZOHO(TJ~jSxQkq8 zS5Dk&#H|2wDPJh{xuG5r{cUO@vp;+M%aApD({Mny^rp06%nWldBXK$65s_eD_5hwCfI& z0g0f;28$qFnFC4f0on=@GIEG51{w9*w&j;7)2LoXM!?q5*^X|4cG1|G@_jWBX;HS_H=t9lHBzfY`ePKh$SSrw5W2 z4vf*dRg#CG{d!&@{pZtG#`x+%)KVZj4DjQpa9HhUGcZvMJ9VvS3(>hosOHMQNpHK7PMID^iW$XijJQx}CzEkP)B;&}M2VY9863oj_K?U_|bSooL| zGibYe?{isUeU`XSjAN5d2&3BKt_+dQYS7EdD>Ib|xyy($5 z;al*KKCcv@2OTayvB9GD=@*^_#u6Z$crALqO3w6)ZX;3vTU%|2^7vK#OG5a9|8u*f z>AU6-Iqf={h z^sy>30e*tn;ujMBQX|fd3s%7P7J5rUVaqhaD8Mr&fL|VWILLqS670@9_6QH&-wCZO zH@b|`YWFZ^OYaZZV6T~;ibY5;1}eZ%SE;|D3zmtEOx7E<2@cfJr#5EUf~-h#MJ65# zm|T>tE+Bpqd=0D|nbep(H7V8_HW>!CysS=muz33A_lauW=tZLE^h@A#)Q?Qzo60f1 z$9mHn(sqqrtqDTl-m*S@&hmzDKGN2!Z5L1YJeQS()% z6gc5j0if?2J+P-Aa;>aOS;BDbPIF5g>y!tY=?Ig69uHXz$y750DbxC_3CW?u| zcE=lM?Ib@UqfAV=H}$ofb4_uU1?wNh2%6ssqYDc8y{`!3OcP02eWIs=K`SFjQS=9h zy~yQ}?2vEUlPUbDEaJH3=}MMI#7Opak`WxaK0zE`{UQ44pLwR+3I(Ba1pDWwy8WM# z8bkx}_pr-d>Cimoq{mP`r#wMNtN{s^zxVfat-O+OAOT=5%8F(^Vi>ON3q>t8nD8${*qW zA6+X`qk=aKbco2le3@6%Zh32nRa5NLR!^^?$gSdAb_BNYulq7Eqi@L=n{}K~>bUYd z`o1#|R2LdgQ8Ok_b|G<`c>Y7dUp05e`E=oiCG+tLTu%fx2UPv3QxweBrU3KD+JA{i zb=O4%$dSe9{lM$da6Ka$n8;x_>_O9pFYTic{_cTY`1Wq-8}#%aL>~#sP&QQPME3%T zGqeeQubgg{%EUQR#T$Rh->LP3bH?%Z4`S1GarsQlZEupjiXO?~p?Se)l7dLwyJ z9jwMprG>VF1>4AmQ>B$V0q)~hY6y);=8JuZV>`>I_!UFKQJaW;YoXDj!y%VVB?}6^!GD|XQH@<3~1_@JAIfxCCGUQCY`@hks8C6 zrslHL(M<>e3nP_#@COZ{`jD-)Rib^G6WFO$9Z}Xu!_QgXxp2r`xgxwda8ZYeps8H?v zQyS<0RC$qia&DFKjX2K(DA!>pZhVWbQd6`Pb{YzNaoVmBqd7~W{hce&q@#GSc((J< zuPTtI1^POv;&o3+_s3^HzC59O2iK4DJh2;_8zgu1SeIn+&K^|wt^L_=q-^e!Ops!Lb)69-NVT-+1&d{zz}^FW@7KUU&H^h{`#ji5E7Rw zG(~^3)Bmm&`VUmoXmo_{nj%TkA(1ddLfdl}m*aFuK6Zo@pu#E6Y4|P#+3jn?v;BO=#~_ zxQ*Tf9wOh!YYL+24IB~`ta+jyVuYOJ1yOK(iCj@>T~z@opd+|9&`lu5(zRjbhpGYO z=~iFT*d!~_US*6mdYNFq3;{x z>^RE739m^8eZNTa2X&OD^!s#&v83J>{+;|(v`$yppyU9iSO;97+rB;?TzSe0)srG$ zL@4R7?c=nHwz=3JxC^Ct>q)K3Q3RDB+zxV%txn-Cp-b~(=yssk6zq{;F_#uZAV}-t zw`)g-BjRnnVi6ZS8o0L2p3)$AGD5)vZ?C^|E{QIOR!NmZCCY>?hQ07|9;W`wP;gI8 zoA0UW|1Y-sKNyRDKdfGyRXpN*!UUC*^)7hSZymTJAs+3_AwZ^0v5Ohy@`|94-X2kMLRbv~hIfZr=RY1?5|tsE4z zR4p~Rh3oL{95jUTWl~`_t->zRxyz2#jD|(oPitrJm4wfySxwD#fFW76Q%`zKkKVGcfE%W(;f+%`FmzpWG9S45l&}0{ixwyPXtp zK})eJcVWxA*%rYA-U*=61q~mGKE);>n&?X9M~{a8z=-@<^k(2|v=CNxMRdRvI%19O z8AeqwWBPmDFZ3U<_q1UlpFRck`_+^5I2c4?7vU^%7^nx5eqSBHsD^K+jo`CXCg~JT z-S4Y)h9Q)xvYirZljbpM_%460mjCnh@1qy*9KMDwTghWA;rlTtH`J|&Kf~nFD(?z( z!kR6OrLn`fyWc@ZW1lD3m2c2s@#5i{73X)2!n1P6N5UF!&%zty{7&dpK8A%U02UQL z&r5795UpH>>)0bErcLP9BkU)?SDoj>2=~|gjt@fhI}xt|ViVm!34X4}DRgQ>9uW(} zfZmO3OvJY3!{I8!da$2sN1WTh#!mlGT0diEU<^lk{*1-HjOU-96a0U5EVV~FhFY^r z(CeKSy;-9bNph2{AJ25$x-~~12n;%Pke_!tRNl~gg*hwgmbl%JVfzU#1BLxn9vGIACIX`m+Y~t^82wQt~vu#HAzLQU<;`+oe{&zLB>YW9GQtu z&_60^=q5trDQ7Lsu>nZq4uIC5cFY5NXC-BcKuLr$$^hQ0&vTKb5Oe~AhZ=tYldQy7 z@UA0oChc0sVw0l+i{6ftKpjl`Nw(l8WfxPFp@DUuR~X3iZ$F9lXQckdnFxNb8A6Zr z|IlFlCySPFmGL(9Isz`!jsI2H*T*xt|Nl=CN|uNcTj!{wqEr+zyQohom36NYI~}Lc z4M`%+rlfUil}e>dAJvkyL{YS1v?IqEQHq#t)pEbZ=EAk>{9bdupU3z2J>SRgcm6qN z&35g&-tX7_>-Byq%VPPNJ|1=kmNBB6cSqOJ{tD7=+k@|5 zAudVQumFN%@i&IrAxw>AL5D*)8`#Z6vi)%ss&zr`}7+v{G}cB&XV@s29y@>FC8tYV!HQVDgR?!ZV7? z)h+8vUrdObp53z0(kXYw48tC7v$kPP6Fzx`A~8%R6!BA-9x8Fq@sX97b)6fB&7DY@ zqIxRbhDNowl4ZR->vOpsArtG|CPZ1$8FQKzZ>ejjdU?yH&9RtMRU0PH+qdlnd)?(m z!+t@vL*~~u)A>I?JsTmr&^LdptS4{s>M5kJvi&{1z0FgtZtM;U3d%^fU2vi;;>hbV zl9VhWj}7HCySGE!F;i*rkp$w&DCcb56C^Hjfu>$5L(S1vxbw> zW_Gh}RfF~{>bCs~f2m{7IO707s#vW>b`W&F=x%kWHda+6;;?a>cSr=8?XX!TJwch1 z)w#y_gfM)2R*;VD`=o(C)|Gg|f7a=pLBBa%oO{O7jPQrkh*wJK!S=PhQ}Ho#UvI6o zc5(jg>3N}5&XRub4b~vPO*8EAh!XeuV_w+YtQ&9%1ceO4{I8?I%suu<_ zC3uNiIDg!P;5*>lkOx4%C+;Srhv?YdcvLdtN%HHNt{Q=p_55Z~(Oa*`>IEbcOM*#( zLOlkcyuhbSvV{0p`5vyvo&`*^ew_2Hf;+E~a0&Ypi27LE1XmDD(Z7!nm&uT7X8GbzL7Z;674=t)^p2?x0uER&&zm`7#(p0`lCQSC(1P^McI+taP z&lHk`kn_!4#kysS&D50@a?L4Ik+1Dci%HQO^fH#vg<_NS^sBq6cJb~!47|X}um)Qf zF-MGtUk5NXm9kvSLFv$?UE~E^sthPcDChWC+sGuzry({NQC+#zgwK@P3y0nS!c(1q zj4l$tCYio}Or>H!Stl5uXSh73KMfb!-`o&-ci$14!2?z-)ji+K8=|6qNL^4?(zAYL z{9tj=0?`*cn&`&kg|)z;IWqhAg#ov`@2y~$@0rS27iSV2Q@ior{B6N8{%?je$(HR4 zr|~DM%>z%akAGC-Q2S)+?u|RIv^h*ms@(PMVOqp@-^LtL#P;v6d;fTkEMr#@lf=9p z({>^@lL_+Hf1BUkhhBFfB1#;B%2PS4?;>;%pW$+v+~4I0htJO=Lq6H^xUv{A=0;9J zRF_aS)UA(j{^xffcsV^mZj23AgCHu1_a%0wJ5@E?}1tv{;yj@ z%0$2EvKR)iRa|it9s>vL%j1uNr|OTw7K7qkGsb-AlMv;58c6q4*7xHqSttYQ&e_LR z#`t6wY?Y)Q^vLKhJrb(kx!~JCjTkw7-G$1I3(4z&j(qfBoo zKMHoG#QxCTNujzgsQe||v!d^E`%AZwm2w-~RWXHs&_^4#4Hw>AyXIs#QFgH29NX=hu%e_Ax-?WOjCp zcyv<(o&;~QhHBRv=V9X-1a_ELBNiJcCv#Y4mt7`_Ww8x-br$2$%U=$s$sOXp9#_Ru zvc~Zad{G|31Pcqet)*6KV|KBnZV%p;IiK7t{2<*t{MbUy*N}VYprf**|~L-O%N0_<8cJBxx{S1L{AZl&~IdRkmEKmuAqw7|IY7Y}AU8 zGBWCq9R~gLrI-AD%gLu%l}2hqJP{HIG@AVkGjwqknLpNdZ;diz#GG|Fn$ux(ZEMAL zV!8Ia!ZqNyG{WS?FXjtFY^+d4*$bgrG-*q6-i^|bu!~=Zm{T^_1TT%UxKN{>x_5kx zmUG!WZ(DzWW>9HqZKi= zn|SlLwSqzF9HcI4z^9`D&2OjD72=9S_3N|IcNx(Ra~B-+E6-&4;|q`{=lo>KlQcR9 z8Bf)lZ@YX*c=jYsWs>44u^`de9XgJ>1{uB|KE_owSmB88lT;*mBl)-|tN%upi9{qd z<_z%>Q&^v$L7*Gj%b_WiCl+x`sx2R&8r7}*jpGipQhz?|4brmqAkaB)#-_Z-C!v-y z9=p`(F~&j<-n@Uq{WR&z0g9`-Sx$JFe&*Tjtu4Qb+U3SSy&oO}0@L!tub&^&G@Mh+ z1*)j=s+FrReti}*RiPp`)3;qPd)0JT@GL{2zVc#j&f|Ucm75OLA9|zy#&VA5$jDQp zk!ecLuUlyIt{K|vZg-O$rrU1ezI*z}+??<2?jL{%M2#B;Z!2o%x$N%rlheyCHEB=i z1(R+MtlpA92q6Au!pIhXx0MlShH6HvE(_BOXS$e!$29(fa31H$S!KJbb$2NGuW#P(XvAwsgo(h0L2IOvTjRsD zCs6v#km;PNhzGh75tnpes{O7~xBb{f=7gHBpCbZ8_AD8S=Xka)L}?e8GgO1HX1T}* z*DB}^jD&Tuc2rR230~>0c6;CcGXIz^plHdb=c$;u2cLM*X3{~wVX>~n%#Ky~Cm|ot z)vWceq<7MrGo{TtZh6kryrg9GhXft!YIg*Go}^TO8(OQ#sTX)@#kvj18=)x@N)2#$D`ki3*h9!qQhJ{*dBJJB#A#8X8Q#*Qq*ehfxL0(VuW z@Ihe$S=@AG+qY(%`=IUmFXXWX*S0)=ez9t*(<{}TQ@;FTDFtTK4V9SZO@-ZiOJ~W` z^CGJbQS;_)4C`I&%BF)UO_X*Ncso2?|B%~`^oY43mO>s79EWOq;KK(QXg&6wVdfXQ9KtfOOJx<5H`mbjH z?UxYw81%S4mS$ldB8ZDExExl2N7u+|)DU7^R`Ya-HSYbR(cJ}k*vKL?rS^)7Af}^t zgyAgw(3OAE2evj@cioTP98_kAf2KD(z)#)&$DhK)Xpc;CiarTYoBPBeWL-8sNx+ud zsALUo{UUDRyl3^c^pl6}t-Jnsj*Ph*o`00L)A{HgDXH4SjDQEIT~9CSovUEl-lJon1a3u2FUEEzfwMBLyPSUY`9j&7aoweR(V;&H!zB<+15CLhP^ zwOzywTmCI5xT(koV>P>K&7jBG$dJy$7uk7_u~gbAk{&JNd@8!gi-=yr>x3Lyl2Xb| zK$rK9cTtJThCpx?tvC8C$esor@r>IF06{lw~>0&s~K_a{IRX2-+6tZ`z8c+9lSC5liyDcGwjSRH0t|XUzt}70!NX^O)_{z z#vLu+_3f%9`gT6vHV0rHCS$M(E^YqtMbZ;qaI7AIr&jc88B;ZrF!WhNqPMx?^VvH|Y70(}^*dOTA6E zBi()_9BwpNF!K-*wIy6xKo!4DnrY_O&;ZSJQ$xerg z1z2+3*CeaSOD%mSyn4lHWSyFAzz}^R0>v{y@E5RQmYxSA+`t_Wm)&DX$|2|~`V%5z z?fc<5J+e${(i%8=c)Z~U1}FdOmMr=_&^`(qdtTo#Sz9?mq^SYO?}qx~QYa+hLYUt+ znU>$HKVfw;^DZ#3)qWc2MkK8Mw@EYhNE~@UJ8NG6gWk|(xcxG%4x%?IFa~7gAW4A# z+T}APIA4SChI(L-91c;ovH?<42|dw>>EG>vLYf1Fghx7$Wpb6xFgPwIy<&282?U_#LYD)gfh!jp2Ps4zfEdEdCg5m?a|>1m{>xile1_fR18(c0qMdQ= ziC#DWZO(qvnpjK@0sLP*_G-{$uY=TA@Hl!l0uO~6;ln&H$0q^BDdmY4)lQ z*f_fmOcmN2YF&x+|CYl5l>EHCRHEAWbvn1plgjdrKYju7@ z3P5PBFXBb|HNX}xB?za7sg>$JJnX)Ad|3n%<4zP` zQO&}+;~DNS=~C`KOZ_W#Axb~04ieegR7&h4xK>Q7@V5E&l+_O;idQrdYZAGv=yJL4 zRW`lqL8ocnK5aUA5s*f)Tc^RO*^PF-e!tk|0I<~9+%xlR#2VrYC+MZdGj2K2x$KF zqR`);~Is|uMGdti)7lrWskA)8WvVEES` z);Y>8Q1OF~W@qyFQo}H3qT=>)5k94n`I97PUp;+$P(*}@@h0{3c6N8Q?1rPtJy~Ga zfkXUrl2^_x3OhZ(Icr1mR=?90$XcWyYiM9{q72GqAKZi%+tBGdpPVV3%y8m&CUDu$ zN?fKyIMCh~Za-X7+^eyVH$0^)VVYn|Pfm|g}NbUHHI>Y1zec;+|)r}cq7n5rM?6p(+;m~SC z_Lxc-TG&bv$w^m!Su<^iQ`1GigBzT`Y`Y)+_(oFDQR`{jXU(pdV`Aci`2X;hr;a;^ zlX0;H6>e$<1YKE4aH2arc$K+6>VeP;t52n zeOs9(uzKgpn05tqg+e2GOS9m`&*V*43|~Lve$19_g7_4&RqUv0x4YGS6V@=o(O?;Wd{r*$6|}GKOvq2_DW9LBxvx#{qCZN-ZH6VP!IO-2j_2;OD~7iS za_(0xE*{4$Q32Col3-#N-G=!i{(A!b6mxEPra}GU12I}=6JwKFSkX*mM=p-Kklse9 zD_!&OTY`{t7cIE!c$P88dGPsgK)z!4S(v$5)c?U{;&2#_Vxv?2? zET=G8?L4=<2)CZ)w0BX3zdg&nNR*tQ#SGp2?_gw{jU$izN77VU4gfJN!nrrVfT_XJ zFFKY6eU!a~Gq*Bt5dnXDw9}V0G0Ggxs0^9dtvauB6(%iyBxL z<6pA*SDPJ?dG~4iSfPh3TE}XOELKhV7$-YDgZz-`El_jB{AO}hh?QC=O%;Z(;TSM$ z=o6R@m;-7mr;%WDHoV0W)n4n-2`Xw=rFFudlsoZh>(r6=as+9QQHeaWt}f z?OuV5MT=f9&2MyL*#P|kJM_8wC!yMiUPYenf*p|YO(ZHofBDM+8`6vhY~|Y|#-OY( z1*Q-j(GBhP^Qg8Td8_g)b4Ewoj0P8BSRPRhGCRG~)+MDITX7$y z&W>F}8(V%CeJHLZO?$^|4UG$1!nIC*7%`mk2f({uujOVzAr{ceKT7_3@wk;1DT3ey z0%#|%SjU_W9Qc|Go>O_4z7ye3c+k`Gqze#(w`Ama8v{=I=1W3Xz~CQ~G-p-oh`I3# z(88=*0`odJ$EA_D^G;Spl`2Yf+frej&WrDVjhMJeEwCt*xP<#KzMwttkK2HpLHK5y zUdb7=)AvVIs*P)!|7>4|^-JHl;07#CslP)Bu!Lel-^ z9$tHMevYD#jT7nuH`i00iCEWkp-T|O&LDV(d~KIQC$Yp z=3`KYV9DE3*Q~ZG)j2u85@IVln-Z~0oh{>w?5T<>ZQJ4^q3TM%@4g4$($lGvRHX@f z3KII3T$R-k*}{l(Le6q+fNBf3=QMRDIhC*O+xh|xhDVn%)=F(y@1>1Dxe5d8?08B}Il^diH`1mtJV? zxO`T>o_sij5f89kiR6lPd}f+t#LER zhA35`1_)g0zmr`4)oPw_kaY*&IbutAd!YbyVWIvfc0#tr%m${}p~En5_#83!EB0nd zUcqonzVxw){z6~+0EQxxo!<_>*8v$<)bA{pesP19!GL#BkEi;YRQ42`lU%vfEX0~* z`BG(;aGBwWh6^q3sAg$}(efba*yZZe@yl`R&s1XRyZL<%C9290k9FosztU3G=l3qM znvx51*Ap%LFfmNwu|mSf$lX6DB#uVkFFDfUPH(><-7&a3{}0N1kV23^{X}3(K1lGl zG0o4J*>Fpm&U(bl{)28W3Ch7jqkK9;$AcjYCoIMqWc#dUJvuxvl>3D?VHZWPNlf0 zx}eG1ASc>{_%1HQ=cxBfiKVk-N3l@3gVDS1%k$8O!J!+U;|=dJo4-1~d-l^yf;!6= z<{mG4+8@Oa6I7kY6kWhhU~XKDoX#@(Bzl4A-|kViV1LUBS;LM0p+rowJUU=SNVz_U z3I7N;c;T2+OK^Ld3fYny;VqsFkBR`jM%pLtA4v}Ly<#BwL)VX8i}R>bpcgrFox2NQ z;0p|TS?PM~ES~vUOIAYD;c?Ordn7XlbO>OROmYqTz{)H%2+lxgkiWf$EBcJ@{x}Rt zc+t=>A@GO;n>o{Y-KWfwtkR;!U2665Bc9=YAImd>nC?@x>?oT zEHWGs6u_9OF$Ssig#02!>S(Hn?A7OrLmbs;&V0lj=N2R!v+jy$4I#i`U-}sGf_}TXg`*WCi|G1iG)fh984$$)=c{UgT=<=SMMb3 zsD547^$cK$7$o$sFk&~s^K2I0R{E^3vUk~H;4jaxs|Kz z(g5z?Aiu4m7odWs=O*Wl+_i>#+}gB)34Ifje`Ll-5WgC!h(FeXF#&j?$Nh{-nw zz|cT3!H=n9`*wnHY#A+ecx4KJbDWz(*c6AcX)!p=YtJcRhVdIPHSi_EkWM>TBoH>M zyOE{CY49*zuzZD32kA_VC)4`jg44(i-~hVtMDK57&R>u``K;4_Bag7*s>)P@2eTCo zygWb|`Ur&$1XxlwTpb?4lUSUtT0)qAE9^oRS*iY-H0PQp2)ZPz{rRLu}ohb`CkH?H6gqWOU+bZ8ZD@^9QpDo zfyHeaA_T4MrZvFuAaoHSeH|ga;tT;^wTAGN13^9lHmZqK_J60fV9@zs(Dhbl7rm|@ z?78qSb8ZerBVb#NWhlTycq0`c(B1KakU0>)_*gn=DI>!y4#2rAk2A(bU?IMZ@lgm# z;0xf{eJqWU4SFXr4DYs&B*J!)OzkHUJUn|{Aw~uCm!-p(AXYMvN%lzOM6}MZ2&6tS zTa~U|n~&ZG#r9D{+GEL*k*KyU)wFdrnd7)`8o8u?$8H%_tmpn`ew2GiOV1yX1gBAj7y0{lFdVy zv}5U+^s~`%=Zl$Eay!wC%zvpbmnCjQxjidRX}LSfWcj&+$VbhPr}eB&xbuq_M|EwH zTXIwY0H!iNBh%geNqy!WgXUOHn;ah~x@l!CoGzHmtZm*}yqIr^=K2U4HBq+MuW&FO zbJ_i)D61v7quFkgI1y%PT9(Jg|Mp0DA?!wIb<88hPgkQJt(jBnYL?%mW!AKmj+_5{ z%6sijO(M7O@CzNcFsHh)uha^vLJt<^&rL7< z%i-?=2*s^1_bG{U^YrGxA zti7z{Oai(gMs|*q@-m8}p&NF^watEBCvff}El>q=piREDe&CE-#sQbg5R>-|qO5g1 z(X{LSmuGx*t-uOR?b@lPCtbz7#}z#E?!6Nowcp_T!kABr??70kcp>@9+Chj&tAkTQ zAg)>CtR00QyAX)IEsI6i31Fpq#G`K*SVY6@F&L-ENk2N%HiNB8AUx_x#kl=?(A>wZE15iyeXusIBlvPXVAjT!onDzdFaWoJ#Pxq$t_DmG z=Aq#4YWM`o4IX^X^yi7SgpHSg^0VOs3rs#RVmb^b8pBNkhui(0RK#-B3A^57C|kJV zEPJoR*ik6L{u? z3YcD%Z5)`l3*h*gK79BlZTiL%V?a~>LCSm30S>QYAZx+{g-Y)Q!QwZ&3mtfRUzrHp zw#6K2ey2BaNaqD{W_as3CVQC0{^Da@4_m2#GBx-2G0g~VIRpuCkg3?{-hNermMYY- zl$uV1-Sqsf4`9Nv4 z!S^?B6|K{DjpX@9Em@QJ%X{mps?T?p_>vq<@?Io+F+~+^OQHi!K6{vxY7XIK{)rm= zS@&MWlN(oeZWa`suMB+%NcH{u7tcBLuCnpG=j>&TjaD!2-t%PoRpMp`h1Ox$duRL} zJ*2bSvr7|oV2#n)njBfEW+#bkMEkf*u;olHY45ghe};yakQzzJ-)r$zfr{kun5#Of zLKs?45-FAlWkL(|zO08|tGO_kqRHDA{DiaY%HDlOd4+>Fdp^hIJP6u*xjM@JC4|yv zoEwNsz71sazEz`1GewyMHZFH+Ik&)@Fc5a)~81T}C z@8Z_g=eYjHoq|*;?P=1s<3^tXFWcNG?Jw;X%)*W}0^zW~xa?+_sP7bHilyWz#XP#4 zbWhfI>N$1ZvxaBV>0L_?T;oLZ+g2!uQ_C@`h(f~1x9)nJSCMP~vLT4fN*?Afoc^voqZ>OJJg z)E62qVM%knaJmFuY9vpL{8*AFVB`?~cYOf@*#!55>-n1Q_en?Drp8}(-|z|SCOq4m zRRI1H1#uSGzm4u2xG^}H-UWv<=8*ZqZ-@j^(T!l6-lo*3YVvNAtHKZ(mL5`IO9 zUw(tk*`h=Xw)z>Q){uKQePVGmus|QA9?DN-UKGSYdC<9J(JK@5!CsS_Fg1?3(h&X=@xNlGR}h0>6zxhBtIHD zEy_fPAz^x-Wqiuv4*u^XJG()G-as}d*?%=}hkaLN8MO(7ce0H7T`|9YhoQPLaIJm7 zq^UP_210Vxa2oc&prok@d}s>cCz#a%3dwOdh(9AFn1`EG0G~?-KMcOMvChyeQ=iuo z@mGYE5r5RD`d9Mg78MKRF8&lGuVkUdVkyB8{PGRNrpfxY-_%d^n>uWy< z{vQyk?M;Dda-ri`7)KsYxU6R()ze18{|p{O8msrp|61+6I%{l}$)}44%{}n@Ne=Mx zmVy!r3bB-sbUEQBA*H%{nxa8vWng*Pm{bgI+Mu0$C(C5&ElcUxI-_6W9R;RYCO}V` zb|OZ?R{-gqTH^tq+9H{8_;J>~Xx5+0GpME1Wwvt=8Hxw?lO>jZ@B1 jK)u7;`@0@EcG_D@t_b0{8LV6u;41woY0i4%KR^B-?vj)T literal 0 HcmV?d00001 diff --git a/screenshots/performance2.jpg b/screenshots/performance2.jpg new file mode 100644 index 0000000000000000000000000000000000000000..6ca797abba43393ab3e732a6b18f390198872091 GIT binary patch literal 52686 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 Date: Fri, 14 Nov 2014 12:11:54 -0500 Subject: [PATCH 11/14] Update README.md --- README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 36f3eab..02305d1 100644 --- a/README.md +++ b/README.md @@ -5,6 +5,7 @@ Fall 2014 ------------------------------------------------------------------------------- [Youtube](https://www.youtube.com/watch?v=ggUH_oqFYuo&feature=youtu.be) + [Live Demo]() ------------------------------------------------------------------------------- @@ -12,6 +13,7 @@ INTRODUCTION: ------------------------------------------------------------------------------- In this project, i write GLSL and OpenGL code to perform various tasks in a deferred lighting pipeline such as creating and writing to a G-Buffer. This project requires a graphic card support for deferred shader pipeline. + ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/diffuseSpec.jpg) ------------------------------------------------------------------------------- @@ -61,39 +63,41 @@ Blinn-Phong: ------------------------------------------------------------------------------- The diffuse and specular shader is implemented in lighting passes and accumulates stage that writes the result to P-buffer. + ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/diffuseSpec.jpg) ------------------------------------------------------------------------------- Bloom ------------------------------------------------------------------------------- Bloom is a post processing effects. Normally, Bloom effects is implemented with a texture that specify the glow source and then blur the glow source. But here I just treat the whole object as a glow source. I use a gaussian convolution on color from G-buffer. + ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/bloom.jpg) + ------------------------------------------------------------------------------- "Toon" Shading (with basic silhouetting) ------------------------------------------------------------------------------- Toon shading is a non-photorealistic rendering technique that is used to achieve a cartoonish or hand-drawn appearance of three-dimensional models. To make is cartoonish we don't want many color in the final rendering so I round the colors in the scene to a certain color set. Basic silhouetting is achieved by compare the depth of the object with the background to get the edge. + ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/toon.jpg) + ------------------------------------------------------------------------------- Screen Space Ambient Occlusion ------------------------------------------------------------------------------- Ambient occlusion is an approximation of the amount by which a point on a surface is occluded by the surrounding geometry. To achieve this I sample a random position within a hemisphere, oriented along the surface normal at that pixel. Then project the sample position into screen space to get its depth on depth buffer. If the depth buffer value is smaller than sample position's depth, then occlusion accumulates. + ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/SSAO.jpg) + ------------------------------------------------------------------------------- PERFORMANCE EVALUATION ------------------------------------------------------------------------------- -The performance evaluation is where you will investigate how to make your -program more efficient using the skills you've learned in class. You must have -performed at least one experiment on your code to investigate the positive or -negative effects on performance. -We encourage you to get creative with your tweaks. Consider places in your code -that could be considered bottlenecks and try to improve them. +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/performance1.jpg) -Each student should provide no more than a one page summary of their -optimizations along with tables and or graphs to visually explain any -performance differences. +In diagnostic mode(show normal, position, etc) I just output the color read from G buffer without light accumulation or post processing. From the chart above we can see the stage 2 and 3 of deferred shading is quite computational intense. And I think the performance is not that good because I implement the deferred shader with simple one-pass pipline. So every part get computed no matter is is used or not. I think Implementing separable convolution will definetely help improving the performance. +![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/performance2.jpg) +Apparently use more sample kernels to compute SSAO will slow down the computation process, but the result is not as obvious as I expect. I want to test with more kernel computed but my laptop can't handle it when the kernel size exceed 90. --- Reference From 1efe8565f1fe4ee406db9af4f293a2cee13a23b6 Mon Sep 17 00:00:00 2001 From: XJMa Date: Fri, 14 Nov 2014 12:14:05 -0500 Subject: [PATCH 12/14] Update README.md --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 02305d1..a025589 100644 --- a/README.md +++ b/README.md @@ -97,6 +97,7 @@ PERFORMANCE EVALUATION In diagnostic mode(show normal, position, etc) I just output the color read from G buffer without light accumulation or post processing. From the chart above we can see the stage 2 and 3 of deferred shading is quite computational intense. And I think the performance is not that good because I implement the deferred shader with simple one-pass pipline. So every part get computed no matter is is used or not. I think Implementing separable convolution will definetely help improving the performance. ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/performance2.jpg) + Apparently use more sample kernels to compute SSAO will slow down the computation process, but the result is not as obvious as I expect. I want to test with more kernel computed but my laptop can't handle it when the kernel size exceed 90. --- From 76ca32670c0bc2a7386040a115b69ae64e92d5a2 Mon Sep 17 00:00:00 2001 From: XJMa Date: Sat, 15 Nov 2014 10:53:44 -0500 Subject: [PATCH 13/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index a025589..f0dd76e 100644 --- a/README.md +++ b/README.md @@ -94,7 +94,7 @@ PERFORMANCE EVALUATION ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/performance1.jpg) -In diagnostic mode(show normal, position, etc) I just output the color read from G buffer without light accumulation or post processing. From the chart above we can see the stage 2 and 3 of deferred shading is quite computational intense. And I think the performance is not that good because I implement the deferred shader with simple one-pass pipline. So every part get computed no matter is is used or not. I think Implementing separable convolution will definetely help improving the performance. +In diagnostic mode(show normal, position, etc) I just output the color read from G buffer without light accumulation or post processing. From the chart above we can see the stage 2 and 3 of deferred shading is quite computational intense. And I think the performance is not that good because I implement the deferred shader with simple one-pass pipline and my browser does not support drawbuffer. So every part get computed no matter is is used or not. I think Implementing separable convolution will definetely help improving the performance. ![blinn](https://raw.githubusercontent.com/XJMa/Project6-DeferredShader/master/screenshots/performance2.jpg) From 803ebdf7a143f7eac066e96f4011f2b6e36e46f7 Mon Sep 17 00:00:00 2001 From: XJMa Date: Sat, 15 Nov 2014 11:02:22 -0500 Subject: [PATCH 14/14] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index f0dd76e..c41c1c4 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Fall 2014 [Youtube](https://www.youtube.com/watch?v=ggUH_oqFYuo&feature=youtu.be) -[Live Demo]() +[Live Demo](http://xjma.github.io/Project6-DeferredShader/) ------------------------------------------------------------------------------- INTRODUCTION: