Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
bb7ac07
almost done writing logic for clusters
AmanSachan1 Oct 22, 2017
83607e2
updating clusters for which lights they contain
AmanSachan1 Oct 22, 2017
7dfb12b
updating clusters for which lights they contain
AmanSachan1 Oct 22, 2017
04d44e8
cluster updating is complete, including writing to a texture
AmanSachan1 Oct 23, 2017
e9bb3b4
saving to work on lab computers
AmanSachan1 Oct 24, 2017
c2edb47
debugging the binary search for planes
sarahforcier Oct 24, 2017
5a80fea
equiangular split for clusters, wrote updatecluster
sarahforcier Oct 24, 2017
e182095
checkpoint; need to debug everything compiles but black screen
sarahforcier Oct 24, 2017
548418f
things are wrong and not working
sarahforcier Oct 25, 2017
5e09a88
different approach still black
AmanSachan1 Oct 25, 2017
149801b
back to planes
AmanSachan1 Oct 25, 2017
6cc16fd
coded the plane stuff again
AmanSachan1 Oct 25, 2017
b305aa2
checkpoint for planes
AmanSachan1 Oct 25, 2017
7d36322
no loops version of cluster indexing; cluster indexing also works in …
AmanSachan1 Oct 26, 2017
f7c303b
shader is screwed, all fragments access same cluster
AmanSachan1 Oct 26, 2017
25b11eb
frag shader works
AmanSachan1 Oct 26, 2017
2dd6e4d
clustered forward plus works
AmanSachan1 Oct 26, 2017
4d08c7f
moving onto deffered
AmanSachan1 Oct 26, 2017
cf94a42
deffered shading works without clustering
AmanSachan1 Oct 26, 2017
98f421e
deffered with clustering works, 2 component normal compression, packi…
AmanSachan1 Oct 27, 2017
336c747
toon shading with blackoutlines disabled, blinn-phong, gamma correction
AmanSachan1 Oct 27, 2017
3c0d145
formatted deffered shader
AmanSachan1 Oct 27, 2017
65c4391
readme
AmanSachan1 Oct 27, 2017
e24ff5c
readme pictures
AmanSachan1 Oct 27, 2017
7d9185f
trying to deploy
AmanSachan1 Oct 27, 2017
5b5dd01
readme
AmanSachan1 Oct 27, 2017
ed1c840
gui
AmanSachan1 Oct 27, 2017
fa1997e
deploying
AmanSachan1 Oct 27, 2017
b8b09a8
changed files for video but changed them back to normal after, ie no …
AmanSachan1 Oct 27, 2017
e60676a
readme
AmanSachan1 Oct 28, 2017
7ff5a97
readme
AmanSachan1 Oct 28, 2017
9f312f6
readme
AmanSachan1 Oct 28, 2017
89930ca
readme
AmanSachan1 Oct 28, 2017
1f4f9c1
readme
AmanSachan1 Oct 28, 2017
571cd08
readme
AmanSachan1 Oct 28, 2017
a7707de
readme
AmanSachan1 Oct 28, 2017
9d887c9
tiny thing in readme
AmanSachan1 Oct 28, 2017
8796515
update readme
AmanSachan1 Dec 19, 2017
cbdbd54
fix spelling in url linked in readme
AmanSachan1 Dec 19, 2017
621b852
updated live link in readme
AmanSachan1 Nov 6, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 104 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,119 @@ WebGL Clustered Deferred and Forward+ 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)
* Aman Sachan
* Tested on: Windows 10, Intel(R) Xeon(R) CPU E5-1630 v4 @ 3.7GHz (8 CPUs) 32GB RAM,
GTX 1070 8081MB (Lab Machine)

### Live Online
## [Try it Live!](https://amansachan1.github.io/WebGL-Clustered-Deferred-Forward-Plus/)

[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading)
## Demo Video

### Demo Video/GIF
[![](readmeImages/VideoLinkImage.png)](https://vimeo.com/240254058)

[![](img/video.png)](TODO)
#### _The demo above has about 300 dynamic lights. The Clustered Deferred Shading model implemented here can handle more than 2100 lights at 60 FPS but this over saturates the scene._

### (TODO: Your README)
## OverView

*DO NOT* leave the README to the last minute! It is a crucial part of the
project, and we will not be able to grade you without a good README.
This project implements Clustered Deferred and Clustered Forward Shading Techniques. These techniques improve performance for scenes with many dynamic lights. Typical forward shading involves looping over every object and then looping over every single light in the scene to accumulate the light energy on the object. If the number of lights is high, this can lead to a drastic drop in performance. We need to cull lights so that only those lights which actually contribute energy on a fragment are looped over. Clustering is a solution that bins lights into 3D buckets based on what the light can influence. Deferred shading is an improvement over this by only dealing with the fragments with the smallest z-depth, which is accomplished via a culling stage and using a "g-buffer".

This assignment has a considerable amount of performance analysis compared
to implementation work. Complete the implementation early to leave time!
These solutions allow you to have an enormous number of dynamic lights in the scene when optimized. This project can run over 2100 lights at 60 FPS.

## Features

### Credits
_A much more in depth explanation of the shading models and clustering can be found [here](http://amansachan.com/webpages/projects.html#Modal_ClusteredDeferredShading)._

### Forward Shading Model

![](readmeImages/NumLightsComparison.png)

![](readmeImages/NumLights_table.png)

A typical forward shading pipeline loops over every object and then inside that loops over all the lights in the scene to accumulate the light energy on the object. If the number of lights is high, this can lead to a drastic drop in performance.

As seen in the charts above forward shading takes a massive hit in performance as you increase the number of dynamic lights in the scene. At around 2000 lights it doesnt even run.

### Clustered Forward+ Shading Model

Forward Plus is a shading technique that involves culling lights in some fashion and then binning them in either 2D tiles or as was done in this project 3D clusters. This means that there is overhead for light culling but the payoff during shading is enormous. Culling lights means every fragment looops through only a select few lights in relation to the entire scene instead of all the lights.

The preformance can be many folds as the number of lights in the scene increase. About 17-18 times at 500 lights as compared to forward shading.

The graphs show at a 1000 lights that forward plus is only about 5 times faster than forward shading. This is much better ofcourse but isn't as great as 17. This might be because of the cluster texture not fitting well into memory leading to many expensive calls to global memorry during shading.

### Clustered Deferred Shading Model

Deferred shading is an improvement over forward plus shading. It has 2 render passes, the first one looping over fragments and creating a "g-buffer" that stores only the topmost fragment, ie the one with the smallest depth, and storing the data associated with it in a single or multiple buffers. This data can be whatever is required for shading such as positions, colors, normals, depth, specular, etc.

Not having to loop over all the the fragments for a pixel and perform light accumulation on all the fragments leads to massive performance boosts even over forward plus. This boost is anywhere from 2 to 8 times more. The performance can be improved by using smaller and more data-coherent textures and so compact g-buffers.

The performance of both clustered forward plus and deffered shading is heavily dependent upon the size of the cluster buffer which is based mainly on how many clusters and the maximum number of lights that can be stored in the cluster. Making this texture data coherent goes a long way towards making the shading technique run really fast. This however is hard to know intuitively and was more of a guessing process.

### Clustering

Clustering is the binning technique used to store lights in a spatial data structure based on their area of influence. Clusters arent simply called grids because they are made y slicing up the camera view frustum, which makes each cluster a tiny frustum. Frustum based culling is hard (false positives are easy) but there are many ways to go about it based on how accurate you need to be.

#### [Fast Frustum Culling](http://gamedevs.org/uploads/fast-extraction-viewing-frustum-planes-from-world-view-projection-matrix.pdf)

![](readmeImages/PointLightCulling.png)

![](readmeImages/false-positive-diagram.jpg)

![](readmeImages/Clustering_depth_Slicing.png)

The clusters used in this project were uniformly spaces in z, and were slicing the view frustum into 15 sections along each dimension. The number of slices per plane affects the size of the cluster buffer and so is a good number to play around with. Additionally it may be beneficially to have exponential or some other way of splitting up the z dimension, because usually in real-time applications things in the distance are sampled at lower resolutions. This is something that can and has been taken advantage of, for example the game doom implemented an exponential divisioning of the z dimension.

### Optimizations

![](readmeImages/OptimizationComparison.png)

![](readmeImages/OptimizationComparison_table.png)

#### Using Two G-buffers (use total 8 channels) - Pack values together into vec4s

Reducing the number of g-buffers helps make deferred shading faster which can be done by compactly storing data in them. Colors often dont need the alpha component, normals can be reconstructed simply from 2 of their components, data can be stored in 24bit floats instead of 32 bits, are just some of the ways to achieve this compression.

This project implemented the following layout for the 2 g-buffers used:

R-Channel | G-Channel | B-Channel | A-Channel
--------- | --------- | --------- | ---------
Position.x | Position.y | Position.z | Normal.x
Color.x | Color.y | Color.z | Normal.y

#### 2 Component Normals

If we assume the magnitude of a normal is one, which it usually is because its simply a direction vector. We can reconstruct the z-value of the normal from its x and y values. The magnitude of a vector is defined as the square root of x squared + y squared + z squared. This formula gives us the magnitude of z. The sign of z is positive in camera space for all the fragments that we can see. Using these 2 bits of information we can easily reconstruct the normal from only its x and y components.

### Effects
#### Toon Shading

![](readmeImages/toonShadingExample.png)

#### Blinn-Phong

![](readmeImages/BlinnPhongShadingExample.png)

#### Gamma Correction

With | Without
---- | -------
![](readmeImages/gammaCorrection.png) | ![](readmeImages/nogammaCorrection.png)

## Debug Views

Fragment X Cluster | Fragment Y Cluster | Fragment Z Cluster
------------------ | ------------------ | ------------------
![](readmeImages/fragmentXcluster.png) | ![](readmeImages/fragmentYcluster.png) | ![](readmeImages/fragmentZcluster.png)

Fragment u coordinate | Lights Per Cluster | Arbitray Light Value Per Cluster
------------------ | ------------------ | ------------------
![](readmeImages/fragment_u.png) | ![](readmeImages/lightsPerCluster.png) | ![](readmeImages/arbitrarylightColorPerCluster.png)

Fragment Depth | Fragment World Space Position | Normals | Albedo
-------------- | ----------------------------- | ------- | ------
![](readmeImages/fragmentdepth.png) | ![](readmeImages/fragmentWorldSpacePositions.png) | ![](readmeImages/worldspacenormals.png) | ![](readmeImages/albedo.png)

## Credits

* [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
Expand Down
2 changes: 2 additions & 0 deletions build/bundle.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions build/bundle.js.map

Large diffs are not rendered by default.

Binary file added readmeImages/BlinnPhongShadingExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/Clustering_depth_Slicing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/LambertianShadingExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/LiveDemoImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/NumLightsComparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/NumLights_table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/OptimizationComparison.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/OptimizationComparison_table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/PointLightCulling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/VideoLinkImage.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/albedo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/arbitrarylightColorPerCluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/blinnPhongSpecular.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/false-positive-diagram.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragmentWorldSpacePositions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragmentXcluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragmentYcluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragmentZcluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragment_u.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/fragmentdepth.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/gammaCorrection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/lightsPerCluster.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/nogammaCorrection.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/toonShadingExample.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added readmeImages/toonshading.png
Binary file added readmeImages/worldspacenormals.png
22 changes: 22 additions & 0 deletions src/AABB.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { mat4, vec4, vec3 } from 'gl-matrix';
const THREE = require('three')

export default class AABB //class for Axis Aligned Bounding Boxes
{
constructor()
{
this.min = vec3.create();
this.max = vec3.create();
}

calcAABB_PointLight(lightPos, radius)
{
this.min[0] = lightPos[0] - radius;
this.min[1] = lightPos[1] - radius;
this.min[2] = lightPos[2] - radius;

this.max[0] = lightPos[0] + radius;
this.max[1] = lightPos[1] + radius;
this.max[2] = lightPos[2] + radius;
}
}
6 changes: 3 additions & 3 deletions src/init.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// TODO: Change this to enable / disable debug mode
export const DEBUG = true && process.env.NODE_ENV === 'development';
// Change this to enable / disable debug mode
export const DEBUG = false && process.env.NODE_ENV === 'development';

import DAT from 'dat-gui';
import WebGLDebug from 'webgl-debug';
Expand Down Expand Up @@ -60,7 +60,7 @@ stats.domElement.style.top = '0px';
document.body.appendChild(stats.domElement);

// Initialize camera
export const camera = new PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 1000);
export const camera = new PerspectiveCamera(75, canvas.clientWidth / canvas.clientHeight, 0.1, 50);

// Initialize camera controls
export const cameraControls = new OrbitControls(camera, canvas);
Expand Down
60 changes: 58 additions & 2 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,34 +3,90 @@ import ForwardRenderer from './renderers/forward';
import ClusteredForwardPlusRenderer from './renderers/clusteredForwardPlus';
import ClusteredDeferredRenderer from './renderers/clusteredDeferred';
import Scene from './scene';
import { NUM_LIGHTS } from './scene';
import { MAX_LIGHTS_PER_CLUSTER } from './scene';

const FORWARD = 'Forward';
const CLUSTERED_FORWARD_PLUS = 'Clustered Forward+';
const CLUSTERED_DEFFERED = 'Clustered Deferred';

const Lambertian = 'Lambertian';
const BlinnPhong = 'BlinnPhong';
const Toon = 'Toon';

var shadertype = 0;
var gammaSet = 0;

const params = {
renderer: CLUSTERED_FORWARD_PLUS,
numLights: NUM_LIGHTS,
maxLightsPerCluster: MAX_LIGHTS_PER_CLUSTER,
GammaCorrection: true,
ShadingModel: Lambertian,
_renderer: null,
};

setRenderer(params.renderer);

function setRenderer(renderer) {
if(params.ShadingModel == 'Lambertian')
{
shadertype = 0;
}
else if(params.ShadingModel == 'BlinnPhong')
{
shadertype = 1;
}
else
{
shadertype = 2;
}

if(params.GammaCorrection == true)
{
gammaSet = 1;
}
else
{
gammaSet = 0;
}

switch(renderer) {
case FORWARD:
params._renderer = new ForwardRenderer();
break;
case CLUSTERED_FORWARD_PLUS:
params._renderer = new ClusteredForwardPlusRenderer(15, 15, 15);
params._renderer = new ClusteredForwardPlusRenderer(15, 15, 15, camera, Math.floor(params.numLights),
Math.floor(params.maxLightsPerCluster),
gammaSet,
shadertype);
break;
case CLUSTERED_DEFFERED:
params._renderer = new ClusteredDeferredRenderer(15, 15, 15);
params._renderer = new ClusteredDeferredRenderer(15, 15, 15, camera, Math.floor(params.numLights),
Math.floor(params.maxLightsPerCluster),
gammaSet,
shadertype);
break;
}
}

gui.add(params, 'renderer', [FORWARD, CLUSTERED_FORWARD_PLUS, CLUSTERED_DEFFERED]).onChange(setRenderer);

// Will have to change the scene file to get dynamic changing of lights
// gui.add(params, 'numLights', 50, 10000).onChange(function(newVal) {
// setRenderer(params.renderer);
// });
// gui.add(params, 'maxLightsPerCluster', 50, 7500).onChange(function(newVal) {
// setRenderer(params.renderer);
// });

gui.add(params, 'GammaCorrection').onChange(function(newVal) {
setRenderer(params.renderer);
});
gui.add(params, 'ShadingModel', [Lambertian, BlinnPhong, Toon]).onChange(function(newVal) {
setRenderer(params.renderer);
});

const scene = new Scene();
scene.loadGLTF('models/sponza/sponza.gltf');

Expand Down
Loading