diff --git a/README.md b/README.md index 25002db..4f083c1 100644 --- a/README.md +++ b/README.md @@ -3,27 +3,85 @@ WebGL Deferred Shading **University of Pennsylvania, CIS 565: GPU Programming and Architecture, Project 5** -* (TODO) YOUR NAME HERE -* Tested on: (TODO) **Google Chrome 222.2** on - Windows 22, i7-2222 @ 2.22GHz 22GB, GTX 222 222MB (Moore 2222 Lab) +* (TODO) Rony Edde +* Tested on: **Google Chrome 222.2** on + Windows 10, i7-6700k @ 4.00GHz 64GB, GTX 980M 8GB (Personal Laptop) ### Live Online - -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +[Live Demo](https://reddeupenn.github.io/Project5-WebGL-Deferred-Shading-with-glTF/) ### Demo Video/GIF +![fov1](./img/layers.gif) -[![](img/video.png)](TODO) - -### (TODO: Your README) - -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. - -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! - +### Description +This is a WebGL deferred renderer. + * Passes are rendered to the frame buffer, then composited in a deferred shader. + The deferred shaders shade and light the resulting fragments by their attributes. + Normals, normal maps, positions, texture and depth are precomputed and the deferred + pass computes the lighting and shading of the final image. + + * Pass layout. + * Z-depth. + * This is the depth pass that computes the distance to camera. This is useful + for multiple post effects such as depth of field. + ![zdepth](./img/depth.png) + * Position. + * This is a position pass where every fragment stores the xyz positions of the rendered geometry. + ![pos](./img/position.png) + * Geometry Normal + * This is a pass for the geometric normals coming straight from the mesh geometry + ![gnorm](./img/geometry_normal.png) + * Color Map + * This is the texture and color of the geometry again with no shading. Only the texture color is applied. + ![colormap](./img/colormap.png) + * Normal Map + * This is similar to the texture pass but it contains the normal map texture for compting fake surface detail shading. + ![normalmap](./img/normalamap.png) + * Surface Normal + * This is a final calculation of the geometric normal with the normal map applied to it. + ![surfacenormal](./img/surface_normal.png) + + * Final result with all passes combined + * After combining all the layers and computing the blinn-phong lighting model, here's the final result: + ![blinn_phong](./img/blinn_phong.png) + + * Final result with a toon shader + * Using the blinn-phong shader, we can extend it by computing the dot product of the surface normal and camera and accentuate the shading. There are 3 color intensities chosen. Black for perpendicular vectors, half color for facing geometry with less than 0.5 value and full color for less than 0.25. + ![toon](./img/toon.png) + * Post processing + * Bloom: + By blurring the final rendered image and compositing it on top with a gl blend function, we can add a bloom post process effect. + There are 2 modes in the bloom shader implemented. Square blur which is the default and gaussian blur which is computed for every pixel. + Here's the result: + ![gaussian_bloom](./img/gaussian_bloom.png) + + * Motion blur + By sampling the camera motion and computing the difference transformation matrix, we can gereate motion vectors and use them to generate blur in that direction. Shader version 100 doesn't support matrix operations so we're forced to compute the difference beforehand which is not a bad idea since the shader will have to do less work over an unvarying variable, however the THREE.js precision isn't as good as expected so we get a few artifacts when the camera is slowing down to a complete stop. Still the results are not too bad: + ![motion_blur](./img/motion_blur.png) + + + * Optimizations: + Enabling scissor tests dramatically improves performance. Rendering only the square that the fragments overlap the sphere radius. + The initial mode with scissor test is shown here: + ![scissor_test](./img/scissor_test.png) + + We can even go further by rendering a sphere as an instance for the scissor test: + ![instance_sphere](./img/instance_sphere.png) + + + * Performance analysis with scissor tests: + * Running the scissor test on 20 lights has almost no impact on performance. It's only when increase the number of lights to about 100 and more that we truly see the benefit of the scissor test. Here are the results: + ![benchmarks](./img/benchmarks.png) + + We can clearly see an improvement inperformance. The time per frame is halved. This is particularly noticeable when using gaussian bloom since the gaussian computation is heavy. + + + THANK YOU + + + + ### Credits * [Three.js](https://github.com/mrdoob/three.js) by [@mrdoob](https://github.com/mrdoob) and contributors diff --git a/glsl/copy.frag.glsl b/glsl/copy.frag.glsl index 823ebcd..edb26b0 100644 --- a/glsl/copy.frag.glsl +++ b/glsl/copy.frag.glsl @@ -10,11 +10,15 @@ varying vec3 v_position; varying vec3 v_normal; varying vec2 v_uv; + void main() { // TODO: copy values into gl_FragData[0], [1], etc. // You can use the GLSL texture2D function to access the textures using // the UV in v_uv. // this gives you the idea - // gl_FragData[0] = vec4( v_position, 1.0 ); + gl_FragData[0] = vec4(v_position, 1.0); + gl_FragData[1] = vec4(v_normal, 1.0); + gl_FragData[2] = vec4(texture2D(u_colmap, v_uv, 0.01)); + gl_FragData[3] = vec4(texture2D(u_normap, v_uv, 0.01)); } diff --git a/glsl/copy.vert.glsl b/glsl/copy.vert.glsl index ec14e69..aebf891 100644 --- a/glsl/copy.vert.glsl +++ b/glsl/copy.vert.glsl @@ -18,4 +18,5 @@ void main() { v_position = a_position; v_normal = a_normal; v_uv = a_uv; + } diff --git a/glsl/deferred/ambient.frag.glsl b/glsl/deferred/ambient.frag.glsl index 1fd4647..24025d4 100644 --- a/glsl/deferred/ambient.frag.glsl +++ b/glsl/deferred/ambient.frag.glsl @@ -24,4 +24,7 @@ void main() { } gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + + //gl_FragColor = gb3; + } diff --git a/glsl/deferred/blinnphong-pointlight.frag.glsl b/glsl/deferred/blinnphong-pointlight.frag.glsl index b24a54a..41353f3 100644 --- a/glsl/deferred/blinnphong-pointlight.frag.glsl +++ b/glsl/deferred/blinnphong-pointlight.frag.glsl @@ -9,9 +9,12 @@ uniform vec3 u_lightPos; uniform float u_lightRad; uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; +uniform vec3 u_cameraPos; varying vec2 v_uv; + + vec3 applyNormalMap(vec3 geomnor, vec3 normap) { normap = normap * 2.0 - 1.0; vec3 up = normalize(vec3(0.001, 1, 0.001)); @@ -21,11 +24,21 @@ vec3 applyNormalMap(vec3 geomnor, vec3 normap) { } void main() { - vec4 gb0 = texture2D(u_gbufs[0], v_uv); - vec4 gb1 = texture2D(u_gbufs[1], v_uv); - vec4 gb2 = texture2D(u_gbufs[2], v_uv); - vec4 gb3 = texture2D(u_gbufs[3], v_uv); - float depth = texture2D(u_depth, v_uv).x; + vec2 uv = vec2(gl_FragCoord.x / 800.0, gl_FragCoord.y / 600.0); //hard coded screen size + vec4 gb0 = texture2D(u_gbufs[0], uv); + vec4 gb1 = texture2D(u_gbufs[1], uv); + vec4 gb2 = texture2D(u_gbufs[2], uv); + vec4 gb3 = texture2D(u_gbufs[3], uv); + float depth = texture2D(u_depth, uv).x; + + + //vec3 pos = gb0.xyz; // World-space position + //vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + //vec3 colmap = gb2.rgb; // The color map - unlit "albedo" (surface color) + //vec3 normap = gb3.xyz; // The raw normal map (normals relative to the surface they're on) + //vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) + + // TODO: Extract needed properties from the g-buffers into local variables // If nothing was rendered to this pixel, set alpha to 0 so that the @@ -35,5 +48,50 @@ void main() { return; } - gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + //gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + + vec3 N = applyNormalMap(gb1.xyz, gb3.xyz); + //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + + float shininess = 10.0; + + // assign variables + vec3 vertPos = vec3(gb0); + //vec3 ambientColor = vec3(0.1, 0.1, 0.1); + vec3 diffuseColor = vec3(gb2); + //vec3 diffuseColor = vec3(1.0, 1.0, 1.0); + vec3 specColor = vec3(0.5, 0.5, 0.5); + + vec3 normal = normalize(N); + vec3 lightDir = normalize(u_lightPos - vertPos); + + float attenuation = max(0.0, 1.0 - length(u_lightPos - vertPos) / u_lightRad); + + float lambertian = max(dot(lightDir,normal), 0.0); + float specular = 0.1; + + if(lambertian > 0.0) + { + + vec3 viewDir = -normalize(vertPos - u_cameraPos); + + // blinn phong + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = min(pow(specAngle, shininess), 1.0); + + vec3 reflectDir = reflect(-lightDir, normal); + specAngle = max(dot(reflectDir, viewDir), 0.0); + + specular = min(pow(specAngle, shininess/4.0), 1.0); + } + vec3 color = u_lightCol * (lambertian * diffuseColor + + specular * specColor); + + gl_FragColor = vec4(color, 1.0) * attenuation; + //gl_FragColor = vec4(u_cameraPos, 1.0); + + + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + } diff --git a/glsl/deferred/blinnphong-pointlightSphere.frag.glsl b/glsl/deferred/blinnphong-pointlightSphere.frag.glsl new file mode 100644 index 0000000..41353f3 --- /dev/null +++ b/glsl/deferred/blinnphong-pointlightSphere.frag.glsl @@ -0,0 +1,97 @@ +#version 100 +precision highp float; +precision highp int; + +#define NUM_GBUFFERS 4 + +uniform vec3 u_lightCol; +uniform vec3 u_lightPos; +uniform float u_lightRad; +uniform sampler2D u_gbufs[NUM_GBUFFERS]; +uniform sampler2D u_depth; +uniform vec3 u_cameraPos; + +varying vec2 v_uv; + + + +vec3 applyNormalMap(vec3 geomnor, vec3 normap) { + normap = normap * 2.0 - 1.0; + vec3 up = normalize(vec3(0.001, 1, 0.001)); + vec3 surftan = normalize(cross(geomnor, up)); + vec3 surfbinor = cross(geomnor, surftan); + return normap.y * surftan + normap.x * surfbinor + normap.z * geomnor; +} + +void main() { + vec2 uv = vec2(gl_FragCoord.x / 800.0, gl_FragCoord.y / 600.0); //hard coded screen size + vec4 gb0 = texture2D(u_gbufs[0], uv); + vec4 gb1 = texture2D(u_gbufs[1], uv); + vec4 gb2 = texture2D(u_gbufs[2], uv); + vec4 gb3 = texture2D(u_gbufs[3], uv); + float depth = texture2D(u_depth, uv).x; + + + //vec3 pos = gb0.xyz; // World-space position + //vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + //vec3 colmap = gb2.rgb; // The color map - unlit "albedo" (surface color) + //vec3 normap = gb3.xyz; // The raw normal map (normals relative to the surface they're on) + //vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) + + + // TODO: Extract needed properties from the g-buffers into local variables + + // If nothing was rendered to this pixel, set alpha to 0 so that the + // postprocessing step can render the sky color. + if (depth == 1.0) { + gl_FragColor = vec4(0, 0, 0, 0); + return; + } + + //gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + + vec3 N = applyNormalMap(gb1.xyz, gb3.xyz); + //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + + float shininess = 10.0; + + // assign variables + vec3 vertPos = vec3(gb0); + //vec3 ambientColor = vec3(0.1, 0.1, 0.1); + vec3 diffuseColor = vec3(gb2); + //vec3 diffuseColor = vec3(1.0, 1.0, 1.0); + vec3 specColor = vec3(0.5, 0.5, 0.5); + + vec3 normal = normalize(N); + vec3 lightDir = normalize(u_lightPos - vertPos); + + float attenuation = max(0.0, 1.0 - length(u_lightPos - vertPos) / u_lightRad); + + float lambertian = max(dot(lightDir,normal), 0.0); + float specular = 0.1; + + if(lambertian > 0.0) + { + + vec3 viewDir = -normalize(vertPos - u_cameraPos); + + // blinn phong + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = min(pow(specAngle, shininess), 1.0); + + vec3 reflectDir = reflect(-lightDir, normal); + specAngle = max(dot(reflectDir, viewDir), 0.0); + + specular = min(pow(specAngle, shininess/4.0), 1.0); + } + vec3 color = u_lightCol * (lambertian * diffuseColor + + specular * specColor); + + gl_FragColor = vec4(color, 1.0) * attenuation; + //gl_FragColor = vec4(u_cameraPos, 1.0); + + + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + +} diff --git a/glsl/deferred/debug.frag.glsl b/glsl/deferred/debug.frag.glsl index 007466f..d84f9df 100644 --- a/glsl/deferred/debug.frag.glsl +++ b/glsl/deferred/debug.frag.glsl @@ -38,15 +38,15 @@ void main() { if (u_debug == 0) { gl_FragColor = vec4(vec3(depth), 1.0); } else if (u_debug == 1) { - // gl_FragColor = vec4(abs(pos) * 0.1, 1.0); + gl_FragColor = vec4(abs(pos) * 0.1, 1.0); } else if (u_debug == 2) { - // gl_FragColor = vec4(abs(geomnor), 1.0); + gl_FragColor = vec4(abs(geomnor), 1.0); } else if (u_debug == 3) { - // gl_FragColor = vec4(colmap, 1.0); + gl_FragColor = vec4(colmap, 1.0); } else if (u_debug == 4) { - // gl_FragColor = vec4(normap, 1.0); + gl_FragColor = vec4(normap, 1.0); } else if (u_debug == 5) { - // gl_FragColor = vec4(abs(nor), 1.0); + gl_FragColor = vec4(abs(nor), 1.0); } else { gl_FragColor = vec4(1, 0, 1, 1); } diff --git a/glsl/deferred/toon.frag.glsl b/glsl/deferred/toon.frag.glsl new file mode 100644 index 0000000..463fd1c --- /dev/null +++ b/glsl/deferred/toon.frag.glsl @@ -0,0 +1,100 @@ +#version 100 +precision highp float; +precision highp int; + +#define NUM_GBUFFERS 4 + +uniform vec3 u_lightCol; +uniform vec3 u_lightPos; +uniform float u_lightRad; +uniform sampler2D u_gbufs[NUM_GBUFFERS]; +uniform sampler2D u_depth; +uniform vec3 u_cameraPos; + +varying vec2 v_uv; + + + +vec3 applyNormalMap(vec3 geomnor, vec3 normap) { + normap = normap * 2.0 - 1.0; + vec3 up = normalize(vec3(0.001, 1, 0.001)); + vec3 surftan = normalize(cross(geomnor, up)); + vec3 surfbinor = cross(geomnor, surftan); + return normap.y * surftan + normap.x * surfbinor + normap.z * geomnor; +} + +void main() { + vec4 gb0 = texture2D(u_gbufs[0], v_uv); + vec4 gb1 = texture2D(u_gbufs[1], v_uv); + vec4 gb2 = texture2D(u_gbufs[2], v_uv); + vec4 gb3 = texture2D(u_gbufs[3], v_uv); + float depth = texture2D(u_depth, v_uv).x; + + + //vec3 pos = gb0.xyz; // World-space position + //vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + //vec3 colmap = gb2.rgb; // The color map - unlit "albedo" (surface color) + //vec3 normap = gb3.xyz; // The raw normal map (normals relative to the surface they're on) + //vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) + + + // TODO: Extract needed properties from the g-buffers into local variables + + // If nothing was rendered to this pixel, set alpha to 0 so that the + // postprocessing step can render the sky color. + if (depth == 1.0) { + gl_FragColor = vec4(0, 0, 0, 0); + return; + } + + //gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + + vec3 N = applyNormalMap(gb1.xyz, gb3.xyz); + //gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); + + float shininess = 20.0; + + // assign variables + vec3 vertPos = vec3(gb0); + //vec3 ambientColor = vec3(0.1, 0.1, 0.1); + vec3 diffuseColor = vec3(gb2); + //vec3 diffuseColor = vec3(1.0, 1.0, 1.0); + vec3 specColor = vec3(0.5, 0.5, 0.5); + + vec3 normal = normalize(N); + vec3 lightDir = normalize(u_lightPos - vertPos); + + float attenuation = max(0.0, 1.0 - length(u_lightPos - vertPos) / u_lightRad); + + float lambertian = max(dot(lightDir,normal), 0.0); + float specular = 0.1; + + + vec3 viewDir = -normalize(vertPos - u_cameraPos); + //vec3 viewDir = -vertPos; + float edge = dot(viewDir, N); + float toon = 0.0; + if (edge > 0.5) + toon = 1.0; + else if (edge > 0.25) + toon = 0.5; + + if(lambertian > 0.0) + { + // blinn phong + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, normal), 0.0); + specular = pow(specAngle, shininess); + + vec3 reflectDir = reflect(-lightDir, normal); + specAngle = max(dot(reflectDir, viewDir), 0.0); + + specular = pow(specAngle, shininess/4.0); + } + vec3 color = u_lightCol * (lambertian * diffuseColor + + specular * specColor); + + gl_FragColor = vec4(color, 1.0) * attenuation * toon; + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + +} diff --git a/glsl/post/bloom.frag.glsl b/glsl/post/bloom.frag.glsl new file mode 100644 index 0000000..4ff5b53 --- /dev/null +++ b/glsl/post/bloom.frag.glsl @@ -0,0 +1,106 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; + +varying vec2 v_uv; +/* +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + vec4 color = texture2D(u_color, v_uv); + + if (color.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } + + gl_FragColor = color; + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} +*/ + + +uniform sampler2D u_texture; +uniform float resolution; +uniform float radius; +uniform vec2 dir; +uniform int u_gauss; + +int i = 0; +int j = 0; +void main() { + + // matlab's 3x3 gaussian + //mat3 G = mat3(0.0113, 0.0838, 0.0113, + // 0.0838, 0.6193, 0.0838, + // 0.0113, 0.0838, 0.0113); + + vec2 coord = v_uv; + float pixelwidth = 1.0/800.0; + float size = 5.0*pixelwidth; + + + vec4 accum = texture2D(u_texture, vec2(coord[0], coord[1])); + + // gaussian blur + if(u_gauss == 1) + { + float sigma_x = 1.0; + float sigma_y = 1.0; + + for (int i=-15; i<15; i++) + { + for (int j=-15; j<15; j++) + { + float X = float(i)/15.0;//*size; + float Y = float(j)/15.0;//*size; + + float theta = atan(X / Y); + float a = pow(cos(theta),2.0)/(2.0) + pow(sin(theta),2.0)/(2.0); + float b = -sin(2.0*theta)/(4.0) + sin(2.0*theta)/(4.0); + float c = pow(sin(theta),2.0)/(2.0) + pow(cos(theta),2.0)/(2.0); + + // compute gaussian height + float amp = 1.0*exp( -(a*pow((X), 2.0) - 2.0*b*(X)*(Y) + c*pow((Y), 2.0))) ; + + accum += texture2D(u_texture, + vec2(coord[0] + float(i)*size, + coord[1] + float(j)*size) + )*amp; + } + } + + vec4 color = min(accum/900.0, 1.0); + float ampl = pow(length(accum.rgb), 2.0); + gl_FragColor = vec4(color.rgb, 1.0); + } + + // ENABLE STANDARD BLUR AND DISABLE GAUSSIAN + else + { + accum = vec4(0.0, 0.0, 0.0, 1.0); + { + for (int i=-15; i< 15; i++) + { + for (int j=-15; j<15; j++) + { + accum += texture2D(u_texture, vec2(coord[0] + float(i)*size, coord[1] + float(j)*size)); + } + } + } + accum /= 900.0; + vec4 color = min(accum, 1.0); + //float ampl = pow(length(accum.rgb), 2.0)*0.1; + + + gl_FragColor = vec4(color.rgb, 1.0); + } + + + //else + //{ + // gl_FragColor = vec4(1.0, 0.0, 0.0, 0.0); + //} +} diff --git a/glsl/post/moblur.frag.glsl b/glsl/post/moblur.frag.glsl new file mode 100644 index 0000000..9db3765 --- /dev/null +++ b/glsl/post/moblur.frag.glsl @@ -0,0 +1,89 @@ +#version 100 +precision highp float; +precision highp int; + +#define NUM_GBUFFERS 4 + +uniform sampler2D u_color; + +varying vec2 v_uv; +/* +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + vec4 color = texture2D(u_color, v_uv); + + if (color.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } + + gl_FragColor = color; + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); +} +*/ + + +uniform sampler2D u_texture; +uniform float resolution; +uniform float radius; +uniform vec2 dir; +uniform int u_gauss; + +uniform mat4 u_matdiff; +uniform mat4 u_cameraMat; +uniform mat4 u_cameraInverse; +uniform mat4 u_lastCamMat; + +uniform int u_debug; +uniform sampler2D u_gbufs; +uniform sampler2D u_depth; + + + +int i = 0; +int j = 0; +void main() { + + // matlab's 3x3 gaussian + //mat3 G = mat3(0.0113, 0.0838, 0.0113, + // 0.0838, 0.6193, 0.0838, + // 0.0113, 0.0838, 0.0113); + + vec2 coord = v_uv; + + + vec4 gb0 = u_lastCamMat*u_cameraInverse*texture2D(u_gbufs, coord); + + + float size = 1.0; + //vec4 gb0 = texture2D(u_gbufs, coord); + vec4 vel = 10.0*((u_matdiff*gb0)-gb0); //vec4(0.0, 0.0, 0.0, 0.0); + //vec4 vel = 10.0*u_matdiff*vec4(0.0, 0.0, 0.0, 1.0); + + + vec4 accum = texture2D(u_texture, vec2(coord[0], coord[1])); + + + + float amp = 1.0; + for (int i=-30; i<30; i++) + { + float delta = float(i); + accum += texture2D(u_texture, vec2(coord[0] + delta*size*vel[0], coord[1] + delta*size*vel[1]))*amp; + } + + vec4 color = accum/60.0; + //float ampl = pow(length(accum.rgb), 2.0); + gl_FragColor = vec4(color.rgb, 1.0); + //gl_FragColor = gb0; + + //gl_FragColor = texture2D(u_texture, vec2(coord[0], coord[1]));; + //gl_FragColor = texture2D(u_gbufs, coord); + + + //else + //{ + // gl_FragColor = gl_FragColor * 1.0001; + //} +} diff --git a/glsl/post/one.frag.glsl b/glsl/post/one.frag.glsl index 94191cd..94518e1 100644 --- a/glsl/post/one.frag.glsl +++ b/glsl/post/one.frag.glsl @@ -17,4 +17,5 @@ void main() { } gl_FragColor = color; + // gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); } diff --git a/glsl/red.frag.glsl b/glsl/red.frag.glsl index f8ef1ec..e0dd4a1 100644 --- a/glsl/red.frag.glsl +++ b/glsl/red.frag.glsl @@ -3,5 +3,5 @@ precision highp float; precision highp int; void main() { - gl_FragColor = vec4(1, 0, 0, 1); + gl_FragColor = vec4(1, 0, 0, 0.01); } diff --git a/glsl/sphere.frag.glsl b/glsl/sphere.frag.glsl new file mode 100644 index 0000000..49785ae --- /dev/null +++ b/glsl/sphere.frag.glsl @@ -0,0 +1,11 @@ +#version 100 +precision highp float; +precision highp int; + +uniform mat4 u_cameraMat; + + +void main() { + + gl_FragColor = vec4(1.0, 0.0, 0.0, 0.1); +} diff --git a/glsl/sphere.vert.glsl b/glsl/sphere.vert.glsl new file mode 100644 index 0000000..2c60ea3 --- /dev/null +++ b/glsl/sphere.vert.glsl @@ -0,0 +1,20 @@ +#version 100 +#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; + +uniform mat4 u_cameraMat; + +attribute vec3 a_position; +attribute vec3 a_normal; +attribute vec2 a_uv; + +uniform vec3 u_offsetpos; +uniform float u_rad; + +void main() { + vec3 position = a_position*u_rad + u_offsetpos; + gl_Position = u_cameraMat * vec4(position, 1.0); + //gl_Position = vec4(position, 1.0); + +} diff --git a/img/benchmarks.png b/img/benchmarks.png new file mode 100644 index 0000000..272b651 Binary files /dev/null and b/img/benchmarks.png differ diff --git a/img/blinn_phong.png b/img/blinn_phong.png new file mode 100644 index 0000000..dde7085 Binary files /dev/null and b/img/blinn_phong.png differ diff --git a/img/colormap.png b/img/colormap.png new file mode 100644 index 0000000..f6aff55 Binary files /dev/null and b/img/colormap.png differ diff --git a/img/depth.png b/img/depth.png new file mode 100644 index 0000000..3f82764 Binary files /dev/null and b/img/depth.png differ diff --git a/img/gaussian_bloom.png b/img/gaussian_bloom.png new file mode 100644 index 0000000..fedc197 Binary files /dev/null and b/img/gaussian_bloom.png differ diff --git a/img/geometry_normal.png b/img/geometry_normal.png new file mode 100644 index 0000000..5c68f0b Binary files /dev/null and b/img/geometry_normal.png differ diff --git a/img/instance_sphere.png b/img/instance_sphere.png new file mode 100644 index 0000000..76178d6 Binary files /dev/null and b/img/instance_sphere.png differ diff --git a/img/layers.gif b/img/layers.gif new file mode 100644 index 0000000..1f02511 Binary files /dev/null and b/img/layers.gif differ diff --git a/img/motion_blur.png b/img/motion_blur.png new file mode 100644 index 0000000..335e91d Binary files /dev/null and b/img/motion_blur.png differ diff --git a/img/normal_map.png b/img/normal_map.png new file mode 100644 index 0000000..e167eac Binary files /dev/null and b/img/normal_map.png differ diff --git a/img/normalmap.png b/img/normalmap.png new file mode 100644 index 0000000..77557a1 Binary files /dev/null and b/img/normalmap.png differ diff --git a/img/perf.py b/img/perf.py new file mode 100644 index 0000000..6b06d23 --- /dev/null +++ b/img/perf.py @@ -0,0 +1,63 @@ +#!/usr/bin/env python +# a bar plot with errorbars +import numpy as np +import matplotlib.pyplot as plt + +''' +200 lights stats + +default : 44ms (49+33+32+98+16+82+16+32+34+49)/10.0 +scissor enabled : 16ms (15+16+17+15+16+17+16+15+16+17)/10.0 + +bloom enabled : 48ms (34+88+27+49+74+24+50+33+50+49)/10.0 +with scissor : 19ms (17+32+17+15+15+33+15+16+17+16)/10.0 + +gaussian bloom : 63ms (83+99+16+65+100+15+34+49+66+100)/10.0 +with scissor : 22ms (34+16+15+32+17+32+17+16+34+15)/10.0 + +toon shading : 41ms (50+16+50+32+50+48+32+50+16+66)/10.0 +with scissor : 16ms (16+17+14+17+16+14+17+16+15+17)/10.0 + +motion blur : 47ms (64+84+16+33+48+33+49+50+32+65)/10.0 +with scissor : 15ms (13+15+16+16+15+16+17+16+17+14)/10.0 + + + + +''' + + +N = 5 +defaultMeans = (44, 48, 63, 41, 47) + +ind = np.arange(N) # the x locations for the groups +width = 0.35 # the width of the bars + +fig, ax = plt.subplots() +rects1 = ax.bar(ind, defaultMeans, width, color='#cc1111') + +scissorMeans = (16, 19, 22, 16, 15) +rects2 = ax.bar(ind + width, scissorMeans, width, color='#0088cc') + +# add some text for labels, title and axes ticks +ax.set_ylabel('time (ms)') +ax.set_title('render types') +ax.set_xticks(ind + width) +ax.set_xticklabels(('blinn-phong', 'bloom', 'gaussian\nbloom', 'toon\nshading', 'motion blur')) + +ax.legend((rects1[0], rects2[0]), ('scissor test off', 'scissor test on')) +ax.axis((0,5,0,100)) + + +def autolabel(rects): + # attach some text labels + for rect in rects: + height = rect.get_height() + ax.text(rect.get_x() + rect.get_width()/2., 1.05*height, + '%d' % int(height), + ha='center', va='bottom') + +autolabel(rects1) +autolabel(rects2) + +plt.show() diff --git a/img/position.png b/img/position.png new file mode 100644 index 0000000..da4e107 Binary files /dev/null and b/img/position.png differ diff --git a/img/scissor_test.png b/img/scissor_test.png new file mode 100644 index 0000000..ad0a34c Binary files /dev/null and b/img/scissor_test.png differ diff --git a/img/surface_normal.png b/img/surface_normal.png new file mode 100644 index 0000000..7f80e6c Binary files /dev/null and b/img/surface_normal.png differ diff --git a/img/toon.png b/img/toon.png new file mode 100644 index 0000000..c6ecda2 Binary files /dev/null and b/img/toon.png differ diff --git a/js/deferredRender.js b/js/deferredRender.js index bb3edd4..e9b05df 100644 --- a/js/deferredRender.js +++ b/js/deferredRender.js @@ -1,7 +1,6 @@ (function() { 'use strict'; // deferredSetup.js must be loaded first - R.deferredRender = function(state) { if (!aborted && ( !R.progCopy || @@ -9,8 +8,11 @@ !R.progClear || !R.prog_Ambient || !R.prog_BlinnPhong_PointLight || + !R.prog_Toon || !R.prog_Debug || - !R.progPost1)) { + !R.progPost1 || + !R.progMoblur || + !R.progBloom)) { console.log('waiting for programs to load...'); return; } @@ -26,13 +28,14 @@ // Execute deferred shading pipeline // CHECKITOUT: START HERE! You can even uncomment this: - //debugger; + // debugger; { // TODO: this block should be removed after testing renderFullScreenQuad - gl.bindFramebuffer(gl.FRAMEBUFFER, null); + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); // TODO: Implement/test renderFullScreenQuad first - renderFullScreenQuad(R.progRed); - return; + //renderFullScreenQuad(R.progRed); + //renderFullScreenQuad(R.progCopy); + //return; } R.pass_copy.render(state); @@ -44,34 +47,92 @@ } else { // * Deferred pass and postprocessing pass(es) // TODO: uncomment these - // R.pass_deferred.render(state); - // R.pass_post1.render(state); - - // OPTIONAL TODO: call more postprocessing passes, if any + R.pass_deferred.render(state); + R.pass_post1.render(state); + + // OPTIONAL TODO: call more postprocessing passes, if any + if(cfg.bloom) + { + + //gl.enable(gl.BLEND); + //gl.blendEquation( gl.FUNC_ADD ); + //gl.blendFunc(gl.ONE,gl.ONE); + + + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc(gl.ONE,gl.ONE); + //gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + + var gauss = 0; + if (cfg.gaussian) + gauss = 1; + + + R.pass_bloom.render(state, gauss); + + //console.log('in bloom section'); + + gl.disable(gl.BLEND); + } + + //R.pass_deferred.render(state); + var matdiff1 = R.lastCamPos; + //console.log(matdiff); + var matdiff2 = new THREE.Matrix4().getInverse( matdiff1 ); + + //console.log(matdiff); + var matdiff = new THREE.Matrix4().multiplyMatrices(matdiff2, state.cameraMat); + //console.log(matdiff); + //console.log(matdiff1); + + //R.pass_moblur.render(state, matdiff); + + // OPTIONAL TODO: call more postprocessing passes, if any + if(cfg.motionblur) + { + + //gl.enable(gl.BLEND); + //gl.blendEquation( gl.FUNC_ADD ); + //gl.blendFunc(gl.ONE,gl.ONE); + + + //gl.enable(gl.BLEND); + //gl.blendEquation( gl.FUNC_ADD ); + //gl.blendFunc(gl.ONE,gl.ONE); + //gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + + + R.pass_moblur.render(state, matdiff, state.cameraMat); + + gl.disable(gl.BLEND); + } + //R.lastCamPos = state.cameraMat; } + //R.lastCamPos = state.cameraMat; }; - + //R.lastCamPos = state.cameraMat; /** * 'copy' pass: Render into g-buffers */ R.pass_copy.render = function(state) { // * Bind the framebuffer R.pass_copy.fbo // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); + gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); // * Clear screen using R.progClear // TODO: uncomment - // renderFullScreenQuad(R.progClear); + renderFullScreenQuad(R.progClear); // * Clear depth buffer to value 1.0 using gl.clearDepth and gl.clear // TODO: uncomment - // gl.clearDepth(1.0); - // gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); // * "Use" the program R.progCopy.prog // TODO: uncomment - // gl.useProgram(R.progCopy.prog); + gl.useProgram(R.progCopy.prog); // TODO: Go write code in glsl/copy.frag.glsl @@ -79,11 +140,12 @@ // * Upload the camera matrix m to the uniform R.progCopy.u_cameraMat // using gl.uniformMatrix4fv // TODO: uncomment - // gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); + gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); // * Draw the scene // TODO: uncomment - // drawScene(state); + drawScene(state); + }; var drawScene = function(state) { @@ -93,25 +155,34 @@ // If you want to render one model many times, note: // readyModelForDraw only needs to be called once. readyModelForDraw(R.progCopy, m); - + + //console.log(m); drawReadyModel(m); + + + //console.log(m); + //console.log(R.sphereModel); + //m = R.sphereModel; + //readyModelForDraw(R.progCopy, m); + //readySphereForDraw(R.progCopy, m); + //drawReadyModel(m); } }; R.pass_debug.render = function(state) { // * Unbind any framebuffer, so we can write to the screen // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); // * Bind/setup the debug "lighting" pass // * Tell shader which debug view to use // TODO: uncomment - // bindTexturesForLightPass(R.prog_Debug); - // gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); + bindTexturesForLightPass(R.prog_Debug); + gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); // * Render a fullscreen quad to perform shading on // TODO: uncomment - // renderFullScreenQuad(R.prog_Debug); + renderFullScreenQuad(R.prog_Debug); }; /** @@ -133,30 +204,178 @@ // Here is a wonderful demo of showing how blend function works: // http://mrdoob.github.io/webgl-blendfunctions/blendfunc.html // TODO: uncomment - // gl.enable(gl.BLEND); - // gl.blendEquation( gl.FUNC_ADD ); - // gl.blendFunc(gl.ONE,gl.ONE); + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc(gl.ONE,gl.ONE); // * Bind/setup the ambient pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_Ambient); renderFullScreenQuad(R.prog_Ambient); // * Bind/setup the Blinn-Phong pass, and render using fullscreen quad - bindTexturesForLightPass(R.prog_BlinnPhong_PointLight); + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); + //gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); + + if(cfg.toon) + bindTexturesForLightPass(R.prog_Toon); + else + bindTexturesForLightPass(R.prog_BlinnPhong_PointLight); + + + //console.log(R.sphereModel); + // TODO: add a loop here, over the values in R.lights, which sets the // uniforms R.prog_BlinnPhong_PointLight.u_lightPos/Col/Rad etc., // then does renderFullScreenQuad(R.prog_BlinnPhong_PointLight). + + + for (var i = 0; i < R.lights.length; i++) + //for (var i = 0; i < 1; i++) + { + //console.log(R.lights[i]); + + + if (cfg.debugScissor) + { + if (cfg.sphericalscissor) + { + readyModelForDraw(R.progSphere, R.sphereModel); + //gl.enable(gl.SCISSOR_TEST); + + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + + if (sc) + {//if(sc[0] > 0 && sc[1] > 0 && sc[0] + sc[2] < 800 && sc[1] + sc[3] < 800) + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + } + gl.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + + //renderFullScreenQuad(R.progRed); + //renderFullScreenSphere(R.progSphere); + //debugger; + gl.uniformMatrix4fv(R.progSphere.u_cameraMat, false, state.cameraMat.elements); + //gl.uniformMatrix4fv(R.progSphere.u_modelMat, false, state.projMat.elements); + gl.uniform3fv(R.progSphere.u_offsetpos, R.lights[i].pos); + gl.uniform1f(R.progSphere.u_rad, R.lights[i].rad); + + drawReadyModel(R.sphereModel); + + //console.log(R.sphereModel); + + gl.disable(gl.BLEND); + //gl.disable(gl.SCISSOR_TEST); + } + else + { + gl.enable(gl.SCISSOR_TEST); + + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + + if (sc) + if(sc[0] > 0 && sc[1] > 0 && sc[0] + sc[2] < 800 && sc[1] + sc[3] < 800) + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + + gl.enable( gl.BLEND ); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); + renderFullScreenQuad(R.progRed); + gl.disable(gl.BLEND); + gl.disable(gl.SCISSOR_TEST); + } + + } + else if(cfg.toon) + { + gl.uniform3f(R.prog_Toon.u_lightPos, + R.lights[i].pos[0], + R.lights[i].pos[1], + R.lights[i].pos[2]); + + gl.uniform3f(R.prog_Toon.u_lightCol, + R.lights[i].col[0], + R.lights[i].col[1], + R.lights[i].col[2]); + + + gl.uniform3f(R.prog_Toon.u_cameraPos, state.cameraPos.x, + state.cameraPos.y, + state.cameraPos.z); + + + gl.uniform1f(R.prog_Toon.u_lightRad, R.lights[i].rad); + + if (cfg.enablescissor) + { + gl.enable(gl.SCISSOR_TEST); + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + if (sc) + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + + renderFullScreenQuad(R.prog_Toon); + + gl.disable(gl.SCISSOR_TEST); + } + else + renderFullScreenQuad(R.prog_Toon); + } + else + { + //debugger; + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightPos, + R.lights[i].pos[0], + R.lights[i].pos[1], + R.lights[i].pos[2]); + //debugger; + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightCol, + R.lights[i].col[0], + R.lights[i].col[1], + R.lights[i].col[2]); + + + + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_cameraPos, + state.cameraPos.x, + state.cameraPos.y, + state.cameraPos.z); + + //console.log([state.cameraPos.x, state.cameraPos.y, state.cameraPos.z]); + + + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, R.lights[i].rad); + + + // TODO: In the lighting loop, use the scissor test optimization + // Enable gl.SCISSOR_TEST, render all lights, then disable it. + // getScissorForLight returns null if the scissor is off the screen. + // Otherwise, it returns an array [xmin, ymin, width, height]. + // + + + if (cfg.enablescissor) + { + gl.enable(gl.SCISSOR_TEST); + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + if (sc) + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + + gl.disable(gl.SCISSOR_TEST); + } + else + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); - // TODO: In the lighting loop, use the scissor test optimization - // Enable gl.SCISSOR_TEST, render all lights, then disable it. - // - // getScissorForLight returns null if the scissor is off the screen. - // Otherwise, it returns an array [xmin, ymin, width, height]. - // - // var sc = getScissorForLight(state.viewMat, state.projMat, light); + } + //console.log(state.viewMat); + //console.log(R.lastCamPos); - // Disable blending so that it doesn't affect other code + + + // Disable blending so that it doesn't affect other code + } gl.disable(gl.BLEND); }; @@ -192,11 +411,11 @@ // * Bind the deferred pass's color output as a texture input // Set gl.TEXTURE0 as the gl.activeTexture unit // TODO: uncomment - // gl.activeTexture(gl.TEXTURE0); + gl.activeTexture(gl.TEXTURE0); // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit // TODO: uncomment - // gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); // Configure the R.progPost1.u_color uniform to point at texture unit 0 gl.uniform1i(R.progPost1.u_color, 0); @@ -204,6 +423,98 @@ // * Render a fullscreen quad to perform shading on renderFullScreenQuad(R.progPost1); }; + + R.pass_bloom.render = function(state, gauss) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progBloom.prog); + + + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE1); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progBloom.u_color uniform to point at texture unit 1 + //gl.uniform1i(R.progBloom.u_color, 1); + gl.uniform1i(R.progBloom.u_gauss, gauss); + + + //console.log(gauss); + //gl.uniform1i(R.progBloom.u_gauss, gauss); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progBloom); + }; + + R.pass_moblur.render = function(state, matdiff, cameraMat) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + // * Clear the framebuffer depth to 1.0 + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); + + // * Bind the postprocessing shader program + gl.useProgram(R.progMoblur.prog); + + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + + gl.activeTexture(gl.TEXTURE2); + //gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + // TEST PASSES FOR POSITION STORING + //bindTexturesForLightPass(R.progMoblur); + //gl.bindTexture(gl.TEXTURE_2D, R.pass_moblur.gbufs); + //gl.uniform1i(R.progMoblur.u_gbufs, 0); + + //gl.bindTexture(gl.TEXTURE_2D, R.pass_copy.gbufs[0]); + //gl.uniform1i(R.progMoblur.u_texture, R.progBloom.u_texture); + gl.uniform1i(R.progMoblur.u_gbufs, R.pass_copy.gbufs[0]); + gl.uniformMatrix4fv(R.progMoblur.u_cameraMat, false, cameraMat.elements); + gl.uniformMatrix4fv(R.progMoblur.u_matdiff, false, matdiff.elements); + + var camInverse = new THREE.Matrix4(); + camInverse = camInverse.getInverse(state.cameraMat); + gl.uniformMatrix4fv(R.progMoblur.u_cameraInverse, false, camInverse.elements); + gl.uniformMatrix4fv(R.progMoblur.u_lastCamMat, false, R.lastCamPos.elements); + + + //gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + //gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + + // Configure the R.progBloom.u_color uniform to point at texture unit 2 + //gl.uniform1i(R.progMoblur.u_color, 2); + + + //console.log(matdiff); + + + + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); + + //console.log(gauss); + //gl.uniform1i(R.progBloom.u_gauss, gauss); + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progMoblur); + + R.lastCamPos = state.cameraMat; + }; var renderFullScreenQuad = (function() { // The variables in this function are private to the implementation of @@ -230,12 +541,74 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER,vbo); + gl.bindBuffer(gl.ARRAY_BUFFER,vbo); + + // Upload the positions array to the currently-bound array buffer + // using gl.bufferData in static draw mode. + // TODO: uncomment + gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); + }; + + return function(prog) { + if (!vbo) { + // If the vbo hasn't been initialized, initialize it. + init(); + } + + // Bind the program to use to draw the quad + gl.useProgram(prog.prog); + + // Bind the VBO as the gl.ARRAY_BUFFER + // TODO: uncomment + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + + // Enable the bound buffer as the vertex attrib array for + // prog.a_position, using gl.enableVertexAttribArray + // TODO: uncomment + gl.enableVertexAttribArray(prog.a_position); + + // Use gl.vertexAttribPointer to tell WebGL the type/layout for + // prog.a_position's access pattern. + // TODO: uncomment + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); + + // Use gl.drawArrays (or gl.drawElements) to draw your quad. + // TODO: uncomment + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + + // Unbind the array buffer. + gl.bindBuffer(gl.ARRAY_BUFFER, null); + }; + })(); + /* + var renderFullScreenSphere = (function() { + // The variables in this function are private to the implementation of + // renderFullScreenQuad. They work like static local variables in C++. + + // Create an array of floats, where each set of 3 is a vertex position. + // You can render in normalized device coordinates (NDC) so that the + // vertex shader doesn't have to do any transformation; draw two + // triangles which cover the screen over x = -1..1 and y = -1..1. + // This array is set up to use gl.drawArrays with gl.TRIANGLE_STRIP. + + + + var positions = R.sphereModel.position; + var vbo = null; + + var init = function() { + // Create a new buffer with gl.createBuffer, and save it as vbo. + // TODO: uncomment + vbo = gl.createBuffer(); + + // Bind the VBO as the gl.ARRAY_BUFFER + // TODO: uncomment + gl.bindBuffer(gl.ARRAY_BUFFER,vbo); // Upload the positions array to the currently-bound array buffer // using gl.bufferData in static draw mode. // TODO: uncomment - // gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); }; return function(prog) { @@ -249,24 +622,25 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // Enable the bound buffer as the vertex attrib array for // prog.a_position, using gl.enableVertexAttribArray // TODO: uncomment - // gl.enableVertexAttribArray(prog.a_position); + gl.enableVertexAttribArray(prog.a_position); // Use gl.vertexAttribPointer to tell WebGL the type/layout for // prog.a_position's access pattern. // TODO: uncomment - // gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); // Use gl.drawArrays (or gl.drawElements) to draw your quad. // TODO: uncomment - // gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Unbind the array buffer. gl.bindBuffer(gl.ARRAY_BUFFER, null); }; })(); + */ })(); diff --git a/js/deferredSetup.js b/js/deferredSetup.js index 65136e0..9a4a475 100644 --- a/js/deferredSetup.js +++ b/js/deferredSetup.js @@ -6,9 +6,14 @@ R.pass_debug = {}; R.pass_deferred = {}; R.pass_post1 = {}; + R.pass_bloom = {}; + R.pass_moblur = {}; R.lights = []; - - R.NUM_GBUFFERS = 4; + R.lastCamPos = new THREE.Matrix4(); + R.camMatDiff = new THREE.Matrix4(); + + + R.NUM_GBUFFERS = 5; /** * Set up the deferred pipeline framebuffer objects and textures. @@ -25,7 +30,7 @@ R.light_max = [14, 18, 6]; R.light_dt = -0.03; R.LIGHT_RADIUS = 4.0; - R.NUM_LIGHTS = 20; // TODO: test with MORE lights! + R.NUM_LIGHTS = 200; // TODO: test with MORE lights! var setupLights = function() { Math.seedrandom(0); @@ -124,7 +129,25 @@ // Create an object to hold info about this shader program R.progRed = { prog: prog }; }); - + + loadShaderProgram(gl, 'glsl/sphere.vert.glsl', 'glsl/sphere.frag.glsl', + function(prog) { + + var p = { prog: prog }; + // Retrieve the uniform and attribute locations + p.u_cameraMat = gl.getUniformLocation(prog, 'u_cameraMat'); + p.u_colmap = gl.getUniformLocation(prog, 'u_colmap'); + p.u_normap = gl.getUniformLocation(prog, 'u_normap'); + p.a_position = gl.getAttribLocation(prog, 'a_position'); + p.a_normal = gl.getAttribLocation(prog, 'a_normal'); + p.a_uv = gl.getAttribLocation(prog, 'a_uv'); + p.u_offsetpos = gl.getUniformLocation(prog, 'u_offsetpos'); + p.u_rad = gl.getUniformLocation(prog, 'u_rad'); + // Create an object to hold info about this shader program + R.progSphere = p; + }); + + loadShaderProgram(gl, 'glsl/quad.vert.glsl', 'glsl/clear.frag.glsl', function(prog) { // Create an object to hold info about this shader program @@ -141,9 +164,28 @@ p.u_lightPos = gl.getUniformLocation(p.prog, 'u_lightPos'); p.u_lightCol = gl.getUniformLocation(p.prog, 'u_lightCol'); p.u_lightRad = gl.getUniformLocation(p.prog, 'u_lightRad'); + p.u_cameraPos = gl.getUniformLocation(p.prog, 'u_cameraPos'); R.prog_BlinnPhong_PointLight = p; }); + loadDeferredProgram('toon', function(p) { + // Save the object into this variable for access later + p.u_lightPos = gl.getUniformLocation(p.prog, 'u_lightPos'); + p.u_lightCol = gl.getUniformLocation(p.prog, 'u_lightCol'); + p.u_lightRad = gl.getUniformLocation(p.prog, 'u_lightRad'); + p.u_cameraPos = gl.getUniformLocation(p.prog, 'u_cameraPos'); + R.prog_Toon = p; + }); + /* + loadDeferredProgram('sphere', function(p) { + // Save the object into this variable for access later + p.u_lightPos = gl.getUniformLocation(p.prog, 'u_lightPos'); + p.u_lightCol = gl.getUniformLocation(p.prog, 'u_lightCol'); + p.u_lightRad = gl.getUniformLocation(p.prog, 'u_lightRad'); + p.u_cameraPos = gl.getUniformLocation(p.prog, 'u_cameraPos'); + R.prog_Sphere = p; + }); + */ loadDeferredProgram('debug', function(p) { p.u_debug = gl.getUniformLocation(p.prog, 'u_debug'); // Save the object into this variable for access later @@ -157,6 +199,24 @@ }); // TODO: If you add more passes, load and set up their shader programs. + + loadPostProgram('bloom', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_gauss = gl.getUniformLocation(p.prog, 'u_gauss'); + // Save the object into this variable for access later + R.progBloom = p; + }); + + loadPostProgram('moblur', function(p) { + p.u_cameraMat = gl.getUniformLocation(p.prog, 'u_cameraMat'); + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_matdiff = gl.getUniformLocation(p.prog, 'u_matdiff'); + p.u_cameraInverse = gl.getUniformLocation(p.prog, 'u_cameraInverse'); + p.u_lastCamMat = gl.getUniformLocation(p.prog, 'u_lastCamMat'); + p.u_gbufs = gl.getUniformLocation(p.prog, 'u_gbufs'); + // Save the object into this variable for access later + R.progMoblur = p; + }); }; var loadDeferredProgram = function(name, callback) { diff --git a/js/framework.js b/js/framework.js index 4f944ee..897d71e 100644 --- a/js/framework.js +++ b/js/framework.js @@ -67,7 +67,8 @@ var width, height; var init = function() { // TODO: For performance measurements, disable debug mode! - var debugMode = true; + //var debugMode = true; + var debugMode = false; canvas = document.getElementById('canvas'); renderer = new THREE.WebGLRenderer({ @@ -122,8 +123,8 @@ var width, height; R.sphereModel = m; }); - // var glTFURL = 'models/glTF-duck/duck.gltf'; - var glTFURL = 'models/glTF-sponza-kai-fix/sponza.gltf'; + //var glTFURL = 'models/glTF-duck/duck.gltf'; + var glTFURL = 'models/gltf-sponza-kai-fix/sponza.gltf'; var glTFLoader = new MinimalGLTFLoader.glTFLoader(gl); glTFLoader.loadGLTF(glTFURL, function (glTF) { var curScene = glTF.scenes[glTF.defaultScene]; @@ -238,6 +239,8 @@ var width, height; gltf: primitive, idx: indicesBuffer, + + interleaved: true, attributes: vertexBuffer, posInfo: {size: posInfo.size, type: posInfo.type, stride: posInfo.stride, offset: posInfo.offset}, @@ -318,6 +321,7 @@ var width, height; var m = { idx: gidx, elemCount: idx.length, + interleaved: false, position: gposition, normal: gnormal, uv: guv diff --git a/js/ui.js b/js/ui.js index abd6119..9558edf 100644 --- a/js/ui.js +++ b/js/ui.js @@ -7,7 +7,12 @@ var cfg; // TODO: Define config fields and defaults here this.debugView = -1; this.debugScissor = false; - this.enableEffect0 = false; + this.sphericalscissor = false; + this.bloom = false; + this.gaussian = false; + this.enablescissor = false; + this.toon = false; + this.motionblur = false; }; var init = function() { @@ -28,8 +33,13 @@ var cfg; var eff0 = gui.addFolder('EFFECT NAME HERE'); eff0.open(); - eff0.add(cfg, 'enableEffect0'); + eff0.add(cfg, 'bloom'); + eff0.add(cfg, 'gaussian'); // TODO: add more effects toggles and parameters here + eff0.add(cfg, 'enablescissor'); + eff0.add(cfg, 'sphericalscissor'); + eff0.add(cfg, 'toon'); + eff0.add(cfg, 'motionblur'); }; window.handle_load.push(init); diff --git a/js/util.js b/js/util.js index 8f43d38..09825a2 100644 --- a/js/util.js +++ b/js/util.js @@ -92,11 +92,56 @@ window.readyModelForDraw = function(prog, m) { gl.uniform1i(prog.u_normap, 1); } - gl.bindBuffer(gl.ARRAY_BUFFER, m.attributes); + if (m.interleaved) { + gl.bindBuffer(gl.ARRAY_BUFFER, m.attributes); + gl.enableVertexAttribArray(prog.a_position); + gl.vertexAttribPointer(prog.a_position, m.posInfo.size, m.posInfo.type, false, m.posInfo.stride, m.posInfo.offset); + + gl.enableVertexAttribArray(prog.a_normal); + gl.vertexAttribPointer(prog.a_normal, m.norInfo.size, m.norInfo.type, false, m.norInfo.stride, m.norInfo.offset); + + gl.enableVertexAttribArray(prog.a_uv); + gl.vertexAttribPointer(prog.a_uv, m.uvInfo.size, m.uvInfo.type, false, m.uvInfo.stride, m.uvInfo.offset); + } else { + gl.enableVertexAttribArray(prog.a_position); + gl.bindBuffer(gl.ARRAY_BUFFER, m.position); + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, false, 0, 0); + + if (prog.a_normal >= 0 && m.normal) { + gl.enableVertexAttribArray(prog.a_normal); + gl.bindBuffer(gl.ARRAY_BUFFER, m.normal); + gl.vertexAttribPointer(prog.a_normal, 3, gl.FLOAT, false, 0, 0); + } + + if (prog.a_uv >= 0 && m.uv) { + gl.enableVertexAttribArray(prog.a_uv); + gl.bindBuffer(gl.ARRAY_BUFFER, m.uv); + gl.vertexAttribPointer(prog.a_uv, 2, gl.FLOAT, false, 0, 0); + } + } + + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.idx); +}; + + +window.readySphereForDraw = function(prog, m) { + gl.useProgram(prog.prog); + + gl.bindBuffer(gl.ARRAY_BUFFER, m.position); gl.enableVertexAttribArray(prog.a_position); - gl.vertexAttribPointer(prog.a_position, m.posInfo.size, m.posInfo.type, false, m.posInfo.stride, m.posInfo.offset); + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, false, 0, 0); + gl.bindBuffer(gl.ARRAY_BUFFER, m.normal); + gl.enableVertexAttribArray(prog.a_normal); + gl.vertexAttribPointer(prog.a_normal, 3, gl.FLOAT, false, 0, 0); + + gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.idx); + + + /* + gl.enableVertexAttribArray(prog.a_normal); gl.vertexAttribPointer(prog.a_normal, m.norInfo.size, m.norInfo.type, false, m.norInfo.stride, m.norInfo.offset); @@ -104,46 +149,61 @@ window.readyModelForDraw = function(prog, m) { gl.vertexAttribPointer(prog.a_uv, m.uvInfo.size, m.uvInfo.type, false, m.uvInfo.stride, m.uvInfo.offset); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.idx); + */ }; window.drawReadyModel = function(m) { // TODO for TA in future: matrix transform for multiple hierachy gltf models // reference: https://github.com/CIS565-Fall-2016/Project5A-WebGL-Forward-Plus-Shading-with-glTF/blob/master/js/forwardPlusRenderer/forwardPlusRenderer.js#L201 - gl.drawElements(m.gltf.mode, m.gltf.indices.length, m.gltf.indicesComponentType, 0); + if(m.gltf) + gl.drawElements(m.gltf.mode, m.gltf.indices.length, m.gltf.indicesComponentType, 0); + else + gl.drawElements(gl.TRIANGLES, m.elemCount, gl.UNSIGNED_INT, 0); }; window.getScissorForLight = (function() { // Pre-allocate for performance - avoids additional allocation var a = new THREE.Vector4(0, 0, 0, 0); var b = new THREE.Vector4(0, 0, 0, 0); - var minpt = new THREE.Vector2(0, 0); - var maxpt = new THREE.Vector2(0, 0); + const neg_one = new THREE.Vector2(-1, -1); + const pos_one = new THREE.Vector2(1, 1); var ret = [0, 0, 0, 0]; - return function(view, proj, l) { - // front bottom-left corner of sphere's bounding cube - a.fromArray(l.pos); - a.w = 1; - a.applyMatrix4(view); - a.x -= l.rad; - a.y -= l.rad; - a.z += l.rad; - a.applyMatrix4(proj); - a.divideScalar(a.w); + const n_cube_vertices = 8; + var offsets = Array(n_cube_vertices); + var pos = new THREE.Vector4(0, 0, 0, 0); + return function(view, proj, light) { // front bottom-left corner of sphere's bounding cube - b.fromArray(l.pos); - b.w = 1; - b.applyMatrix4(view); - b.x += l.rad; - b.y += l.rad; - b.z += l.rad; - b.applyMatrix4(proj); - b.divideScalar(b.w); - - minpt.set(Math.max(-1, a.x), Math.max(-1, a.y)); - maxpt.set(Math.min( 1, b.x), Math.min( 1, b.y)); + + // The following results in: + // [ -1, -1, -1, 0 ] <== back upper left corner of cube + // [ -1, -1, 1, 0 ] <== front upper left corner ... + // [ -1, 1, -1, 0 ] + // ... + var index = 0; + for (var i = -1; i <= 1; i += 2) { + for (var j = -1; j <= 1; j += 2) { + for (var k = -1; k <= 1; k += 2) { + offsets[index] = new THREE.Vector4(i, j, k, 0); + index += 1; + } + } + } + + var minpt = new THREE.Vector2(1, 1); + var maxpt = new THREE.Vector2(-1, -1); + for (var i in offsets) { + pos.fromArray(light.pos); + pos.w = 1; + pos.applyMatrix4(view); // project light into view space + pos.addScaledVector(offsets[i], light.rad); // offset from light + pos.applyMatrix4(proj); // project onto screen + pos.divideScalar(pos.w); + minpt.clamp(neg_one, pos); // update min point with pos + maxpt.clamp(pos, pos_one); // update max point with pos + } if (maxpt.x < -1 || 1 < minpt.x || maxpt.y < -1 || 1 < minpt.y) { diff --git a/layers.xcf b/layers.xcf new file mode 100644 index 0000000..ab6bbb0 Binary files /dev/null and b/layers.xcf differ diff --git a/lib/stats.min.js b/lib/stats.min.js index a2d1872..f02bc5c 100644 --- a/lib/stats.min.js +++ b/lib/stats.min.js @@ -1,5 +1,5 @@ // stats.js - http://github.com/mrdoob/stats.js var Stats=function(){function f(a,e,b){a=document.createElement(a);a.id=e;a.style.cssText=b;return a}function l(a,e,b){var c=f("div",a,"padding:0 0 3px 3px;text-align:left;background:"+b),d=f("div",a+"Text","font-family:Helvetica,Arial,sans-serif;font-size:9px;font-weight:bold;line-height:15px;color:"+e);d.innerHTML=a.toUpperCase();c.appendChild(d);a=f("div",a+"Graph","width:74px;height:30px;background:"+e);c.appendChild(a);for(e=0;74>e;e++)a.appendChild(f("span","","width:1px;height:30px;float:left;opacity:0.9;background:"+ b));return c}function m(a){for(var b=c.children,d=0;dr+1E3&&(d=Math.round(1E3* +A=b.children[0],B=b.children[1];c.appendChild(b);var g=0,w=Infinity,x=0,b=l("ms","#0f0","#020"),C=b.children[0],D=b.children[1];c.appendChild(b);if(self.performance&&self.performance.memory){var h=0,y=Infinity,z=0,b=l("mb","#f08","#201"),E=b.children[0],F=b.children[1];c.appendChild(b)}m(n);return{REVISION:14,domElement:c,setMode:m,begin:function(){k=q()},end:function(){var a=q();g=a-k;w=Math.min(w,g);x=Math.max(x,g);/*console.log(g|0)*/;C.textContent=(g|0)+" MS ("+(w|0)+"-"+(x|0)+")";p(D,g/200);t++;if(a>r+1E3&&(d=Math.round(1E3* t/(a-r)),u=Math.min(u,d),v=Math.max(v,d),A.textContent=d+" FPS ("+u+"-"+v+")",p(B,d/100),r=a,t=0,void 0!==h)){var b=performance.memory.usedJSHeapSize,c=performance.memory.jsHeapSizeLimit;h=Math.round(9.54E-7*b);y=Math.min(y,h);z=Math.max(z,h);E.textContent=h+" MB ("+y+"-"+z+")";p(F,b/c)}return a},update:function(){k=this.end()}}};"object"===typeof module&&(module.exports=Stats);