Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Project 5: Jian Ru #19

Open
wants to merge 20 commits into
base: master
Choose a base branch
from
61 changes: 51 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,67 @@ 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)
* Jian Ru
* Tested on: **Google Chrome 54.0.2840.71 m (64-bit)**
Windows 10, i7-4850 @ 2.3GHz 16GB, GT 750M 2GB (Personal)

### Live Online

[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading)
[![](img/thumb.png)](https://jian-ru.github.io/Project5-WebGL-Deferred-Shading-with-glTF)

### Demo Video/GIF

[![](img/video.png)](TODO)
![](img/demo.gif)

### (TODO: Your README)
### Analysis

*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.
* Features
* Basic deferred shading pipeline
* Bloom with 2-pass Gaussian filtering
* SS Motion Blur
* G-buffer optimization
* Spherical light proxies with depth test and front face culling optimization

This assignment has a considerable amount of performance analysis compared
to implementation work. Complete the implementation early to leave time!
* Bloom
* Extracting bright regions
* Convert RGB color to illuminance using `I = 0.2126 * R + 0.7152 * G + 0.0722 * B` and copy the pixels whose brightness pass a user defined threshold.
* Blur the resulting brightness map
* I used two 1D Gaussian kernels with size 1x9 and 9x1 respectively and convolve them with the brightness map 5 times each.

![](img/bloom_perf.png)

* SS Motion Blur
* Implemented by following the idea from GPU Gems 3, Ch. 27.
* Reconstruct point world position using pixel texture coordinates and depth
* Transform the point from world to the NDC space in the previous frame by pre-multiplying it with the inverse of view-projection matrix from last frame
* Velocity vector is defined as the difference between the point's positions in the current and last frame

![](img/motionblur_perf.png)

* G-Buffer Optimization
* G-buffer number reduced to 1 from 4 by
* Applying normal map in the copy pass instead of the light pass
* Using only two-component normal by taking advantage of the fact that normals have lengths of 1
* Reconstructing eye space position from screen coordinates and depth of pixels
* Can easily reduce further to 1 by packing albedo with normal but choose not to for the sake of performance because bit shift and bit-wise operations are not available in GLSL 1.0. Doing packing hence requires many floating point divisions and multiplications.
* Further testing shows that using only 1 G-buffer gives better performance. I think the reason is that enabling multiple rendering targets and the extra global memory writes have greater cost than the extra floating point computations.

| G-buffers | R | G | B | A |
| --- | --- | --- | --- | --- |
| G-buffer 1 | nrm.x | nrm.y | packedAlbedo | unused |

![](img/gbuffer_perf.png)

* Spherical Light Proxies
* Render spheres instead of a full screen quad with scissor test to trigger light computation
* Each sphere is move to center at each point light source and scaled to the light's radius of influence
* The benefits is increased accuracy of light influence estimation, which further reduce the chance of shading unlit pixels. As a result, it further improves performance.

| Scissors | Spherical Proxies |
| --- | --- |
| ![](img/scissor.PNG) | ![](img/sphere_proxy.PNG) |

![](img/scissor_vs_proxy.png)

### Credits

Expand Down
31 changes: 26 additions & 5 deletions glsl/copy.frag.glsl
Original file line number Diff line number Diff line change
@@ -1,20 +1,41 @@
#version 100
#extension GL_EXT_draw_buffers: enable
// #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_position;
varying vec3 v_normal;
varying vec2 v_uv;

void main() {
float packRGBA(vec4 color)
{
color = floor(color * 255.0 + 0.5);
return color.r * 16777216.0 + color.g * 65536.0 + color.b * 256.0 + color.a;
}

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.

vec4 albedo = texture2D(u_colmap, v_uv);
vec3 mapnrm = texture2D(u_normap, v_uv).xyz;

vec3 nrm = applyNormalMap(v_normal, mapnrm);

// this gives you the idea
// gl_FragData[0] = vec4( v_position, 1.0 );
// gl_FragData[0] = vec4(nrm.xy, packRGBA(albedo), 1.0);
gl_FragColor = vec4(nrm.xy, packRGBA(albedo), 1.0);
}
15 changes: 8 additions & 7 deletions glsl/copy.vert.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
precision highp float;
precision highp int;

uniform mat4 u_cameraMat;
uniform mat4 u_cameraMat; // proj * view
uniform mat4 u_viewMat;

attribute vec3 a_position;
attribute vec3 a_normal;
attribute vec3 a_position; // in world space
attribute vec3 a_normal; // in world space
attribute vec2 a_uv;

varying vec3 v_position;
varying vec3 v_normal;
// varying vec3 v_position; // in eye space
varying vec3 v_normal; // in eye space
varying vec2 v_uv;

void main() {
gl_Position = u_cameraMat * vec4(a_position, 1.0);
v_position = a_position;
v_normal = a_normal;
// v_position = vec3(u_viewMat * vec4(a_position, 1.0));
v_normal = vec3(u_viewMat * vec4(a_normal, 0.0));
v_uv = a_uv;
}
12 changes: 12 additions & 0 deletions glsl/copydepth.frag.glsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#version 100
precision highp float;
precision highp int;

uniform sampler2D u_depth;

varying vec2 v_uv;

void main()
{
gl_FragColor = texture2D(u_depth, v_uv);
}
25 changes: 19 additions & 6 deletions glsl/deferred/ambient.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,31 @@
precision highp float;
precision highp int;

#define NUM_GBUFFERS 4
#define NUM_GBUFFERS 1

uniform sampler2D u_gbufs[NUM_GBUFFERS];
uniform sampler2D u_depth;

varying vec2 v_uv;

void main() {
vec4 unpackRGBA(float packedColor)
{
float tmp1 = packedColor;
float tmp2 = floor(tmp1 * 0.00390625);
float a = tmp1 - tmp2 * 256.0;
tmp1 = floor(tmp2 * 0.00390625);
float b = tmp2 - tmp1 * 256.0;
tmp2 = floor(tmp1 * 0.00390625);
float g = tmp1 - tmp2 * 256.0;
tmp1 = floor(tmp2 * 0.00390625);
float r = tmp2 - tmp1 * 256.0;

return vec4(r, g, b, a) * 0.0039215686274509803921568627451;
}

void main()
{
vec4 gb0 = texture2D(u_gbufs[0], v_uv);
vec4 gb1 = texture2D(u_gbufs[1], v_uv);
vec4 gb2 = texture2D(u_gbufs[2], v_uv);
vec4 gb3 = texture2D(u_gbufs[3], v_uv);
float depth = texture2D(u_depth, v_uv).x;
// TODO: Extract needed properties from the g-buffers into local variables

Expand All @@ -23,5 +36,5 @@ void main() {
return;
}

gl_FragColor = vec4(0.1, 0.1, 0.1, 1); // TODO: replace this
gl_FragColor = vec4(0.1 * unpackRGBA(gb0.z).rgb, 1); // TODO: replace this
}
72 changes: 56 additions & 16 deletions glsl/deferred/blinnphong-pointlight.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,38 +2,78 @@
precision highp float;
precision highp int;

#define NUM_GBUFFERS 4
#define NUM_GBUFFERS 1

uniform vec3 u_viewportInfo; // (2_times_tan_halfFovy, width, height)
uniform vec3 u_lightCol;
uniform vec3 u_lightPos;
uniform vec3 u_lightPos; // in eye space
uniform float u_lightRad;
uniform sampler2D u_gbufs[NUM_GBUFFERS];
uniform sampler2D u_depth;

varying vec2 v_uv;
// 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;
vec3 recoverEyePos(float depth)
{
float oneOverHeight = 1.0 / u_viewportInfo.z;
float aspect = u_viewportInfo.y * oneOverHeight;
float ph = u_viewportInfo.x * oneOverHeight;
float pw = ph * aspect;
vec3 viewVec = vec3(vec2(pw, ph) * (gl_FragCoord.xy - u_viewportInfo.yz * 0.5), -1.0);
depth = depth * 2.0 - 1.0;
float eyeDepth = (depth - (101.0 / 99.0)) * (99.0 / 200.0);
eyeDepth = -1.0 / eyeDepth;
return viewVec * eyeDepth;
}

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 unpackRGBA(float packedColor)
{
float tmp1 = packedColor;
float tmp2 = floor(tmp1 * 0.00390625);
float a = tmp1 - tmp2 * 256.0;
tmp1 = floor(tmp2 * 0.00390625);
float b = tmp2 - tmp1 * 256.0;
tmp2 = floor(tmp1 * 0.00390625);
float g = tmp1 - tmp2 * 256.0;
tmp1 = floor(tmp2 * 0.00390625);
float r = tmp2 - tmp1 * 256.0;

return vec4(r, g, b, a) * 0.0039215686274509803921568627451;
}

void main()
{
vec2 v_uv = gl_FragCoord.xy / u_viewportInfo.yz;
vec4 gb0 = texture2D(u_gbufs[0], v_uv); // (eye_normal_x, eye_normal_y, packedAlbedo)
float depth = texture2D(u_depth, v_uv).x;
// TODO: Extract needed properties from the g-buffers into local variables

// If nothing was rendered to this pixel, set alpha to 0 so that the
// postprocessing step can render the sky color.
if (depth == 1.0) {
if (depth == 1.0)
{
gl_FragColor = vec4(0, 0, 0, 0);
return;
}

gl_FragColor = vec4(0, 0, 1, 1); // TODO: perform lighting calculations
// TODO: perform lighting calculations
vec3 pos = recoverEyePos(depth);
vec3 nrm = vec3(gb0.xy, sqrt(max(0.0, 1.0 - dot(gb0.xy, gb0.xy))));
vec3 albedo = unpackRGBA(gb0.z).rgb;
float dist2Light = distance(pos, u_lightPos);

if (dist2Light < u_lightRad)
{
vec3 view = normalize(-pos);
vec3 light = normalize(u_lightPos - pos);
vec3 h = normalize(view + light);
vec3 finalColor = 0.5 * albedo * max(0.0, dot(nrm, light)) +
0.5 * u_lightCol * pow(max(0.0, dot(nrm, h)), 20.0);
float attenuation = (u_lightRad - dist2Light) / u_lightRad;
gl_FragColor = vec4(attenuation * finalColor, 1.0);
}
else
{
gl_FragColor = vec4(0.0);
}
}
Loading