diff --git a/INSTRUCTION.md b/INSTRUCTION.md index a7a4b2d..bf01a90 100644 --- a/INSTRUCTION.md +++ b/INSTRUCTION.md @@ -303,7 +303,7 @@ UI is accessible anywhere as `cfg.enableEffect0`, etc. **Pass 3:** Performs post-processing. * `quad.vert.glsl`, `post/one.frag.glsl` -* Takes `pass_deferred.colorTex` as a texture input `u_color`. +* Takes `pass_BlinnPhong_PointLight.colorTex` as a texture input `u_color`. * Renders directly to the screen if there are no additional passes. More passes may be added for additional effects (e.g. combining bloom with diff --git a/Performance.txt b/Performance.txt new file mode 100644 index 0000000..416bab6 --- /dev/null +++ b/Performance.txt @@ -0,0 +1,15 @@ +use 2 gbuffer, calculate world position in shader: +33 + +compute normal in copy: +29 + +Scissor Test gl.scissor: +24 + +Scissor Test NULL: +16 + +Without Scissor Test: +13 + diff --git a/README.md b/README.md index 25002db..cd5cea6 100644 --- a/README.md +++ b/README.md @@ -3,27 +3,123 @@ 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) +* Kaixiang Miao +* Tested on: Windows 7, i7-3630QM @ 2.40GHz 8GB, GTX 660M 2GB (Lenovo Y580 laptop, personal computer) ### Live Online -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +[![](img/index.jpg)](https://immiao.github.io/Project5-WebGL-Deferred-Shading-with-glTF/) ### Demo Video/GIF -[![](img/video.png)](TODO) +![](img/72.gif) -### (TODO: Your README) +### Features -*DO NOT* leave the README to the last minute! It is a crucial part of the -project, and we will not be able to grade you without a good README. +* Blinn-Phong shading model +* Optimized lights rendering by scissor test +* Optimized by using less G-Buffers (reduce from 4 to 2) +* Toon shading (ramp shading && edge detection) +* Bloom effect (Gaussian blur && ping-pong frame buffers) +* Screen space motion blur -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! +### G-Buffers + + + + + + + + + + + +
depth buffergeometry normalnormal (tangent space)
+### Effects + + + + + + + + + +
non-toon shadingtoon shading
+ + + + + + + + + +
no bloombloom
+ + + + + + + + +
motion blur
+ +#### Scissor Test + + + + + + + +
Scissor Area
+ +### Analysis + +* Using less G-Buffers + * calculate the world-space normal in the copy stage + * convert the screen-space point to world-space point using the depth value in the depth buffer and the camera transformation matrix + +FPS is increased: + ![table1](img/table1.jpg) + +* Scissor Test + +While rendering the point-light effect, it's unnecessary to render all screen since point lights have their own radius. The part which is outside of the circle of the point lights can be cut. + +Scissor Test increases the FPS significantly. + +Without scissor test, the average FPS is **13**. + +If only discarding the lights whose scissor is off the screen, the average FPS is increased to **16**. + +If we perform scissor test to cut the parts which are out of the rectangular, the average FPS is **24**. + +* Toon shading + +Implemented with ramp shading and edge detection. + +Reference: [Toon shading](http://www.lighthouse3d.com/tutorials/glsl-12-tutorial/toon-shading-version-i/) + +* Bloom effect + +Bloom gives noticeable visual cues about the brightness of objects as bloom tends to give the illusion objects are really bright. When done in a subtle fashion (which some games drastically fail to do) bloom significantly boosts the lighting of the scene and allows for a large range of dramatic effects. + +The idea is to extract the bright color and do a 2-pass Gaussian blur first, then merge the blurred image to the scene, which means we should use framebuffer to temporarily store the color. + +Reference: [Advanced Lighting: Bloom](http://learnopengl.com/#!Advanced-Lighting/Bloom) + +* Screen space motion blur + +store the previous camera transformation matrix + +Reference: [Motion Blur as a Post-Processing Effect](http://http.developer.nvidia.com/GPUGems3/gpugems3_ch27.html) + ### 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..9bf378a 100644 --- a/glsl/copy.frag.glsl +++ b/glsl/copy.frag.glsl @@ -10,11 +10,26 @@ 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. + vec3 normap = texture2D(u_normap, v_uv).xyz; + vec3 nor = applyNormalMap(v_normal, normap); // this gives you the idea - // gl_FragData[0] = vec4( v_position, 1.0 ); + //gl_FragData[2] = vec4( v_position, 1.0 ); + gl_FragData[0] = vec4( nor, 1.0); + gl_FragData[1] = texture2D(u_colmap, v_uv); + + //gl_FragData[3] = texture2D(u_normap, v_uv); + //gl_FragData[4] = } diff --git a/glsl/deferred/ambient.frag.glsl b/glsl/deferred/ambient.frag.glsl index 1fd4647..af8c8de 100644 --- a/glsl/deferred/ambient.frag.glsl +++ b/glsl/deferred/ambient.frag.glsl @@ -3,7 +3,7 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 2 uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; @@ -13,8 +13,7 @@ varying vec2 v_uv; 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 gb2 = texture2D(u_gbufs[2], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables @@ -23,5 +22,8 @@ void main() { return; } - gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + float ambient = 0.35; + //gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this + //gl_FragColor = vec4(1, 1, 1, 1); + gl_FragColor = vec4(gb1.rgb * ambient, 1.0); } diff --git a/glsl/deferred/blinnphong-pointlight.frag.glsl b/glsl/deferred/blinnphong-pointlight.frag.glsl index b24a54a..f65fe02 100644 --- a/glsl/deferred/blinnphong-pointlight.frag.glsl +++ b/glsl/deferred/blinnphong-pointlight.frag.glsl @@ -2,7 +2,7 @@ precision highp float; precision highp int; -#define NUM_GBUFFERS 4 +#define NUM_GBUFFERS 2 uniform vec3 u_lightCol; uniform vec3 u_lightPos; @@ -10,8 +10,14 @@ uniform float u_lightRad; uniform sampler2D u_gbufs[NUM_GBUFFERS]; uniform sampler2D u_depth; +uniform int u_isToon; + varying vec2 v_uv; +uniform vec3 u_cameraPos; +uniform mat4 u_invCrtCameraMat; +//uniform sampler2D u_tex; + vec3 applyNormalMap(vec3 geomnor, vec3 normap) { normap = normap * 2.0 - 1.0; vec3 up = normalize(vec3(0.001, 1, 0.001)); @@ -21,19 +27,82 @@ vec3 applyNormalMap(vec3 geomnor, vec3 normap) { } void main() { + vec3 result = vec3(0, 0, 0); + + float zOverW = texture2D(u_depth, v_uv).x * 2.0 - 1.0; + vec4 HH = vec4(v_uv.x * 2.0 - 1.0, v_uv.y * 2.0 - 1.0, zOverW, 1.0); + vec4 DD = u_invCrtCameraMat * HH; + vec4 worldPos = DD / DD.w; + 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 gb2 = texture2D(u_gbufs[2], v_uv); + //vec4 gb3 = texture2D(u_gbufs[3], v_uv); float depth = texture2D(u_depth, v_uv).x; // TODO: Extract needed properties from the g-buffers into local variables + //vec3 pos = gb0.xyz; + vec3 pos = worldPos.xyz; + //vec3 pos = vec3(0, 0, 0); + //vec3 geomnor = gb1.xyz; + vec3 colmap = gb1.rgb; + //vec3 normap = gb3.xyz; + //vec3 nor = applyNormalMap(geomnor, normap); + vec3 nor = gb0.xyz; + + + vec3 L = normalize(u_lightPos - pos); + vec3 V = normalize(u_cameraPos - pos); + vec3 H = normalize(V + L); + float distance = length(u_lightPos - pos); + float attenuation = max(0.0, 1.0 - distance / u_lightRad); + float intensity = dot(nor, L); + vec3 diffuseColor; + vec3 specularColor = u_lightCol * pow(max(0.0, dot(nor, H)), 200.0); + if (u_isToon == 1) + { + int edgeDetection = dot(V, nor) > 0.1 ? 1 : 0; + if (intensity > 0.9) + { + diffuseColor = colmap * 0.9 * u_lightCol * 0.9; + } + else if (intensity > 0.7) + diffuseColor = colmap * 0.7 * u_lightCol * 0.7; + else if (intensity > 0.4) + { + diffuseColor = colmap * 0.4 * u_lightCol * 0.4; + } + else + { + diffuseColor = colmap * 0.1 * u_lightCol * 0.1; + } + + if (pow(max(0.0, dot(nor, H)), 200.0) < 0.4) + specularColor = vec3(0, 0, 0); + if (edgeDetection == 0) + { + diffuseColor = vec3(0, 0, 0); + specularColor = vec3(0, 0, 0); + } + } + else + { + diffuseColor = u_lightCol * clamp(intensity, 0.0, 1.0); + //specularColor = u_lightCol * pow(max(0.0, dot(nor, H)), 200.0); + } + // 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 + result += (diffuseColor + specularColor) * attenuation; + gl_FragColor = vec4(result, 1.0); + //gl_FragColor = vec4(gb0.xyz, 1.0); + //gl_FragColor = worldPos; + //gl_FragColor = vec4(specularColor, 1.0); + //gl_FragColor = vec4(u_cameraPos, 1.0); + //gl_FragColor = vec4(attenuation, 0, 0, 1.0); + //gl_FragColor = vec4(worldPos.y, 0, 0, 1.0); } diff --git a/glsl/deferred/debug.frag.glsl b/glsl/deferred/debug.frag.glsl index 007466f..c135b87 100644 --- a/glsl/deferred/debug.frag.glsl +++ b/glsl/deferred/debug.frag.glsl @@ -33,20 +33,23 @@ void main() { 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 (gb2.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } // 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(pos, 1.0); } else if (u_debug == 2) { - // gl_FragColor = vec4(abs(geomnor), 1.0); + gl_FragColor = vec4(abs(geomnor), 1.0); } else if (u_debug == 3) { - // gl_FragColor = vec4(colmap, 1.0); + gl_FragColor = vec4(colmap, 1.0); } else if (u_debug == 4) { - // gl_FragColor = vec4(normap, 1.0); + gl_FragColor = vec4(normap, 1.0); } else if (u_debug == 5) { - // gl_FragColor = vec4(abs(nor), 1.0); + gl_FragColor = vec4(abs(nor), 1.0); } else { gl_FragColor = vec4(1, 0, 1, 1); } diff --git a/glsl/post/bloomblur.frag.glsl b/glsl/post/bloomblur.frag.glsl new file mode 100644 index 0000000..5733ab3 --- /dev/null +++ b/glsl/post/bloomblur.frag.glsl @@ -0,0 +1,57 @@ +#version 100 +//#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; +#define w0 0.227027 +#define w1 0.1945946 +#define w2 0.1216216 +#define w3 0.054054 +#define w4 0.016216 + +uniform sampler2D u_color; + +varying vec2 v_uv; + +uniform int u_horizontal; +uniform vec2 u_textureSize; +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +// uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, +// 0.054054, 0.016216); + +void main() { + vec2 tex_offset = 1.0 / u_textureSize; + vec4 color = texture2D(u_color, v_uv); + vec3 result = color.rgb * w0; + + if (color.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } + if (u_horizontal == 1) + { + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 1.0, 0.0)).rgb * w1; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 1.0, 0.0)).rgb * w1; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 2.0, 0.0)).rgb * w2; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 2.0, 0.0)).rgb * w2; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 3.0, 0.0)).rgb * w3; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 3.0, 0.0)).rgb * w3; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 4.0, 0.0)).rgb * w4; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 4.0, 0.0)).rgb * w4; + } + else + { + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 1.0)).rgb * w1; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 1.0)).rgb * w1; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 2.0)).rgb * w2; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 2.0)).rgb * w2; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 3.0)).rgb * w3; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 3.0)).rgb * w3; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 4.0)).rgb * w4; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 4.0)).rgb * w4; + } + + gl_FragColor = vec4(result, 1.0); + //gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + //gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} diff --git a/glsl/post/bloommerge.frag.glsl b/glsl/post/bloommerge.frag.glsl new file mode 100644 index 0000000..c1ca1dd --- /dev/null +++ b/glsl/post/bloommerge.frag.glsl @@ -0,0 +1,52 @@ +#version 100 +//#extension GL_EXT_draw_buffers: enable +precision highp float; +precision highp int; + +uniform sampler2D u_color; + +varying vec2 v_uv; + +uniform int u_horizontal; +uniform vec2 u_textureSize; +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +// uniform float weight[5] = float[] (0.227027, 0.1945946, 0.1216216, +// 0.054054, 0.016216); + +void main() { + vec2 tex_offset = 1.0 / u_textureSize; + vec4 color = texture2D(u_color, v_uv); + vec3 result = color.rgb * w0; + + if (color.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } + if (u_horizontal == 1) + { + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 1.0, 0.0)).rgb * w1; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 1.0, 0.0)).rgb * w1; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 2.0, 0.0)).rgb * w2; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 2.0, 0.0)).rgb * w2; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 3.0, 0.0)).rgb * w3; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 3.0, 0.0)).rgb * w3; + result += texture2D(u_color, v_uv + vec2(tex_offset.x * 4.0, 0.0)).rgb * w4; + result += texture2D(u_color, v_uv - vec2(tex_offset.x * 4.0, 0.0)).rgb * w4; + } + else + { + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 1.0)).rgb * w1; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 1.0)).rgb * w1; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 2.0)).rgb * w2; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 2.0)).rgb * w2; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 3.0)).rgb * w3; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 3.0)).rgb * w3; + result += texture2D(u_color, v_uv + vec2(0.0, tex_offset.y * 4.0)).rgb * w4; + result += texture2D(u_color, v_uv - vec2(0.0, tex_offset.y * 4.0)).rgb * w4; + } + + //gl_FragColor = vec4(result, 1.0); + gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); + //gl_FragColor = vec4(0.0, 0.0, 0.0, 0.0); +} diff --git a/glsl/post/bloomthreshold.frag.glsl b/glsl/post/bloomthreshold.frag.glsl new file mode 100644 index 0000000..fb90f81 --- /dev/null +++ b/glsl/post/bloomthreshold.frag.glsl @@ -0,0 +1,23 @@ +#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; + } + + float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722)); + if (brightness < 1.0) + color = vec4(0.0, 0.0, 0.0, 1.0); + gl_FragColor = color; +} diff --git a/glsl/post/motionblur.frag.glsl b/glsl/post/motionblur.frag.glsl new file mode 100644 index 0000000..7b1655f --- /dev/null +++ b/glsl/post/motionblur.frag.glsl @@ -0,0 +1,47 @@ +#version 100 +precision highp float; +precision highp int; + +uniform sampler2D u_color; +uniform sampler2D u_depth; + +varying vec2 v_uv; + +uniform mat4 u_crtCameraMat; +uniform mat4 u_invCrtCameraMat; +uniform mat4 u_preCameraMat; + +const vec4 SKY_COLOR = vec4(0.01, 0.14, 0.42, 1.0); + +void main() { + float zOverW = texture2D(u_depth, v_uv).x * 2.0 - 1.0; + vec4 HH = vec4(v_uv.x * 2.0 - 1.0, v_uv.y * 2.0 - 1.0, zOverW, 1.0); + vec4 DD = u_invCrtCameraMat * HH; + vec4 worldPos = DD / DD.w; + vec4 prePos = u_preCameraMat * worldPos; + prePos /= prePos.w; + vec2 velocity = ((HH - prePos) * 0.1).xy; + + vec4 color = texture2D(u_color, v_uv); + vec4 tempResult = color; + if (color.a == 0.0) { + gl_FragColor = SKY_COLOR; + return; + } + + vec2 texCoord = v_uv + velocity; + //int g_numSamples = 4; + + for (int i = 1; i < 16; i++) + { + vec4 currentColor = texture2D(u_color, texCoord); + color += currentColor; + texCoord += velocity; + } + + //gl_FragColor = vec4(velocity.x, 0.0, 0.0, 1.0); + gl_FragColor = color / 16.0; + //gl_FragColor = tempResult; + //gl_FragColor = texture2D(u_color, v_uv); + //gl_FragColor = vec4(worldPos.y, 0.0, 0.0, 1.0); +} diff --git a/glsl/red.frag.glsl b/glsl/red.frag.glsl index f8ef1ec..737303a 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(0.1, 0, 0, 0); } diff --git a/img/71.gif b/img/71.gif new file mode 100644 index 0000000..5680f01 Binary files /dev/null and b/img/71.gif differ diff --git a/img/72.gif b/img/72.gif new file mode 100644 index 0000000..9d146db Binary files /dev/null and b/img/72.gif differ diff --git a/img/bloom.jpg b/img/bloom.jpg new file mode 100644 index 0000000..937fc92 Binary files /dev/null and b/img/bloom.jpg differ diff --git a/img/depth.jpg b/img/depth.jpg new file mode 100644 index 0000000..69ee3ef Binary files /dev/null and b/img/depth.jpg differ diff --git a/img/geometryNormal.jpg b/img/geometryNormal.jpg new file mode 100644 index 0000000..038bb46 Binary files /dev/null and b/img/geometryNormal.jpg differ diff --git a/img/index.jpg b/img/index.jpg new file mode 100644 index 0000000..2f04e7b Binary files /dev/null and b/img/index.jpg differ diff --git a/img/material.jpg b/img/material.jpg new file mode 100644 index 0000000..9ab17dd Binary files /dev/null and b/img/material.jpg differ diff --git a/img/nonbloom.jpg b/img/nonbloom.jpg new file mode 100644 index 0000000..9da1994 Binary files /dev/null and b/img/nonbloom.jpg differ diff --git a/img/nontoon.jpg b/img/nontoon.jpg new file mode 100644 index 0000000..f7c73ce Binary files /dev/null and b/img/nontoon.jpg differ diff --git a/img/normalTangentSpace.jpg b/img/normalTangentSpace.jpg new file mode 100644 index 0000000..0afd088 Binary files /dev/null and b/img/normalTangentSpace.jpg differ diff --git a/img/scissor.jpg b/img/scissor.jpg new file mode 100644 index 0000000..44e8910 Binary files /dev/null and b/img/scissor.jpg differ diff --git a/img/table1.jpg b/img/table1.jpg new file mode 100644 index 0000000..f9d346a Binary files /dev/null and b/img/table1.jpg differ diff --git a/img/toon.jpg b/img/toon.jpg new file mode 100644 index 0000000..eee7542 Binary files /dev/null and b/img/toon.jpg differ diff --git a/js/deferredRender.js b/js/deferredRender.js index bb3edd4..3f020d3 100644 --- a/js/deferredRender.js +++ b/js/deferredRender.js @@ -10,7 +10,10 @@ !R.prog_Ambient || !R.prog_BlinnPhong_PointLight || !R.prog_Debug || - !R.progPost1)) { + !R.progPost1 || + !R.progMotionBlur || + !R.progBloomThreshold || + !R.progBloomBlur)) { console.log('waiting for programs to load...'); return; } @@ -28,12 +31,12 @@ // CHECKITOUT: START HERE! You can even uncomment this: //debugger; - { // TODO: this block should be removed after testing renderFullScreenQuad - gl.bindFramebuffer(gl.FRAMEBUFFER, null); - // TODO: Implement/test renderFullScreenQuad first - renderFullScreenQuad(R.progRed); - return; - } + // { // TODO: this block should be removed after testing renderFullScreenQuad + // gl.bindFramebuffer(gl.FRAMEBUFFER, null); + // // TODO: Implement/test renderFullScreenQuad first + // renderFullScreenQuad(R.progRed); + // return; + // } R.pass_copy.render(state); @@ -41,14 +44,35 @@ // Do a debug render instead of a regular render // Don't do any post-processing in debug mode R.pass_debug.render(state); - } else { + return; + } + if (cfg && cfg.debugScissor) { + + R.pass_deferred.render(state, true, 0); + //R.pass_motionblur.render(state); + //R.pass_post1.render(state); + + } else if (cfg && cfg.Toonshading) { + R.pass_deferred.render(state, false, 1); + //R.pass_motionblur.render(state); + + + }else { // * Deferred pass and postprocessing pass(es) // TODO: uncomment these - // R.pass_deferred.render(state); - // R.pass_post1.render(state); - + R.pass_deferred.render(state, false, 0); + + //R.pass_post1.render(state); // OPTIONAL TODO: call more postprocessing passes, if any } + + R.pass_bloomThreshold.render(state); + R.pass_bloomBlur.render(state); + if (cfg && cfg.Bloom) + R.pass_motionblur.render(state, R.pass_bloomBlur.colorTex[0]); + else + R.pass_motionblur.render(state, R.pass_deferred.colorTex); + R.pass_post1.render(state); }; /** @@ -57,21 +81,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 +103,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) { @@ -101,23 +126,24 @@ 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); }; - /** * 'deferred' pass: Add lighting results for each individual light */ - R.pass_deferred.render = function(state) { + R.pass_deferred.render = function(state, isShowScissor, isToon) { + + //var crtCameraMat = state.cameraMat.elements; // * Bind R.pass_deferred.fbo to write into for later postprocessing gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_deferred.fbo); @@ -133,9 +159,9 @@ // Here is a wonderful demo of showing how blend function works: // http://mrdoob.github.io/webgl-blendfunctions/blendfunc.html // TODO: uncomment - // gl.enable(gl.BLEND); - // gl.blendEquation( gl.FUNC_ADD ); - // gl.blendFunc(gl.ONE,gl.ONE); + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc(gl.ONE,gl.ONE); // * Bind/setup the ambient pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_Ambient); @@ -144,8 +170,9 @@ // * Bind/setup the Blinn-Phong pass, and render using fullscreen quad bindTexturesForLightPass(R.prog_BlinnPhong_PointLight); + // TODO: add a loop here, over the values in R.lights, which sets the - // uniforms R.prog_BlinnPhong_PointLight.u_lightPos/Col/Rad etc., + // uniforms R.prog_BlinnPhong_PointLight.u_lightPos/Col etc., // then does renderFullScreenQuad(R.prog_BlinnPhong_PointLight). // TODO: In the lighting loop, use the scissor test optimization @@ -153,11 +180,55 @@ // // 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); + // glUseProgram is applied in bindTexturesForLightPass + gl.uniform1i(R.prog_BlinnPhong_PointLight.u_isToon, isToon); + var mat = new THREE.Matrix4(); + gl.uniformMatrix4fv(R.prog_BlinnPhong_PointLight.u_invCrtCameraMat, false, mat.getInverse(state.cameraMat).elements); + + //console.log(mat.getInverse(state.cameraMat)); + gl.enable(gl.SCISSOR_TEST); + + if (isShowScissor) + { + for (var i = 0; i < R.NUM_LIGHTS; i++) { + //console.log(i, R.lights[]) + //console.log(i, R.lights[i].col[0], R.lights[i].col[1], R.lights[i].col[2]); + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + if (sc == null) continue; + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + //console.log(sc[1]); + //gl.viewport(sc[0], sc[1], sc[2], sc[3]); + + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightPos, R.lights[i].pos[0], R.lights[i].pos[1], R.lights[i].pos[2]); + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightCol, R.lights[i].col[0], R.lights[i].col[1], R.lights[i].col[2]); + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, R.lights[i].rad); + //var cameraPs = state.cameraPos.elements; + //console.log(); + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_cameraPos, state.cameraPos.getComponent(0), state.cameraPos.getComponent(1), state.cameraPos.getComponent(2)); + //gl.uniform + renderFullScreenQuad(R.progRed); + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + } + } else { + for (var i = 0; i < R.NUM_LIGHTS; i++) { + var sc = getScissorForLight(state.viewMat, state.projMat, R.lights[i]); + if (sc == null) continue; + gl.scissor(sc[0], sc[1], sc[2], sc[3]); + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightPos, R.lights[i].pos[0], R.lights[i].pos[1], R.lights[i].pos[2]); + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_lightCol, R.lights[i].col[0], R.lights[i].col[1], R.lights[i].col[2]); + gl.uniform1f(R.prog_BlinnPhong_PointLight.u_lightRad, R.lights[i].rad); + gl.uniform3f(R.prog_BlinnPhong_PointLight.u_cameraPos, state.cameraPos.getComponent(0), state.cameraPos.getComponent(1), state.cameraPos.getComponent(2)); + renderFullScreenQuad(R.prog_BlinnPhong_PointLight); + } + + } + gl.disable(gl.SCISSOR_TEST); // Disable blending so that it doesn't affect other code gl.disable(gl.BLEND); + }; var bindTexturesForLightPass = function(prog) { @@ -173,6 +244,11 @@ gl.activeTexture(gl['TEXTURE' + R.NUM_GBUFFERS]); gl.bindTexture(gl.TEXTURE_2D, R.pass_copy.depthTex); gl.uniform1i(prog.u_depth, R.NUM_GBUFFERS); + + // sponza texture??? + // gl.activeTexture(gl['TEXTURE' + (R.NUM_GBUFFERS + 1)]); + // gl.bindTexture(gl.TEXTURE_2D, webGLTextures[0].texture); + // gl.uniform1i(prog.u_tex, R.NUM_GBUFFERS + 1); }; /** @@ -192,11 +268,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_motionblur.colorTex); // Configure the R.progPost1.u_color uniform to point at texture unit 0 gl.uniform1i(R.progPost1.u_color, 0); @@ -205,6 +281,98 @@ renderFullScreenQuad(R.progPost1); }; + R.pass_motionblur.render = function(state, colortex) { + + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_motionblur.fbo); + + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); + + gl.useProgram(R.progMotionBlur.prog); + + var mat = new THREE.Matrix4(); + var crtMat = state.cameraMat; + gl.uniformMatrix4fv(R.progMotionBlur.u_preCameraMat, false, R.preCameraMat.elements); + gl.uniformMatrix4fv(R.progMotionBlur.u_crtCameraMat, false, crtMat.elements); + gl.uniformMatrix4fv(R.progMotionBlur.u_invCrtCameraMat, false, mat.getInverse(crtMat).elements); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, colortex); + //console.log(R.progMotionBlur.u_color); + gl.uniform1i(R.progMotionBlur.u_color, 0); + + gl.activeTexture(gl['TEXTURE' + R.NUM_GBUFFERS]); + gl.bindTexture(gl.TEXTURE_2D, R.pass_copy.depthTex); + gl.uniform1i(R.progMotionBlur.u_depth, R.NUM_GBUFFERS); + + renderFullScreenQuad(R.progMotionBlur); + R.preCameraMat = crtMat.clone(); + }; + + R.pass_bloomThreshold.render = function(state) { + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_bloomThreshold.fbo); + + gl.clearColor(0.0, 0.0, 0.0, 0.0); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); + + gl.useProgram(R.progBloomThreshold.prog); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + gl.uniform1i(R.progBloomThreshold.u_color, 0); + + renderFullScreenQuad(R.progBloomThreshold); + }; + + + R.pass_bloomBlur.render = function(state) { + gl.useProgram(R.progBloomBlur.prog); + + // first pass + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_bloomBlur.fbo[0]); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_bloomThreshold.colorTex); + gl.uniform1i(R.progBloomBlur.u_horizontal, 1); + gl.uniform1i(R.progBloomBlur.u_color, 0); + gl.uniform2f(R.progBloomBlur.u_textureSize, width, height); + renderFullScreenQuad(R.progBloomBlur); + + // second pass, + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_bloomBlur.fbo[1]); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_bloomBlur.colorTex[0]); + gl.uniform1i(R.progBloomBlur.u_horizontal, 0); + gl.uniform1i(R.progBloomBlur.u_color, 0); + gl.uniform2f(R.progBloomBlur.u_textureSize, width, height); + renderFullScreenQuad(R.progBloomBlur); + + // merge the bloom image and the original image + gl.enable(gl.BLEND); + gl.blendEquation( gl.FUNC_ADD ); + gl.blendFunc(gl.ONE,gl.ONE); + + gl.bindFramebuffer(gl.FRAMEBUFFER, R.pass_bloomBlur.fbo[0]); + gl.clearColor(0.0, 0.0, 0.0, 1.0); + gl.clearDepth(1.0); + gl.clear(gl.COLOR_BUFFER_BIT || gl.DEPTH_BUFFER_BIT); + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_bloomBlur.colorTex[1]); + renderFullScreenQuad(R.progPost1); + + gl.activeTexture(gl.TEXTURE0); + gl.bindTexture(gl.TEXTURE_2D, R.pass_deferred.colorTex); + renderFullScreenQuad(R.progPost1); + gl.disable(gl.BLEND); + }; + var renderFullScreenQuad = (function() { // The variables in this function are private to the implementation of // renderFullScreenQuad. They work like static local variables in C++. @@ -230,12 +398,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 +417,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..5447f62 100644 --- a/js/deferredSetup.js +++ b/js/deferredSetup.js @@ -6,9 +6,12 @@ R.pass_debug = {}; R.pass_deferred = {}; R.pass_post1 = {}; + R.pass_motionblur = {}; + R.pass_bloomThreshold = {}; + R.pass_bloomBlur = {}; R.lights = []; - R.NUM_GBUFFERS = 4; + R.NUM_GBUFFERS = 2; /** * Set up the deferred pipeline framebuffer objects and textures. @@ -18,8 +21,17 @@ loadAllShaderPrograms(); R.pass_copy.setup(); R.pass_deferred.setup(); + R.pass_motionblur.setup(); + R.pass_bloomThreshold.setup(); + R.pass_bloomBlur.setup(); + R.preCameraMat = new THREE.Matrix4(); + + //loadSponzaTexture(); }; + // var loadSponzaTexture() = function() { + + // } // TODO: Edit if you want to change the light initial positions R.light_min = [-14, 0, -6]; R.light_max = [14, 18, 6]; @@ -98,6 +110,60 @@ gl.bindFramebuffer(gl.FRAMEBUFFER, null); }; + R.pass_motionblur.setup = function() { + // * Create the FBO + R.pass_motionblur.fbo = gl.createFramebuffer(); + // * Create, bind, and store a single color target texture for the FBO + R.pass_motionblur.colorTex = createAndBindColorTargetTexture( + R.pass_motionblur.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + + // * Check for framebuffer errors + abortIfFramebufferIncomplete(R.pass_deferred.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_bloomThreshold.setup = function() { + R.pass_bloomThreshold.fbo = gl.createFramebuffer(); + // * Create, bind, and store a single color target texture for the FBO + R.pass_bloomThreshold.colorTex = createAndBindColorTargetTexture( + R.pass_bloomThreshold.fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + + // * Check for framebuffer errors + abortIfFramebufferIncomplete(R.pass_bloomThreshold.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_bloomBlur.setup = function() { + R.pass_bloomBlur.fbo = []; + R.pass_bloomBlur.colorTex = []; + var fbo; + var colorTex; + + fbo = gl.createFramebuffer(); + colorTex = createAndBindColorTargetTexture(fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + R.pass_bloomBlur.fbo.push(fbo); + R.pass_bloomBlur.colorTex.push(colorTex); + + //gl.bindFramebuffer(gl.FRAMEBUFFER, null); + fbo = gl.createFramebuffer(); + colorTex = createAndBindColorTargetTexture(fbo, gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL); + abortIfFramebufferIncomplete(fbo); + gl_draw_buffers.drawBuffersWEBGL([gl_draw_buffers.COLOR_ATTACHMENT0_WEBGL]); + R.pass_bloomBlur.fbo.push(fbo); + R.pass_bloomBlur.colorTex.push(colorTex); + + gl.bindFramebuffer(gl.FRAMEBUFFER, null); + }; /** * Loads all of the shader programs used in the pipeline. */ @@ -141,6 +207,11 @@ 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'); + p.u_isToon = gl.getUniformLocation(p.prog, 'u_isToon'); + + p.u_invCrtCameraMat = gl.getUniformLocation(p.prog, 'u_invCrtCameraMat'); + R.prog_BlinnPhong_PointLight = p; }); @@ -156,6 +227,32 @@ R.progPost1 = p; }); + loadPostProgram('motionblur', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_depth = gl.getUniformLocation(p.prog, 'u_depth'); + + p.u_crtCameraMat = gl.getUniformLocation(p.prog, 'u_crtCameraMat'); + p.u_preCameraMat = gl.getUniformLocation(p.prog, 'u_preCameraMat'); + p.u_invCrtCameraMat = gl.getUniformLocation(p.prog, 'u_invCrtCameraMat'); + + //console.log(p.u_invCrtCameraMat); + R.progMotionBlur = p; + }); + + loadPostProgram('bloomthreshold', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + + //console.log(p.u_invCrtCameraMat); + R.progBloomThreshold = p; + }); + + loadPostProgram('bloomblur', function(p) { + p.u_color = gl.getUniformLocation(p.prog, 'u_color'); + p.u_horizontal = gl.getUniformLocation(p.prog, 'u_horizontal'); + p.u_textureSize = gl.getUniformLocation(p.prog, 'u_textureSize'); + //console.log(p.u_invCrtCameraMat); + R.progBloomBlur = p; + }); // TODO: If you add more passes, load and set up their shader programs. }; @@ -174,6 +271,7 @@ p.u_depth = gl.getUniformLocation(prog, 'u_depth'); p.a_position = gl.getAttribLocation(prog, 'a_position'); + // call custom callback function callback(p); }); }; diff --git a/js/framework.js b/js/framework.js index 80d64f2..41db70e 100644 --- a/js/framework.js +++ b/js/framework.js @@ -13,6 +13,12 @@ var width, height; camera.updateMatrixWorld(); camera.matrixWorldInverse.getInverse(camera.matrixWorld); cameraMat.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + //R.preCameraMat = cameraMat; + //var mat = new THREE.Matrix4(); + //var crtMat = mat.multiplyMatrices(camera.projectionMatrix, camera.matrixWorldInverse); + //console.log('cameraMat'); + //console.log(cameraMat); + //console.log(crtMat); R.deferredRender({ cameraMat: cameraMat, projMat: camera.projectionMatrix, @@ -67,7 +73,7 @@ var width, height; var init = function() { // TODO: For performance measurements, disable debug mode! - var debugMode = true; + var debugMode = false; canvas = document.getElementById('canvas'); renderer = new THREE.WebGLRenderer({ @@ -88,7 +94,8 @@ var width, height; initExtensions(); stats = new Stats(); - stats.setMode(1); // 0: fps, 1: ms, 2: mb + stats.setMode(0); // 0: fps, 1: ms, 2: mb + //console.log(stats.fps); stats.domElement.style.position = 'absolute'; stats.domElement.style.left = '0px'; stats.domElement.style.top = '0px'; @@ -106,6 +113,7 @@ var width, height; ); camera.position.set(-15.5, 1, -1); + //R.preCameraMat = cameraMat; controls = new THREE.OrbitControls(camera, renderer.domElement); controls.enableDamping = true; controls.enableZoom = true; @@ -151,11 +159,7 @@ var width, height; // temp for sponza - var colorTextureName = 'texture_color'; // for sponza - if (!glTF.json.textures[colorTextureName]) { - colorTextureName = (Object.keys(glTF.json.textures))[0]; - console.log(colorTextureName); - } + var colorTextureName = 'texture_color'; var normalTextureName = 'texture_normal'; // textures @@ -206,8 +210,9 @@ var width, height; target: target, id: textureID }; - + //console.log(glTFURL + ' textureID:' + textureID); textureID++; + } @@ -236,29 +241,23 @@ var width, height; var posInfo = primitive.attributes[primitive.technique.parameters['position'].semantic]; var norInfo = primitive.attributes[primitive.technique.parameters['normal'].semantic]; - var uvInfo; - if (primitive.technique.parameters['texcoord_0']) { - uvInfo = primitive.attributes[primitive.technique.parameters['texcoord_0'].semantic]; - } else if (primitive.technique.parameters['texcoord0']) { - uvInfo = primitive.attributes[primitive.technique.parameters['texcoord0'].semantic]; - } - + var uvInfo = primitive.attributes[primitive.technique.parameters['texcoord_0'].semantic]; models.push({ gltf: primitive, idx: indicesBuffer, - interleaved: true, - attributes: vertexBuffer, posInfo: {size: posInfo.size, type: posInfo.type, stride: posInfo.stride, offset: posInfo.offset}, norInfo: {size: norInfo.size, type: norInfo.type, stride: norInfo.stride, offset: norInfo.offset}, uvInfo: {size: uvInfo.size, type: uvInfo.type, stride: uvInfo.stride, offset: uvInfo.offset}, + //texture: + // specific textures temp test colmap: webGLTextures[colorTextureName].texture, - normap: webGLTextures[normalTextureName] ? webGLTextures[normalTextureName].texture : null + normap: webGLTextures[normalTextureName].texture }); } @@ -327,21 +326,9 @@ var width, height; gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, gidx); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, idx, gl.STATIC_DRAW); - // var m = { - // idx: gidx, - // elemCount: idx.length, - // position: gposition, - // normal: gnormal, - // uv: guv - // }; - - // adapt to new readyModelForDraw and drawReadyModel (glTF version) 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..dd7719d 100644 --- a/js/ui.js +++ b/js/ui.js @@ -7,7 +7,8 @@ var cfg; // TODO: Define config fields and defaults here this.debugView = -1; this.debugScissor = false; - this.enableEffect0 = false; + this.Toonshading = false; + this.Bloom = false; }; var init = function() { @@ -17,18 +18,19 @@ var cfg; // TODO: Define any other possible config values gui.add(cfg, 'debugView', { 'None': -1, - '0 Depth': 0, - '1 Position': 1, - '2 Geometry normal': 2, - '3 Color map': 3, - '4 Normal map': 4, - '5 Surface normal': 5 + // '0 Depth': 0, + // '1 Position': 1, + // '2 Geometry normal': 2, + // '3 Color map': 3, + // '4 Normal map': 4, + // '5 Surface normal': 5 }); gui.add(cfg, 'debugScissor'); - var eff0 = gui.addFolder('EFFECT NAME HERE'); + var eff0 = gui.addFolder('EFFECTS'); eff0.open(); - eff0.add(cfg, 'enableEffect0'); + eff0.add(cfg, 'Toonshading'); + eff0.add(cfg, 'Bloom'); // TODO: add more effects toggles and parameters here }; diff --git a/js/util.js b/js/util.js index 4055894..1c50aae 100644 --- a/js/util.js +++ b/js/util.js @@ -92,35 +92,16 @@ window.readyModelForDraw = function(prog, m) { gl.uniform1i(prog.u_normap, 1); } - if (m.interleaved) { - gl.bindBuffer(gl.ARRAY_BUFFER, m.attributes); + 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); - } + gl.enableVertexAttribArray(prog.a_position); + gl.vertexAttribPointer(prog.a_position, m.posInfo.size, m.posInfo.type, false, m.posInfo.stride, m.posInfo.offset); - 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.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); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, m.idx); }; @@ -129,55 +110,41 @@ 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 - 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); - } + gl.drawElements(m.gltf.mode, m.gltf.indices.length, m.gltf.indicesComponentType, 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); - const neg_one = new THREE.Vector2(-1, -1); - const pos_one = new THREE.Vector2(1, 1); + var minpt = new THREE.Vector2(0, 0); + var maxpt = new THREE.Vector2(0, 0); var ret = [0, 0, 0, 0]; - 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) { + return function(view, proj, l) { // front bottom-left corner of sphere's bounding cube + var eps = 0; + a.fromArray(l.pos); + a.w = 1; + a.applyMatrix4(view); + a.x -= l.rad + eps; + a.y -= l.rad + eps; + a.z += l.rad; + a.applyMatrix4(proj); + a.divideScalar(a.w); - // 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 - } + // front bottom-left corner of sphere's bounding cube + b.fromArray(l.pos); + b.w = 1; + b.applyMatrix4(view); + b.x += l.rad + eps; + b.y += l.rad + eps; + 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)); if (maxpt.x < -1 || 1 < minpt.x || maxpt.y < -1 || 1 < minpt.y) { diff --git a/params.json b/params.json new file mode 100644 index 0000000..50de4e3 --- /dev/null +++ b/params.json @@ -0,0 +1,6 @@ +{ + "name": "Project5-webgl-deferred-shading-with-gltf", + "tagline": "Project5-WebGL-Deferred-Shading-with-glTF", + "body": "### Welcome to GitHub Pages.\r\nThis automatic page generator is the easiest way to create beautiful pages for all of your projects. Author your page content here [using GitHub Flavored Markdown](https://guides.github.com/features/mastering-markdown/), select a template crafted by a designer, and publish. After your page is generated, you can check out the new `gh-pages` branch locally. If you’re using GitHub Desktop, simply sync your repository and you’ll see the new branch.\r\n\r\n### Designer Templates\r\nWe’ve crafted some handsome templates for you to use. Go ahead and click 'Continue to layouts' to browse through them. You can easily go back to edit your page before publishing. After publishing your page, you can revisit the page generator and switch to another theme. Your Page content will be preserved.\r\n\r\n### Creating pages manually\r\nIf you prefer to not use the automatic generator, push a branch named `gh-pages` to your repository to create a page manually. In addition to supporting regular HTML content, GitHub Pages support Jekyll, a simple, blog aware static site generator. Jekyll makes it easy to create site-wide headers and footers without having to copy them across every page. It also offers intelligent blog support and other advanced templating features.\r\n\r\n### Authors and Contributors\r\nYou can @mention a GitHub username to generate a link to their profile. The resulting `` element will link to the contributor’s GitHub Profile. For example: In 2007, Chris Wanstrath (@defunkt), PJ Hyett (@pjhyett), and Tom Preston-Werner (@mojombo) founded GitHub.\r\n\r\n### Support or Contact\r\nHaving trouble with Pages? Check out our [documentation](https://help.github.com/pages) or [contact support](https://github.com/contact) and we’ll help you sort it out.\r\n", + "note": "Don't delete this file! It's used internally to help with page regeneration." +} \ No newline at end of file diff --git a/stylesheets/github-light.css b/stylesheets/github-light.css new file mode 100644 index 0000000..0c6b24d --- /dev/null +++ b/stylesheets/github-light.css @@ -0,0 +1,124 @@ +/* +The MIT License (MIT) + +Copyright (c) 2016 GitHub, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +*/ + +.pl-c /* comment */ { + color: #969896; +} + +.pl-c1 /* constant, variable.other.constant, support, meta.property-name, support.constant, support.variable, meta.module-reference, markup.raw, meta.diff.header */, +.pl-s .pl-v /* string variable */ { + color: #0086b3; +} + +.pl-e /* entity */, +.pl-en /* entity.name */ { + color: #795da3; +} + +.pl-smi /* variable.parameter.function, storage.modifier.package, storage.modifier.import, storage.type.java, variable.other */, +.pl-s .pl-s1 /* string source */ { + color: #333; +} + +.pl-ent /* entity.name.tag */ { + color: #63a35c; +} + +.pl-k /* keyword, storage, storage.type */ { + color: #a71d5d; +} + +.pl-s /* string */, +.pl-pds /* punctuation.definition.string, string.regexp.character-class */, +.pl-s .pl-pse .pl-s1 /* string punctuation.section.embedded source */, +.pl-sr /* string.regexp */, +.pl-sr .pl-cce /* string.regexp constant.character.escape */, +.pl-sr .pl-sre /* string.regexp source.ruby.embedded */, +.pl-sr .pl-sra /* string.regexp string.regexp.arbitrary-repitition */ { + color: #183691; +} + +.pl-v /* variable */ { + color: #ed6a43; +} + +.pl-id /* invalid.deprecated */ { + color: #b52a1d; +} + +.pl-ii /* invalid.illegal */ { + color: #f8f8f8; + background-color: #b52a1d; +} + +.pl-sr .pl-cce /* string.regexp constant.character.escape */ { + font-weight: bold; + color: #63a35c; +} + +.pl-ml /* markup.list */ { + color: #693a17; +} + +.pl-mh /* markup.heading */, +.pl-mh .pl-en /* markup.heading entity.name */, +.pl-ms /* meta.separator */ { + font-weight: bold; + color: #1d3e81; +} + +.pl-mq /* markup.quote */ { + color: #008080; +} + +.pl-mi /* markup.italic */ { + font-style: italic; + color: #333; +} + +.pl-mb /* markup.bold */ { + font-weight: bold; + color: #333; +} + +.pl-md /* markup.deleted, meta.diff.header.from-file */ { + color: #bd2c00; + background-color: #ffecec; +} + +.pl-mi1 /* markup.inserted, meta.diff.header.to-file */ { + color: #55a532; + background-color: #eaffea; +} + +.pl-mdr /* meta.diff.range */ { + font-weight: bold; + color: #795da3; +} + +.pl-mo /* meta.output */ { + color: #1d3e81; +} + diff --git a/stylesheets/normalize.css b/stylesheets/normalize.css new file mode 100644 index 0000000..30366a6 --- /dev/null +++ b/stylesheets/normalize.css @@ -0,0 +1,424 @@ +/*! normalize.css v3.0.2 | MIT License | git.io/normalize */ + +/** + * 1. Set default font family to sans-serif. + * 2. Prevent iOS text size adjust after orientation change, without disabling + * user zoom. + */ + +html { + font-family: sans-serif; /* 1 */ + -ms-text-size-adjust: 100%; /* 2 */ + -webkit-text-size-adjust: 100%; /* 2 */ +} + +/** + * Remove default margin. + */ + +body { + margin: 0; +} + +/* HTML5 display definitions + ========================================================================== */ + +/** + * Correct `block` display not defined for any HTML5 element in IE 8/9. + * Correct `block` display not defined for `details` or `summary` in IE 10/11 + * and Firefox. + * Correct `block` display not defined for `main` in IE 11. + */ + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +main, +menu, +nav, +section, +summary { + display: block; +} + +/** + * 1. Correct `inline-block` display not defined in IE 8/9. + * 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera. + */ + +audio, +canvas, +progress, +video { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Prevent modern browsers from displaying `audio` without controls. + * Remove excess height in iOS 5 devices. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Address `[hidden]` styling not present in IE 8/9/10. + * Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22. + */ + +[hidden], +template { + display: none; +} + +/* Links + ========================================================================== */ + +/** + * Remove the gray background color from active links in IE 10. + */ + +a { + background-color: transparent; +} + +/** + * Improve readability when focused and also mouse hovered in all browsers. + */ + +a:active, +a:hover { + outline: 0; +} + +/* Text-level semantics + ========================================================================== */ + +/** + * Address styling not present in IE 8/9/10/11, Safari, and Chrome. + */ + +abbr[title] { + border-bottom: 1px dotted; +} + +/** + * Address style set to `bolder` in Firefox 4+, Safari, and Chrome. + */ + +b, +strong { + font-weight: bold; +} + +/** + * Address styling not present in Safari and Chrome. + */ + +dfn { + font-style: italic; +} + +/** + * Address variable `h1` font-size and margin within `section` and `article` + * contexts in Firefox 4+, Safari, and Chrome. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/** + * Address styling not present in IE 8/9. + */ + +mark { + background: #ff0; + color: #000; +} + +/** + * Address inconsistent and variable font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` affecting `line-height` in all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Remove border when inside `a` element in IE 8/9/10. + */ + +img { + border: 0; +} + +/** + * Correct overflow not hidden in IE 9/10/11. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Grouping content + ========================================================================== */ + +/** + * Address margin not present in IE 8/9 and Safari. + */ + +figure { + margin: 1em 40px; +} + +/** + * Address differences between Firefox and other browsers. + */ + +hr { + box-sizing: content-box; + height: 0; +} + +/** + * Contain overflow in all browsers. + */ + +pre { + overflow: auto; +} + +/** + * Address odd `em`-unit font size rendering in all browsers. + */ + +code, +kbd, +pre, +samp { + font-family: monospace, monospace; + font-size: 1em; +} + +/* Forms + ========================================================================== */ + +/** + * Known limitation: by default, Chrome and Safari on OS X allow very limited + * styling of `select`, unless a `border` property is set. + */ + +/** + * 1. Correct color not being inherited. + * Known issue: affects color of disabled elements. + * 2. Correct font properties not being inherited. + * 3. Address margins set differently in Firefox 4+, Safari, and Chrome. + */ + +button, +input, +optgroup, +select, +textarea { + color: inherit; /* 1 */ + font: inherit; /* 2 */ + margin: 0; /* 3 */ +} + +/** + * Address `overflow` set to `hidden` in IE 8/9/10/11. + */ + +button { + overflow: visible; +} + +/** + * Address inconsistent `text-transform` inheritance for `button` and `select`. + * All other form control elements do not inherit `text-transform` values. + * Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera. + * Correct `select` style inheritance in Firefox. + */ + +button, +select { + text-transform: none; +} + +/** + * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` + * and `video` controls. + * 2. Correct inability to style clickable `input` types in iOS. + * 3. Improve usability and consistency of cursor style between image-type + * `input` and others. + */ + +button, +html input[type="button"], /* 1 */ +input[type="reset"], +input[type="submit"] { + -webkit-appearance: button; /* 2 */ + cursor: pointer; /* 3 */ +} + +/** + * Re-set default cursor for disabled elements. + */ + +button[disabled], +html input[disabled] { + cursor: default; +} + +/** + * Remove inner padding and border in Firefox 4+. + */ + +button::-moz-focus-inner, +input::-moz-focus-inner { + border: 0; + padding: 0; +} + +/** + * Address Firefox 4+ setting `line-height` on `input` using `!important` in + * the UA stylesheet. + */ + +input { + line-height: normal; +} + +/** + * It's recommended that you don't attempt to style these elements. + * Firefox's implementation doesn't respect box-sizing, padding, or width. + * + * 1. Address box sizing set to `content-box` in IE 8/9/10. + * 2. Remove excess padding in IE 8/9/10. + */ + +input[type="checkbox"], +input[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Fix the cursor style for Chrome's increment/decrement buttons. For certain + * `font-size` values of the `input`, it causes the cursor style of the + * decrement button to change from `default` to `text`. + */ + +input[type="number"]::-webkit-inner-spin-button, +input[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Address `appearance` set to `searchfield` in Safari and Chrome. + * 2. Address `box-sizing` set to `border-box` in Safari and Chrome + * (include `-moz` to future-proof). + */ + +input[type="search"] { + -webkit-appearance: textfield; /* 1 */ /* 2 */ + box-sizing: content-box; +} + +/** + * Remove inner padding and search cancel button in Safari and Chrome on OS X. + * Safari (but not Chrome) clips the cancel button when the search input has + * padding (and `textfield` appearance). + */ + +input[type="search"]::-webkit-search-cancel-button, +input[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * Define consistent border, margin, and padding. + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct `color` not being inherited in IE 8/9/10/11. + * 2. Remove padding so people aren't caught out if they zero out fieldsets. + */ + +legend { + border: 0; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Remove default vertical scrollbar in IE 8/9/10/11. + */ + +textarea { + overflow: auto; +} + +/** + * Don't inherit the `font-weight` (applied by a rule above). + * NOTE: the default cannot safely be changed in Chrome and Safari on OS X. + */ + +optgroup { + font-weight: bold; +} + +/* Tables + ========================================================================== */ + +/** + * Remove most spacing between table cells. + */ + +table { + border-collapse: collapse; + border-spacing: 0; +} + +td, +th { + padding: 0; +} diff --git a/stylesheets/stylesheet.css b/stylesheets/stylesheet.css new file mode 100644 index 0000000..b5f20c2 --- /dev/null +++ b/stylesheets/stylesheet.css @@ -0,0 +1,245 @@ +* { + box-sizing: border-box; } + +body { + padding: 0; + margin: 0; + font-family: "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 16px; + line-height: 1.5; + color: #606c71; } + +a { + color: #1e6bb8; + text-decoration: none; } + a:hover { + text-decoration: underline; } + +.btn { + display: inline-block; + margin-bottom: 1rem; + color: rgba(255, 255, 255, 0.7); + background-color: rgba(255, 255, 255, 0.08); + border-color: rgba(255, 255, 255, 0.2); + border-style: solid; + border-width: 1px; + border-radius: 0.3rem; + transition: color 0.2s, background-color 0.2s, border-color 0.2s; } + .btn + .btn { + margin-left: 1rem; } + +.btn:hover { + color: rgba(255, 255, 255, 0.8); + text-decoration: none; + background-color: rgba(255, 255, 255, 0.2); + border-color: rgba(255, 255, 255, 0.3); } + +@media screen and (min-width: 64em) { + .btn { + padding: 0.75rem 1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .btn { + padding: 0.6rem 0.9rem; + font-size: 0.9rem; } } + +@media screen and (max-width: 42em) { + .btn { + display: block; + width: 100%; + padding: 0.75rem; + font-size: 0.9rem; } + .btn + .btn { + margin-top: 1rem; + margin-left: 0; } } + +.page-header { + color: #fff; + text-align: center; + background-color: #159957; + background-image: linear-gradient(120deg, #155799, #159957); } + +@media screen and (min-width: 64em) { + .page-header { + padding: 5rem 6rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .page-header { + padding: 3rem 4rem; } } + +@media screen and (max-width: 42em) { + .page-header { + padding: 2rem 1rem; } } + +.project-name { + margin-top: 0; + margin-bottom: 0.1rem; } + +@media screen and (min-width: 64em) { + .project-name { + font-size: 3.25rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .project-name { + font-size: 2.25rem; } } + +@media screen and (max-width: 42em) { + .project-name { + font-size: 1.75rem; } } + +.project-tagline { + margin-bottom: 2rem; + font-weight: normal; + opacity: 0.7; } + +@media screen and (min-width: 64em) { + .project-tagline { + font-size: 1.25rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .project-tagline { + font-size: 1.15rem; } } + +@media screen and (max-width: 42em) { + .project-tagline { + font-size: 1rem; } } + +.main-content :first-child { + margin-top: 0; } +.main-content img { + max-width: 100%; } +.main-content h1, .main-content h2, .main-content h3, .main-content h4, .main-content h5, .main-content h6 { + margin-top: 2rem; + margin-bottom: 1rem; + font-weight: normal; + color: #159957; } +.main-content p { + margin-bottom: 1em; } +.main-content code { + padding: 2px 4px; + font-family: Consolas, "Liberation Mono", Menlo, Courier, monospace; + font-size: 0.9rem; + color: #383e41; + background-color: #f3f6fa; + border-radius: 0.3rem; } +.main-content pre { + padding: 0.8rem; + margin-top: 0; + margin-bottom: 1rem; + font: 1rem Consolas, "Liberation Mono", Menlo, Courier, monospace; + color: #567482; + word-wrap: normal; + background-color: #f3f6fa; + border: solid 1px #dce6f0; + border-radius: 0.3rem; } + .main-content pre > code { + padding: 0; + margin: 0; + font-size: 0.9rem; + color: #567482; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; } +.main-content .highlight { + margin-bottom: 1rem; } + .main-content .highlight pre { + margin-bottom: 0; + word-break: normal; } +.main-content .highlight pre, .main-content pre { + padding: 0.8rem; + overflow: auto; + font-size: 0.9rem; + line-height: 1.45; + border-radius: 0.3rem; } +.main-content pre code, .main-content pre tt { + display: inline; + max-width: initial; + padding: 0; + margin: 0; + overflow: initial; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; } + .main-content pre code:before, .main-content pre code:after, .main-content pre tt:before, .main-content pre tt:after { + content: normal; } +.main-content ul, .main-content ol { + margin-top: 0; } +.main-content blockquote { + padding: 0 1rem; + margin-left: 0; + color: #819198; + border-left: 0.3rem solid #dce6f0; } + .main-content blockquote > :first-child { + margin-top: 0; } + .main-content blockquote > :last-child { + margin-bottom: 0; } +.main-content table { + display: block; + width: 100%; + overflow: auto; + word-break: normal; + word-break: keep-all; } + .main-content table th { + font-weight: bold; } + .main-content table th, .main-content table td { + padding: 0.5rem 1rem; + border: 1px solid #e9ebec; } +.main-content dl { + padding: 0; } + .main-content dl dt { + padding: 0; + margin-top: 1rem; + font-size: 1rem; + font-weight: bold; } + .main-content dl dd { + padding: 0; + margin-bottom: 1rem; } +.main-content hr { + height: 2px; + padding: 0; + margin: 1rem 0; + background-color: #eff0f1; + border: 0; } + +@media screen and (min-width: 64em) { + .main-content { + max-width: 64rem; + padding: 2rem 6rem; + margin: 0 auto; + font-size: 1.1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .main-content { + padding: 2rem 4rem; + font-size: 1.1rem; } } + +@media screen and (max-width: 42em) { + .main-content { + padding: 2rem 1rem; + font-size: 1rem; } } + +.site-footer { + padding-top: 2rem; + margin-top: 2rem; + border-top: solid 1px #eff0f1; } + +.site-footer-owner { + display: block; + font-weight: bold; } + +.site-footer-credits { + color: #819198; } + +@media screen and (min-width: 64em) { + .site-footer { + font-size: 1rem; } } + +@media screen and (min-width: 42em) and (max-width: 64em) { + .site-footer { + font-size: 1rem; } } + +@media screen and (max-width: 42em) { + .site-footer { + font-size: 0.9rem; } } diff --git a/table.xlsx b/table.xlsx new file mode 100644 index 0000000..882740d Binary files /dev/null and b/table.xlsx differ