diff --git a/README.md b/README.md index 8705350..e4ca357 100644 --- a/README.md +++ b/README.md @@ -3,24 +3,181 @@ 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) +* Akshay Shah +* Tested on: **Google Chrome 54.0.2840.87 m** on Windows 10, i7-5700HQ @ 2.70GHz 16GB, GTX 970M 6GB (Personal Computer) ### Live Online -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +[![Deferred Shader](img/deferred_shader.PNG)](https://aksris.github.io/Project5-WebGL-Deferred-Shading-with-glTF/) ### Demo Video/GIF -[![](img/video.png)](TODO) +[![](img/bloom.PNG)](https://vimeo.com/190890932) -### (TODO: Your README) +### Deferred Renderer -*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. +Deferred shading is a screen-space method where the first pass does not actually involve shading and is deferred to the second pass. In the first pass positions, normals, and materials for each surface are rendered into the g-buffer and the shading happens in the deferred pass computing the lighting at each pixel using textures. The primary advantage is that geometry is separate from lighting giving a significant performance improvement over raytracers or rasterizers. -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! +    This renderer uses several debug passes: Depth, position, normal and color map after packing the geometry normal into the copy stage. +| Depth | Position | Color Map | Blinn-Phong | +| ----- | -------- | --------- | ----------- | +| ![](img/depth.PNG) | ![](img/position.PNG) | ![](img/colormap.PNG) | ![](img/blinnphong.PNG) | +This is the [sponza scene](http://graphics.cs.williams.edu/data/meshes.xml), with 20 lights (unless mentioned otherwise). +Optimizations include: gbuffer packing, improved screen-space aabb scissor test + +| Without optimizations | With Optimizations | +| --------------------- | ------------------ | +| runs @ 40ms | runs @ 28ms | + +Effects +------- + +- [x] Implemented deferred Blinn-Phong shading (diffuse + specular) for point lights + - With normal mapping +- [x] Bloom using post-process blur + - [x] Two-pass Gaussian blur using separable convolution using a second postprocess render pass) to improve bloom or other 2D blur performance + +![](img/bloomunopt.PNG) +There are also some undesired blurring artifacts visible. + +This does not include the optimizations to the shading pass. With all the optimizations, the bloom runs at 34ms. + +| Deferred pass | Bloom with 2-pass blur | +| ------------- | ---------------------- | +| runs @ 35ms | runs @ 40ms | + +| Bloom without optimizations | Bloom with Optimizations | +| --------------------------- | ------------------------ | +| runs @ 40ms | runs @ 32ms | + + The bloom pass added 7.91% to the total computation which was only a ~4.8ms of the render time. + +- [x] implemented efficient gaussian blur with linear sampling + +| Bloom unoptimized | Bloom optimized with efficient linear sampling | +| ----------------- | ---------------------------------------------- | +| runs @ 40ms | runs @ 31ms | + +There wasn't a very significant improvement in the performance as noted by [this][1] article. + +![](img/bloomopt.PNG) +No more artifacts. + +[![](img/bloom.PNG)](https://vimeo.com/190890932) + +Optimizations +------------- + +- [x] Scissor test optimization: when accumulating shading from each point light source, only render in a rectangle around the light. + - [x] Improved screen-space AABB for scissor test + + ![](img/scissor_debug.PNG) + + The scissor test is an optimization that discards fragments that are out of the light's bounding box portion of the screen, thus improving the shading performance. + + > Polygons are clipped to the edge of projection space, but other draw operations like glClear() are not. So, you use glViewport() to determine the location and size of the screen space viewport region, but the rasterizer can still occasionally render pixels outside that region. + + Borrowed from [postgoodism](http://gamedev.stackexchange.com/users/19286/postgoodism) from [gamedev.stackexchange.com](http://gamedev.stackexchange.com/questions/40704/what-is-the-purpose-of-glscissor) + +```javascript + a.fromArray(l.pos); +a.w = 1; +a.applyMatrix4(view); +a.x -= l.rad; +a.y -= l.rad; +// a.z += l.rad; + +// front bottom-left corner of sphere's bounding cube +b.fromArray(l.pos); +b.w = 1; +b.applyMatrix4(view); +b.x = a.x + l.rad * 2.0; +b.y = a.y + l.rad * 2.0; +b.z = a.z; +a.applyMatrix4(proj); +a.divideScalar(a.w); +b.applyMatrix4(proj); +b.divideScalar(b.w); +``` + +Here I'm not using `a.z`, but instead just reusing the variables to fill the `b` array. This neat little trick can improve the performance by a healthy 5~7ms. + +- [x] Optimized g-buffer format - reduced the number and size of g-buffers: + - Reduce number of properties passed via g-buffer, by: + - Applying the normal map in the copy shader pass instead of copying both geometry normals and normal maps + +So instead of using 4 GBUFFERS, I pack the normal in the copy pass as the geometry normal was only being used to calculate the surface normals which was what I needed all along. + +So in the copy fragment shader: + +```glsl +#version 100 +#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; + +uniform sampler2D u_colmap; +uniform sampler2D u_normap; + +varying vec3 v_position; +varying vec3 v_normal; +varying vec2 v_uv; + +vec3 applyNormalMap(vec3 geomnor, vec3 normap) { + normap = normap * 2.0 - 1.0; + vec3 up = normalize(vec3(0.001, 1, 0.001)); + vec3 surftan = normalize(cross(geomnor, up)); + vec3 surfbinor = cross(geomnor, surftan); + + return normap.y * surftan + normap.x * surfbinor + normap.z * geomnor; +} +void main() { + + gl_FragData[0] = vec4( v_position, 1.0 ); + gl_FragData[1] = vec4(applyNormalMap(v_normal, texture2D(u_normap, v_uv).rgb), 1.0); + gl_FragData[2] = texture2D(u_colmap, v_uv).rgba; + +} + +``` + +| With 4 GBuffers | With 3 GBuffers | +| --------------- | --------------- | +| runs @ 40ms | runs @ 36ms | + +I figured most of the time spent calculating normals in the deferred pass is now being done in the copy pass and more registers on the GPU are freed up. + +More stuff +----------- + +- [x] Toon shading (ramp shader + simple edge detection) +![](img/toon.png) +![](img/toon2edit.PNG) +Simple edge detection with ramp shader +![](img/toon2edge.PNG) + +- [x] Improved screen-space AABB for scissor test +- [x] Two-pass Gaussian blur using separable convolution (using a second postprocess render pass) to improve bloom or other 2D blur performance +- [x] Implemented a sobel edge filter +![](img/sobel_edge.PNG) + +GPU tracing on Chrome +--------------------- + +Trying to make heads and tails of the gpu tracing in chrome. +![](img/gputrace.PNG) + +### References + +* [Three.js](https://github.com/mrdoob/three.js) by [@mrdoob](https://github.com/mrdoob) and contributors +* [stats.js](https://github.com/mrdoob/stats.js) by [@mrdoob](https://github.com/mrdoob) and contributors +* [webgl-debug](https://github.com/KhronosGroup/WebGLDeveloperTools) by Khronos Group Inc. +* [glMatrix](https://github.com/toji/gl-matrix) by [@toji](https://github.com/toji) and contributors +* [minimal-gltf-loader](https://github.com/shrekshao/minimal-gltf-loader) by [@shrekshao](https://github.com/shrekshao) +* [Toon shader](http://in2gpu.com/2014/06/23/toon-shading-effect-and-simple-contour-detection/) by Sergiu Craitoiu +* [Silhouette Extraction](http://prideout.net/blog/?p=54) by Philip Rideout +* [Bloom effect](http://learnopengl.com/#!Advanced-Lighting/Bloom) by Joey de Vries +* [Sobel and Frei-Chen edge detector](http://rastergrid.com/blog/2011/01/frei-chen-edge-detector/) by Daniel Rákos +[1]: http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ "Efficient Gaussian blur with linear sampling" diff --git a/glsl/clear.frag.glsl b/glsl/clear.frag.glsl index b4e4ff3..53d8f4c 100644 --- a/glsl/clear.frag.glsl +++ b/glsl/clear.frag.glsl @@ -3,7 +3,7 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 void main() { for (int i = 0; i < NUM_GBUFFERS; i++) { diff --git a/glsl/copy.frag.glsl b/glsl/copy.frag.glsl index 823ebcd..4585c96 100644 --- a/glsl/copy.frag.glsl +++ b/glsl/copy.frag.glsl @@ -10,11 +10,22 @@ varying vec3 v_position; varying vec3 v_normal; varying vec2 v_uv; +vec3 applyNormalMap(vec3 geomnor, vec3 normap) { + normap = normap * 2.0 - 1.0; + vec3 up = normalize(vec3(0.001, 1, 0.001)); + vec3 surftan = normalize(cross(geomnor, up)); + vec3 surfbinor = cross(geomnor, surftan); + + return normap.y * surftan + normap.x * surfbinor + normap.z * geomnor; +} void main() { // TODO: copy values into gl_FragData[0], [1], etc. // You can use the GLSL texture2D function to access the textures using // the UV in v_uv. // this gives you the idea - // gl_FragData[0] = vec4( v_position, 1.0 ); + gl_FragData[0] = vec4( v_position, 1.0 ); + gl_FragData[1] = vec4(applyNormalMap(v_normal, texture2D(u_normap, v_uv).rgb), 1.0); + gl_FragData[2] = texture2D(u_colmap, v_uv).rgba; + // gl_FragData[3] = texture2D(u_normap, v_uv).xyzw; } diff --git a/glsl/deferred/ambient.frag.glsl b/glsl/deferred/ambient.frag.glsl index 1fd4647..02ac2d6 100644 --- a/glsl/deferred/ambient.frag.glsl +++ b/glsl/deferred/ambient.frag.glsl @@ -3,25 +3,29 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; varying vec2 v_uv; +const vec3 ambient = vec3(0.05, 0.07, 0.12); + void main() { - vec4 gb0 = texture2D(u_gbufs[0], v_uv); - vec4 gb1 = texture2D(u_gbufs[1], v_uv); + // vec4 gb0 = texture2D(u_gbufs[0], v_uv); + // vec4 gb1 = texture2D(u_gbufs[1], v_uv); vec4 gb2 = texture2D(u_gbufs[2], v_uv); - vec4 gb3 = texture2D(u_gbufs[3], v_uv); + // vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables - if (depth == 1.0) { - gl_FragColor = vec4(0, 0, 0, 0); // set alpha to 0 - return; - } + vec3 colmap = gb2.rgb; + + if (depth == 1.0) { + gl_FragColor = vec4(0, 0, 0, 0); // set alpha to 0 + return; + } - gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + gl_FragColor = vec4(vec3(0.1) * colmap, 1); } diff --git a/glsl/deferred/blinnphong-pointlight.frag.glsl b/glsl/deferred/blinnphong-pointlight.frag.glsl index b24a54a..8f11092 100644 --- a/glsl/deferred/blinnphong-pointlight.frag.glsl +++ b/glsl/deferred/blinnphong-pointlight.frag.glsl @@ -2,38 +2,89 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 +// #extension GL_OES_standard_derivatives : enable uniform vec3 u_lightCol; uniform vec3 u_lightPos; uniform float u_lightRad; +uniform vec3 u_viewDir; uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; +uniform int u_toon; 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; -} +// const vec3 ambientColor = vec3(0.1, 0.1, 0.1); +// const vec3 diffuseColor = vec3(0.5, 0.5, 0.5); +// const vec3 specColor = vec3(1.0, 1.0, 1.0); +// const float shininess = 16.0; +// const float screenGamma = 2.2; // Assume the monitor is calibrated to the sRGB color space + +// 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); + // vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables + vec3 pos = gb0.xyz; // World-space position + // vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + vec3 nor = normalize(gb1.xyz); + 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) // 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); + // if (depth == 1.0) { + // gl_FragColor = vec4(0, 0, 0, 0); + // return; + // } + + float lightDistance = distance(u_lightPos, pos); + if (lightDistance > u_lightRad) { return; } + float edgeDetection = 1.0; + + vec3 lightDir = normalize(u_lightPos - pos); + float lambertian = clamp(dot(lightDir, normalize(nor)), 0.0, 1.0); + vec3 viewDir = normalize(u_viewDir - pos); + // this is blinn phong + vec3 halfDir = normalize(lightDir + viewDir); + float specAngle = max(dot(halfDir, nor), 0.0); + float specular = float(lambertian > 0.0) * pow(specAngle, 16.0); + float falloff = clamp(1.0 - (lightDistance * lightDistance)/(u_lightRad * u_lightRad), 0.0, 1.0); + if(u_toon == 1) { + /* Reference: http://prideout.net/blog/?p=22 */ + // float cutoff = 3.0; + // lambertian = ceil(lambertian * cutoff) / cutoff; + // specular = ceil(specular * cutoff) / cutoff; + // falloff = ceil(falloff * cutoff) / cutoff; + const float A = 0.1; + const float B = 0.3; + const float C = 0.6; + const float D = 1.0; + + if (lambertian < A) lambertian = 0.1; + else if (lambertian < B) lambertian = 0.35; + else if (lambertian < C) lambertian = 0.7; + else lambertian = D; + specular = step(0.5, specular); + edgeDetection = (dot(viewDir, nor) > 0.4) ? 1.0 : 0.0; + } - gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations + gl_FragColor = vec4( + (colmap * lambertian + + specular * vec3(1.0)) * u_lightCol * falloff * edgeDetection + , 1); } diff --git a/glsl/deferred/debug.frag.glsl b/glsl/deferred/debug.frag.glsl index 007466f..3cffa91 100644 --- a/glsl/deferred/debug.frag.glsl +++ b/glsl/deferred/debug.frag.glsl @@ -2,7 +2,7 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 3 uniform int u_debug; uniform sampler2D u_gbufs[NUM_GBUFFERS]; @@ -12,41 +12,42 @@ varying vec2 v_uv; const vec4 SKY_COLOR = vec4(0.66, 0.73, 1.0, 1.0); -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; -} +// 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); + // vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables // These definitions are suggested for starting out, but you will probably want to change them. vec3 pos = gb0.xyz; // World-space position - vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + // vec3 geomnor = gb1.xyz; // Normals of the geometry as defined, without normal mapping + vec3 nor = gb1.xyz; vec3 colmap = gb2.rgb; // The color map - unlit "albedo" (surface color) - vec3 normap = gb3.xyz; // The raw normal map (normals relative to the surface they're on) - vec3 nor = applyNormalMap (geomnor, normap); // The true normals as we want to light them - with the normal map applied to the geometry normals (applyNormalMap above) + // vec3 normap = 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: uncomment if (u_debug == 0) { gl_FragColor = vec4(vec3(depth), 1.0); } else if (u_debug == 1) { - // gl_FragColor = vec4(abs(pos) * 0.1, 1.0); + gl_FragColor = vec4(abs(pos) * 0.1, 1.0); } else if (u_debug == 2) { - // gl_FragColor = vec4(abs(geomnor), 1.0); + gl_FragColor = vec4(abs(nor), 1.0); } else if (u_debug == 3) { - // gl_FragColor = vec4(colmap, 1.0); - } else if (u_debug == 4) { - // gl_FragColor = vec4(normap, 1.0); - } else if (u_debug == 5) { - // gl_FragColor = vec4(abs(nor), 1.0); + gl_FragColor = vec4(colmap, 1.0); + // } else if (u_debug == 4) { + // gl_FragColor = vec4(normap, 1.0); + // } else if (u_debug == 5) { + // gl_FragColor = vec4(abs(nor), 1.0); } else { gl_FragColor = vec4(1, 0, 1, 1); } diff --git a/glsl/post/blend.frag.glsl b/glsl/post/blend.frag.glsl new file mode 100644 index 0000000..619ab04 --- /dev/null +++ b/glsl/post/blend.frag.glsl @@ -0,0 +1,23 @@ +#version 100 +precision highp float; +precision highp int; + +varying vec2 v_uv; + +uniform sampler2D u_scene; +uniform sampler2D u_bloomBlur; + + +// Extract bright colors for bloom +void main() +{ + // Reference: http://learnopengl.com/#!Advanced-Lighting/Bloom + const float exposure = 0.8; + const float gamma = 1.1; + vec3 hdrColor = texture2D(u_scene, v_uv).rgb; + hdrColor += texture2D(u_bloomBlur, v_uv).rgb; + hdrColor = vec3(1.0) - exp(-hdrColor * exposure); + hdrColor = pow(hdrColor, vec3(1.0/gamma)); + + gl_FragColor = vec4(hdrColor, 0.5); +} diff --git a/glsl/post/blur.frag.glsl b/glsl/post/blur.frag.glsl new file mode 100644 index 0000000..05cae90 --- /dev/null +++ b/glsl/post/blur.frag.glsl @@ -0,0 +1,62 @@ +#version 100 +precision highp float; +precision highp int; + +varying vec2 v_uv; + +uniform sampler2D u_color; +uniform bool u_horizontal; +uniform vec2 u_tex_offset; + +// float weight[5]; + +float weight[3]; +float offset[3]; + +// uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, 0.054054, 0.016216); +//stupid shit, cant do array in version 100 + + +void main() +{ + + // weight[0] = 0.227027; + // weight[1] = 0.1945946; + // weight[2] = 0.1216216; + // weight[3] = 0.054054; + // weight[4] = 0.016216; + + offset[0] = 0.0; + offset[1] = 1.3846153846; + offset[2] = 3.2307692308; + weight[0] = 0.2270270270; + weight[1] = 0.3162162162; + weight[2] = 0.0702702703; + + /* efficient gaussian blur: linear sampling; + Reference: http://rastergrid.com/blog/2010/09/efficient-gaussian-blur-with-linear-sampling/ */ + + // vec2 tex_offset = 1.0 / textureSize(image, 0); // gets size of single texel + vec3 result = texture2D(u_color, v_uv).rgb * weight[0]; // current fragment's contribution + if(u_horizontal) + { + for(int i = 1; i < 3; ++i) + { + // result += texture2D(u_color, v_uv + vec2(u_tex_offset.x * float(i), 0.0)).rgb * weight[i]; + // result += texture2D(u_color, v_uv - vec2(u_tex_offset.x * float(i), 0.0)).rgb * weight[i]; + result += texture2D(u_color, v_uv + vec2(u_tex_offset.x * offset[i], 0.0)).rgb * weight[i]; + result += texture2D(u_color, v_uv - vec2(u_tex_offset.x * offset[i], 0.0)).rgb * weight[i]; + } + } + else + { + for(int i = 1; i < 3; ++i) + { + // result += texture2D(u_color, v_uv + vec2(u_tex_offset.y * float(i), 0.0)).rgb * weight[i]; + // result += texture2D(u_color, v_uv - vec2(u_tex_offset.y * float(i), 0.0)).rgb * weight[i]; + result += texture2D(u_color, v_uv + vec2(0.0, u_tex_offset.y * offset[i])).rgb * weight[i]; + result += texture2D(u_color, v_uv - vec2(0.0, u_tex_offset.y * offset[i])).rgb * weight[i]; + } + } + gl_FragColor = vec4(result, 1.0); +} diff --git a/glsl/post/hdr.frag.glsl b/glsl/post/hdr.frag.glsl new file mode 100644 index 0000000..dc4faac --- /dev/null +++ b/glsl/post/hdr.frag.glsl @@ -0,0 +1,25 @@ +#version 100 +precision highp float; +precision highp int; + +varying vec2 v_uv; + +uniform sampler2D u_color; + +const float threshold = 0.95; + +// Extract bright colors for bloom +void main() +{ + // Reference: http://learnopengl.com/#!Advanced-Lighting/Bloom + vec4 color = texture2D(u_color, v_uv); + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (brightness > threshold) + { + gl_FragColor = color; + } + else + { + gl_FragColor = vec4(0.0); + } +} diff --git a/glsl/post/one.frag.glsl b/glsl/post/one.frag.glsl index 94191cd..6f34556 100644 --- a/glsl/post/one.frag.glsl +++ b/glsl/post/one.frag.glsl @@ -6,7 +6,7 @@ uniform sampler2D u_color; varying vec2 v_uv; -const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 0.5); void main() { vec4 color = texture2D(u_color, v_uv); diff --git a/glsl/post/sobel.frag.glsl b/glsl/post/sobel.frag.glsl new file mode 100644 index 0000000..b5e7ccc --- /dev/null +++ b/glsl/post/sobel.frag.glsl @@ -0,0 +1,42 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform vec2 u_tex_offset; +varying vec2 v_uv; +mat3 G[2]; + + +void main(void) +{ + /*Reference: http://rastergrid.com/blog/2011/01/frei-chen-edge-detector/ by Daniel Rákos */ + G[0] = mat3( 1.0, 2.0, 1.0, 0.0, 0.0, 0.0, -1.0, -2.0, -1.0 ); + + G[1] = mat3( 1.0, 0.0, -1.0, 2.0, 0.0, -2.0, 1.0, 0.0, -1.0 ); + + mat3 I; + float cnv[2]; + highp vec3 sample; + + /* fetch the 3x3 neighbourhood and use the RGB vector's length as intensity value */ + + I[0][0] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(-1, -1)).rgb); + I[0][1] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(0, -1)).rgb); + I[0][2] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(1, -1)).rgb); + I[1][0] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(-1, 0)).rgb); + I[1][1] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(0, 0)).rgb); + I[1][2] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(1, 0)).rgb); + I[2][0] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(-1, 1)).rgb); + I[2][1] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(0, 1)).rgb); + I[2][2] = length(texture2D(u_color, v_uv + u_tex_offset * vec2(1, 1)).rgb); + + /* calculate the convolution values for all the masks */ + for (int i=0; i<2; i++) { + float dp3 = dot(G[i][0], I[0]) + dot(G[i][1], I[1]) + dot(G[i][2], I[2]); + cnv[i] = dp3 * dp3; + } + + gl_FragColor = vec4(0.5 * sqrt(cnv[0]*cnv[0]+cnv[1]*cnv[1])); + +} diff --git a/glsl/scissor.frag.glsl b/glsl/scissor.frag.glsl new file mode 100644 index 0000000..641561e --- /dev/null +++ b/glsl/scissor.frag.glsl @@ -0,0 +1,9 @@ +#version 100 +precision highp float; +precision highp int; + +uniform vec4 u_color; + +void main() { + gl_FragColor = u_color; +} diff --git a/img/blinnphong.PNG b/img/blinnphong.PNG new file mode 100644 index 0000000..1f563d6 Binary files /dev/null and b/img/blinnphong.PNG differ diff --git a/img/bloom.PNG b/img/bloom.PNG new file mode 100644 index 0000000..504b276 Binary files /dev/null and b/img/bloom.PNG differ diff --git a/img/bloomopt.PNG b/img/bloomopt.PNG new file mode 100644 index 0000000..6246e16 Binary files /dev/null and b/img/bloomopt.PNG differ diff --git a/img/bloomunopt.PNG b/img/bloomunopt.PNG new file mode 100644 index 0000000..854882c Binary files /dev/null and b/img/bloomunopt.PNG differ diff --git a/img/colormap.PNG b/img/colormap.PNG new file mode 100644 index 0000000..efb2727 Binary files /dev/null and b/img/colormap.PNG differ diff --git a/img/deferred_shader.PNG b/img/deferred_shader.PNG new file mode 100644 index 0000000..f30beef Binary files /dev/null and b/img/deferred_shader.PNG differ diff --git a/img/demo.mp4 b/img/demo.mp4 new file mode 100644 index 0000000..329ca06 Binary files /dev/null and b/img/demo.mp4 differ diff --git a/img/depth.PNG b/img/depth.PNG new file mode 100644 index 0000000..eafc5d6 Binary files /dev/null and b/img/depth.PNG differ diff --git a/img/gputrace.PNG b/img/gputrace.PNG new file mode 100644 index 0000000..29fde61 Binary files /dev/null and b/img/gputrace.PNG differ diff --git a/img/normal.PNG b/img/normal.PNG new file mode 100644 index 0000000..61942b9 Binary files /dev/null and b/img/normal.PNG differ diff --git a/img/position.PNG b/img/position.PNG new file mode 100644 index 0000000..7668dc5 Binary files /dev/null and b/img/position.PNG differ diff --git a/img/scissor_debug.PNG b/img/scissor_debug.PNG new file mode 100644 index 0000000..20779dc Binary files /dev/null and b/img/scissor_debug.PNG differ diff --git a/img/sobel_edge.PNG b/img/sobel_edge.PNG new file mode 100644 index 0000000..fbb5092 Binary files /dev/null and b/img/sobel_edge.PNG differ diff --git a/img/toon.png b/img/toon.png new file mode 100644 index 0000000..88420ef Binary files /dev/null and b/img/toon.png differ diff --git a/img/toon2.PNG b/img/toon2.PNG new file mode 100644 index 0000000..7335a27 Binary files /dev/null and b/img/toon2.PNG differ diff --git a/img/toon2edge.PNG b/img/toon2edge.PNG new file mode 100644 index 0000000..0a536b9 Binary files /dev/null and b/img/toon2edge.PNG differ diff --git a/img/toon2edit.PNG b/img/toon2edit.PNG new file mode 100644 index 0000000..49ba75e Binary files /dev/null and b/img/toon2edit.PNG differ diff --git a/index.html b/index.html index 7ada197..cfb829f 100644 --- a/index.html +++ b/index.html @@ -98,8 +98,8 @@
- DEBUG MODE! - (Disable before measuring performance.) +
diff --git a/js/deferredRender.js b/js/deferredRender.js index bb3edd4..308ca24 100644 --- a/js/deferredRender.js +++ b/js/deferredRender.js @@ -26,14 +26,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); - // TODO: Implement/test renderFullScreenQuad first - renderFullScreenQuad(R.progRed); - return; - } + // { // TODO: this block should be removed after testing renderFullScreenQuad + // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + // // TODO: Implement/test renderFullScreenQuad first + // renderFullScreenQuad(R.progRed); + // return; + // } R.pass_copy.render(state); @@ -44,8 +44,12 @@ } else { // * Deferred pass and postprocessing pass(es) // TODO: uncomment these - // R.pass_deferred.render(state); - // R.pass_post1.render(state); + R.pass_deferred.render(state); + if(cfg.bloom) + R.pass_blur.render(state); + if(cfg.sobel) + R.pass_sobel.render(state); + R.pass_post1.render(state); // OPTIONAL TODO: call more postprocessing passes, if any } @@ -57,21 +61,21 @@ R.pass_copy.render = function(state) { // * Bind the framebuffer R.pass_copy.fbo // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); + gl.bindFramebuffer(gl.FRAMEBUFFER,R.pass_copy.fbo); // * Clear screen using R.progClear // TODO: uncomment - // renderFullScreenQuad(R.progClear); + renderFullScreenQuad(R.progClear); // * Clear depth buffer to value 1.0 using gl.clearDepth and gl.clear // TODO: uncomment - // gl.clearDepth(1.0); - // gl.clear(gl.DEPTH_BUFFER_BIT); + gl.clearDepth(1.0); + gl.clear(gl.DEPTH_BUFFER_BIT); // * "Use" the program R.progCopy.prog // TODO: uncomment - // gl.useProgram(R.progCopy.prog); + gl.useProgram(R.progCopy.prog); // TODO: Go write code in glsl/copy.frag.glsl @@ -79,11 +83,11 @@ // * Upload the camera matrix m to the uniform R.progCopy.u_cameraMat // using gl.uniformMatrix4fv // TODO: uncomment - // gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); + gl.uniformMatrix4fv(R.progCopy.u_cameraMat, false, m); // * Draw the scene // TODO: uncomment - // drawScene(state); + drawScene(state); }; var drawScene = function(state) { @@ -101,17 +105,17 @@ R.pass_debug.render = function(state) { // * Unbind any framebuffer, so we can write to the screen // TODO: uncomment - // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); // * Bind/setup the debug "lighting" pass // * Tell shader which debug view to use // TODO: uncomment - // bindTexturesForLightPass(R.prog_Debug); - // gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); + bindTexturesForLightPass(R.prog_Debug); + gl.uniform1i(R.prog_Debug.u_debug, cfg.debugView); // * Render a fullscreen quad to perform shading on // TODO: uncomment - // renderFullScreenQuad(R.prog_Debug); + renderFullScreenQuad(R.prog_Debug); }; /** @@ -129,13 +133,14 @@ // * _ADD_ together the result of each lighting pass // Enable blending and use gl.blendFunc to blend with: - // color = 1 * src_color + 1 * dst_color - // Here is a wonderful demo of showing how blend function works: + // color = 1 * src_color + 1 * dst_color + // 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.enable(gl.BLEND); // gl.blendEquation( gl.FUNC_ADD ); - // gl.blendFunc(gl.ONE,gl.ONE); + gl.blendFunc(gl.ONE,gl.ONE); + // gl.blendFunc( gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA ); // * Bind/setup the ambient pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_Ambient); @@ -143,21 +148,47 @@ // * Bind/setup the Blinn-Phong pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_BlinnPhong_PointLight); - + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_viewDir, state.cameraPos.x, state.cameraPos.y, state.cameraPos.z); // TODO: add a loop here, over the values in R.lights, which sets the // uniforms R.prog_BlinnPhong_PointLight.u_lightPos/Col/Rad etc., // then does renderFullScreenQuad(R.prog_BlinnPhong_PointLight). - + if(cfg.toon) gl.uniform1i(R.prog_BlinnPhong_PointLight.u_toon, 1); + if (cfg.debugScissor) gl.enable(gl.SCISSOR_TEST); + for(var i = 0; i < R.NUM_LIGHTS; ++i){ + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightPos, R.lights[i].pos); + gl.uniform3fv(R.prog_BlinnPhong_PointLight.u_lightCol, R.lights[i].col); + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, R.lights[i].rad); + + if (cfg.debugScissor) { + //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); + if (cfg.debugScissor) { + gl.blendFunc(gl.SRC_ALPHA, gl.ONE); + gl.useProgram(R.progScissor.prog); + gl.uniform4f(R.progScissor.u_color, 1, 0, 0, 0.1); + renderFullScreenQuad(R.progScissor); + gl.blendFunc(gl.ONE, gl.ONE); + gl.useProgram(R.prog_BlinnPhong_PointLight.prog); + } + } + } else { + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + } + } + if (cfg.debugScissor) gl.disable(gl.SCISSOR_TEST); // TODO: In the lighting loop, use the scissor test optimization // Enable gl.SCISSOR_TEST, render all lights, then disable it. // // 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); // Disable blending so that it doesn't affect other code gl.disable(gl.BLEND); + R.old_colorTex = R.pass_deferred.colorTex; }; var bindTexturesForLightPass = function(prog) { @@ -175,6 +206,95 @@ gl.uniform1i(prog.u_depth, R.NUM_GBUFFERS); }; + + R.pass_blur.render = function(state) { + + /*Reference: "http://learnopengl.com/#!Advanced-Lighting/Bloom" */ + + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_blur.pingpongFBOs[0]); + + gl.useProgram(R.prog_hdr.prog) + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.old_colorTex); + gl.uniform1i(R.prog_hdr.u_color, 0); + renderFullScreenQuad(R.prog_hdr); + + var horizontal = true, first_iter = true; + var amount = 2; + gl.useProgram(R.prog_blur.prog) + gl.uniform2fv(R.prog_blur.u_tex_offset, [1.0 / width, 1.0/ height]); + + //testing one pass gauss blur + // gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_blur.pingpongFBOs[1]); + // gl.uniform1i(R.prog_blur.u_horizontal, horizontal); + // gl.activeTexture(gl.TEXTURE0); + // gl.bindTexture(gl.TEXTURE_2D, R.pass_blur.pingpongBuffer[0]); + // gl.uniform1i(R.prog_blur.u_color, 0); + // renderFullScreenQuad(R.prog_blur); + + for(var i = 0; i < amount; ++i) { + + //array of boolean doesnt seem to work. wtf! + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_blur.pingpongFBOs[(horizontal?1:0) + 1]); + gl.uniform1i(R.prog_blur.u_horizontal, horizontal); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_blur.pingpongBuffer[(horizontal?1:0)]); + gl.uniform1i(R.prog_blur.u_color, 0); + renderFullScreenQuad(R.prog_blur); + horizontal = !horizontal; + if (first_iter) + first_iter = false; + } + + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_blur.pingpongFBOs[3]); + + gl.useProgram(R.prog_blend.prog) + + // deferred pass + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.old_colorTex); + gl.uniform1i(R.prog_blend.u_scene, 0); + + // blur pass + gl.activeTexture(gl.TEXTURE1); + gl.bindTexture(gl.TEXTURE_2D, R.pass_blur.pingpongBuffer[2]); + gl.uniform1i(R.prog_blend.u_bloomBlur, 1); + + renderFullScreenQuad(R.prog_blend); + + R.old_colorTex = R.pass_blur.pingpongBuffer[3]; + + }; + + /** + * 'sobel filter' pass: Perform (sobel) pass of post-processing + */ + R.pass_sobel.render = function(state) { + // * Unbind any existing framebuffer (if there are no more passes) + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_sobel.fbo); + + // * Bind the postprocessing shader program + gl.useProgram(R.progSobel.prog); + + // * Bind the deferred pass's color output as a texture input + // Set gl.TEXTURE0 as the gl.activeTexture unit + // TODO: uncomment + gl.activeTexture(gl.TEXTURE0); + + // Bind the TEXTURE_2D, R.pass_deferred.colorTex to the active texture unit + // TODO: uncomment + gl.bindTexture(gl.TEXTURE_2D, R.old_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.progSobel.u_color, 0); + gl.uniform2fv(R.progSobel.u_tex_offset, [1.0 / width, 1.0/ height]); + + // * Render a fullscreen quad to perform shading on + renderFullScreenQuad(R.progSobel); + R.old_colorTex = R.pass_sobel.colorTex; + }; + /** * 'post1' pass: Perform (first) pass of post-processing */ @@ -192,10 +312,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.old_colorTex); // gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); // Configure the R.progPost1.u_color uniform to point at texture unit 0 @@ -230,12 +351,12 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER,vbo); + gl.bindBuffer(gl.ARRAY_BUFFER,vbo); // Upload the positions array to the currently-bound array buffer // using gl.bufferData in static draw mode. // TODO: uncomment - // gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); + gl.bufferData(gl.ARRAY_BUFFER,positions,gl.STATIC_DRAW); }; return function(prog) { @@ -249,21 +370,21 @@ // Bind the VBO as the gl.ARRAY_BUFFER // TODO: uncomment - // gl.bindBuffer(gl.ARRAY_BUFFER, vbo); + gl.bindBuffer(gl.ARRAY_BUFFER, vbo); // Enable the bound buffer as the vertex attrib array for // prog.a_position, using gl.enableVertexAttribArray // TODO: uncomment - // gl.enableVertexAttribArray(prog.a_position); + gl.enableVertexAttribArray(prog.a_position); // Use gl.vertexAttribPointer to tell WebGL the type/layout for // prog.a_position's access pattern. // TODO: uncomment - // gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); + gl.vertexAttribPointer(prog.a_position, 3, gl.FLOAT, gl.FALSE, 0, 0); // Use gl.drawArrays (or gl.drawElements) to draw your quad. // TODO: uncomment - // gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); + gl.drawArrays(gl.TRIANGLE_STRIP, 0, 4); // Unbind the array buffer. gl.bindBuffer(gl.ARRAY_BUFFER, null); diff --git a/js/deferredSetup.js b/js/deferredSetup.js index 65136e0..c0ab964 100644 --- a/js/deferredSetup.js +++ b/js/deferredSetup.js @@ -6,9 +6,15 @@ R.pass_debug = {}; R.pass_deferred = {}; R.pass_post1 = {}; + R.pass_blur = {}; + R.pass_hdr = {}; + R.pass_blend = {}; + R.pass_bloom = {}; + R.pass_sobel = {}; R.lights = []; + R.old_colorTex; - R.NUM_GBUFFERS = 4; + R.NUM_GBUFFERS = 3; /** * Set up the deferred pipeline framebuffer objects and textures. @@ -18,6 +24,10 @@ loadAllShaderPrograms(); R.pass_copy.setup(); R.pass_deferred.setup(); + R.pass_blur.setup(); + R.pass_sobel.setup(); + + // R.pass_blend.setup(); }; // TODO: Edit if you want to change the light initial positions @@ -98,6 +108,37 @@ gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; + R.pass_sobel.setup = function() { + // * Create the FBO + R.pass_sobel.fbo = gl.createFramebuffer(); + // * Create, bind, and store a single color target texture for the FBO + R.pass_sobel.colorTex = createAndBindColorTargetTexture( + R.pass_sobel.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + + // * Check for framebuffer errors + abortIfFramebufferIncomplete(R.pass_sobel.fbo); + // * Tell the WEBGL_draw_buffers extension which FBO attachments are + // being used. (This extension allows for multiple render targets.) + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + }; + + R.pass_blur.setup = function() { + var pingpongFBOs = []; + var pingpongBuffer = []; + for(var i = 0; i < 4; ++i){ + pingpongFBOs.push(gl.createFramebuffer()); + pingpongBuffer.push(createAndBindColorTargetTexture(pingpongFBOs[i], gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL)); + abortIfFramebufferIncomplete(pingpongFBOs[i]); + + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + } + R.pass_blur.pingpongFBOs = pingpongFBOs; + R.pass_blur.pingpongBuffer = pingpongBuffer; + }; + /** * Loads all of the shader programs used in the pipeline. */ @@ -125,6 +166,15 @@ R.progRed = { prog: prog }; }); + loadShaderProgram(gl, 'glsl/quad.vert.glsl', 'glsl/scissor.frag.glsl', + function(prog) { + // Create an object to hold info about this shader program + R.progScissor = { + prog: prog, + u_color: gl.getUniformLocation(prog, 'u_color') + }; + }); + loadShaderProgram(gl, 'glsl/quad.vert.glsl', 'glsl/clear.frag.glsl', function(prog) { // Create an object to hold info about this shader program @@ -141,6 +191,8 @@ 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_viewDir = gl.getUniformLocation(p.prog, 'u_viewDir'); + p.u_toon = gl.getUniformLocation(p.prog, 'u_toon'); R.prog_BlinnPhong_PointLight = p; }); @@ -150,12 +202,40 @@ R.prog_Debug = p; }); + loadPostProgram('blur', function(p) { + + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_horizontal = gl.getUniformLocation(p.prog, 'u_horizontal'); + p.u_tex_offset = gl.getUniformLocation(p.prog, 'u_tex_offset'); + R.prog_blur = p; + }); + + loadPostProgram('hdr', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + + R.prog_hdr = p; + }); + + loadPostProgram('blend', function(p) { + p.u_scene = gl.getUniformLocation(p.prog, 'u_scene'); + p.u_bloomBlur = gl.getUniformLocation(p.prog, 'u_bloomBlur'); + + R.prog_blend = p; + }); + loadPostProgram('one', function(p) { p.u_color = gl.getUniformLocation(p.prog, 'u_color'); // Save the object into this variable for access later R.progPost1 = p; }); + loadPostProgram('sobel', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_tex_offset = gl.getUniformLocation(p.prog, 'u_tex_offset'); + // Save the object into this variable for access later + R.progSobel = p; + }); + // TODO: If you add more passes, load and set up their shader programs. }; diff --git a/js/framework.js b/js/framework.js index 4f944ee..f6eab61 100644 --- a/js/framework.js +++ b/js/framework.js @@ -123,7 +123,7 @@ var width, height; }); // var glTFURL = 'models/glTF-duck/duck.gltf'; - var glTFURL = 'models/glTF-sponza-kai-fix/sponza.gltf'; + var glTFURL = 'models/gltf-sponza-kai-fix/sponza.gltf'; var glTFLoader = new MinimalGLTFLoader.glTFLoader(gl); glTFLoader.loadGLTF(glTFURL, function (glTF) { var curScene = glTF.scenes[glTF.defaultScene]; @@ -187,8 +187,8 @@ var width, height; gl.texParameteri(target, gl.TEXTURE_MAG_FILTER, magFilter); gl.texParameteri(target, gl.TEXTURE_WRAP_S, wrapS); gl.texParameteri(target, gl.TEXTURE_WRAP_T, wrapT); - if (minFilter == gl.NEAREST_MIPMAP_NEAREST || - minFilter == gl.NEAREST_MIPMAP_LINEAR || + if (minFilter == gl.NEAREST_MIPMAP_NEAREST || + minFilter == gl.NEAREST_MIPMAP_LINEAR || minFilter == gl.LINEAR_MIPMAP_NEAREST || minFilter == gl.LINEAR_MIPMAP_LINEAR ) { gl.generateMipmap(target); @@ -245,7 +245,7 @@ var width, height; uvInfo: {size: uvInfo.size, type: uvInfo.type, stride: uvInfo.stride, offset: uvInfo.offset}, // specific textures temp test - colmap: webGLTextures[colorTextureName].texture, + colmap: webGLTextures[colorTextureName].texture, normap: webGLTextures[normalTextureName].texture }); @@ -254,7 +254,7 @@ var width, height; } - + }); diff --git a/js/ui.js b/js/ui.js index abd6119..49ad34b 100644 --- a/js/ui.js +++ b/js/ui.js @@ -7,7 +7,9 @@ var cfg; // TODO: Define config fields and defaults here this.debugView = -1; this.debugScissor = false; - this.enableEffect0 = false; + this.bloom = false; + this.sobel = false; + this.toon = false; }; var init = function() { @@ -19,16 +21,20 @@ var cfg; 'None': -1, '0 Depth': 0, '1 Position': 1, - '2 Geometry normal': 2, - '3 Color map': 3, - '4 Normal map': 4, - '5 Surface normal': 5 + // '2 Geometry normal': 2, + // '3 Color map': 3, + // '4 Normal map': 4, + '2 Surface normal': 2, + '3 Color Map': 3 }); gui.add(cfg, 'debugScissor'); - var eff0 = gui.addFolder('EFFECT NAME HERE'); + var eff0 = gui.addFolder('Post processing'); eff0.open(); - eff0.add(cfg, 'enableEffect0'); + eff0.add(cfg, 'bloom'); + eff0.add(cfg, 'toon'); + eff0.add(cfg, 'sobel'); + // TODO: add more effects toggles and parameters here }; diff --git a/js/util.js b/js/util.js index 91a910e..2801bbe 100644 --- a/js/util.js +++ b/js/util.js @@ -93,7 +93,7 @@ window.readyModelForDraw = function(prog, m) { } 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); @@ -160,6 +160,55 @@ window.getScissorForLight = (function() { }; })(); +window.getBetterScissorForLight = (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); + 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; + + // front bottom-left corner of sphere's bounding cube + b.fromArray(l.pos); + b.w = 1; + b.applyMatrix4(view); + b.x = a.x + l.rad * 2.0; + b.y = a.y + l.rad * 2.0; + b.z = a.z; + a.applyMatrix4(proj); + a.divideScalar(a.w); + 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)); + + if (maxpt.x < -1 || 1 < minpt.x || + maxpt.y < -1 || 1 < minpt.y) { + return null; + } + + minpt.addScalar(1.0); minpt.multiplyScalar(0.5); + maxpt.addScalar(1.0); maxpt.multiplyScalar(0.5); + + ret[0] = Math.round(width * minpt.x); + ret[1] = Math.round(height * minpt.y); + ret[2] = Math.round(width * (maxpt.x - minpt.x)); + ret[3] = Math.round(height * (maxpt.y - minpt.y)); + return ret; + }; +})(); + + window.abortIfFramebufferIncomplete = function(fbo) { gl.bindFramebuffer(gl.FRAMEBUFFER, fbo); var fbstatus = gl.checkFramebufferStatus(gl.FRAMEBUFFER);