Skip to content

Commit

Permalink
# This is a combination of 2 commits.
Browse files Browse the repository at this point in the history
# This is the 1st commit message:

update - added images, live link, and info update

# The commit message CIS565-Fall-2018#2 will be skipped:

# temp
  • Loading branch information
Hannah Bollar committed Oct 27, 2018
1 parent b6c8d69 commit 6f0d1e6
Show file tree
Hide file tree
Showing 18 changed files with 326 additions and 37 deletions.
124 changes: 114 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,28 +5,132 @@ WebGL Clustered and Forward+ Shading

Hannah Bollar: [LinkedIn](https://www.linkedin.com/in/hannah-bollar/), [Website](http://hannahbollar.com/)

Tested on: Windows 10 Pro, i7-6700HQ @ 2.60GHz 15.9GB, GTX 980M (Personal)
Tested on: Chrome, Windows 10 Pro, i7-8550U @ 1.80GHz 15.9GB (Personal)
____________________________________________________________________________________

![Developer](https://img.shields.io/badge/Developer-Hannah-0f97ff.svg?style=flat) ![CUDA 8.0](https://img.shields.io/badge/CUDA-8.0-yellow.svg) ![Built](https://img.shields.io/appveyor/ci/gruntjs/grunt.svg) ![Progress](https://img.shields.io/badge/implementation-in%20progress-orange.svg)
![Developer](https://img.shields.io/badge/Developer-Hannah-0f97ff.svg?style=flat) ![CUDA 8.0](https://img.shields.io/badge/CUDA-8.0-yellow.svg) ![Built](https://img.shields.io/appveyor/ci/gruntjs/grunt.svg) ![Issues](https://img.shields.io/badge/issues-one-green.svg)

[//]: # ( !![Issues](https://img.shields.io/badge/issues-none-green.svg)
[//]: # ( !![Issues](https://img.shields.io/badge/issues-1-green.svg)


IN PROGRESS
[Live Online](#live-online) - [Features](#visuals) - [Shading Techniques](#shading-techniques) - [Effects and Optimizations](#effects-and-optimizations) - [Bloopers and Debugging](#bloopers-and-debugging) - [Credits](#credits)

### Live Online
## Live Online

[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading)
![LINK to live demo](http://hanbollar.github.io/Project5-WebGL-Clustered-Deferred-Forward-Plus)

### Demo Video/GIF
This didnt deploy properly so the live link doesnt work (tbd).

[![](img/video.png)](TODO)
## Features

### (TODO: rest of README)
- Forward
- already implemented as a demo for the assignment
- Forward+
- Build a data structure to keep track of how many lights are in each cluster and what their indices are
- Render the scene using only the lights that overlap a given cluster
- Clustered
- Reuse clustering logic from Forward+
- Store vertex attributes in g-buffer
- Read g-buffer in a shader to produce final output
- Effects
- deferred Blinn-Phong shading (diffuse + specular) for point lights

## Shading Techniques
Note: in the below gifs, LICECap software creates the blocky effect, the live version is much smoother.

### Forward Rendering

This was already implemented as a demo for how the assignment was constructed, and this version of shading is the most straight forward. The overhead of this is that an excessive number of lights will very quickly slow down this simulation.
```
for each geometry {
for each light {
do calculations...
}
}
```

### Forward Plus
![](./my_images/fplus_blinn_phong.gif)
`*` GIF CONVERTER MADE THIS CHOPPY
This video is of a forward plus implementation with a Blinn-Phong shader.

Instead of doing a double for loop over all the lights, this idea takes the looping and shortens the number of lights associated with each geometry based on the geometry's distance to the light. This improves the performance slightly to prevent the overhead of doing all the light checking for every single geometry.

```
prepass to find light distances
for each geometry {
for each light that's appropriately near this geometry {
do some calculations...
}
}
```

### Clustered Deferred
![](./my_images/clustered_wrong_demo.gif)
`*` GIF CONVERTER MADE THIS CHOPPY
This video is of a clustered deferred implementation with a lambert shader.

Instead of doing the double `for loop` as in the previous two explanations, we create a mid geometry buffer which has a 2d texture storing geometry displacements as needed for rendering (`normal`s, `depth`, `albedo`, etc). This makes rendering even more optimized because it no longer requares the double for loop, but instead just requires a texture call.

```
for each light {
prepass to fill in texture will all needed information
}
for each geometry {
look up information in g-buffer as needed
do some calculations with this information...
}
```

Something is currently wrong in this implementation :( . My assumption is that my light indexing isnt occuring on a gradient as it should, but is being additive without taking into consideration distances (giving the solid circle effect).

## Effects and Optimizations

### Lambert and Blinn-Phong Shading

Lambert | Blinn-Phong
:-------------------------:|:-------------------------:
![](./my_images/lambert.png)| ![](./my_images/blinn_phong.png)


Currently, I have regular lambert shading and Blinn-Phong shading which adds a highlight to the scene. Comparing the two, Blinn-Phong has much stronger highlights as noted in the below images. Notice the flatness of the purple pillar's coloring in the Lambert compared to the more accentuated color changes in the Blinn-Phong.

### Optimizations

I packed information into the buffers in `vec4`s instead of just a listing of `float`s. Additionally, I optimized the space usage from three `vec4`s for positions, colors, and normals, to two `vec4`s by just abstracting out the `z` component of the normal to be recomputed later.

That is, I only put two `vec4`s into my buffer: `vec4(position.x, position.y, position.z, normal.x)` and `vec4(red, blue, green, normal.y)` and I recalculated `normal.z` by using `sqrt(1 - normal.x * normal.x - normal.y * normal.y)`. This optimization works because (hence the name) a normal is always normalized, so we can use the cross of their two attributes we do know to solve for the third.

### Runtime Comparisons

![](./my_images/timeperframe.png)

For shading techniques - it follows properly that as the number of lights increases, the runtime of all of the shading implementations goes up; however, the cluster deferred implementation still maintains the fastest runtime due to the texture call instead of recalculations (as explained in the ![Shading Techniques](#shading-techniques) section). It also goes to show how much each of the techniques improves upon the one prior and that for real-time rendering deferred shading is an optimal solution.

![](./my_images/compressionspeedup.png)

For optimizations - Again, shorter is better. As the graph shows, the `z` optimization effectively makes for fewer pieces of memory being passed through the g-buffer, leading to a faster runtime.

## Bloopers and Debugging

### Bloopers

Clustered Deferred | Indexing-Resolution Issue
:-------------------------:|:-------------------------:
![](./my_images/clustered_wrong.png)| ![](./my_images/indexing_wrong_issue.png)

- [no image]: for a long period of time I had my input values in my deferred shader swapped without realizing, so my output of `albedo` would skew up my lighting calculations since they were using the `albedo` as the `normal` and the `normal` as the `albedo`
- clustered deferred: as mentioned before, I still have this issue and my current understanding is that it's because of an indexing issue and/or a clamp issue when filling in the lights into the buffer in the first place. It's missing the gradient from the light's source to its radius but it does have the appropriate colors and locations. (unresolved)
- indexing-resolution issue: The indexing part of this error was that I was casting to an int for my indices before finishing certain calculations with them so I would get a squarish effect in the texture coordinates. The resolution part of this issue that led to LICECap's screen bar being captured in its own video was because worked on this project on a different machine than usual and the resolution was different than my screen capture's software settings. To fix this, I had to reconfigure the resolution specifically for capturing even though it made the text too small to read on the screen.

### Debug Visuals

Generic | Albedo | AbsNormals
:-------------------------:|:-------------------------:|:-------------------------:
![](./my_images/lambert.png)| ![](./my_images/albedo.png) | ![](./my_images/absnormals.png)

### Credits
## 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
Binary file added my_images/absnormals.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 my_images/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 my_images/blinn_phong.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 my_images/clustered_wrong.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 my_images/clustered_wrong_demo.gif
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 my_images/compressionspeedup.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 my_images/fplus_blinnphong.gif
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 my_images/indexing_wrong_issue.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 my_images/lambert.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 my_images/timeperframe.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion 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';
export const DEBUG = false && process.env.NODE_ENV === 'development';

import DAT from 'dat.gui';
import WebGLDebug from 'webgl-debug';
Expand Down
8 changes: 0 additions & 8 deletions src/renderers/base.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,6 @@ function cos_atan(angle) {
return 1.0 / Math.sqrt(1.0 + angle * angle);
}

function getNormalComponents(angle) {

let bigHypot = Math.sqrt(1 + angle*angle);
let normSide1 = 1 / bigHypot;
let normSide2 = -angle*normSide1;
return vec2.fromValues(normSide1, normSide2);
}

export default class BaseRenderer {
constructor(xSlices, ySlices, zSlices) {
// Create a texture to store cluster data. Each cluster stores the number of lights followed by the light indices
Expand Down
32 changes: 28 additions & 4 deletions src/renderers/clustered.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@ import { gl, WEBGL_draw_buffers, canvas } from '../init';
import { mat4, vec4 } from 'gl-matrix';
import { loadShaderProgram, renderFullscreenQuad } from '../utils';
import { NUM_LIGHTS } from '../scene';
import { MAX_LIGHTS_PER_CLUSTER } from './base.js'
import toTextureVert from '../shaders/deferredToTexture.vert.glsl';
import toTextureFrag from '../shaders/deferredToTexture.frag.glsl';
import QuadVertSource from '../shaders/quad.vert.glsl';
import fsSource from '../shaders/deferred.frag.glsl.js';
import TextureBuffer from './textureBuffer';
import BaseRenderer from './base';

export const NUM_GBUFFERS = 4;
export const MAX_LIGHTS_PER_CLUSTER = 100;
export const NUM_GBUFFERS = 2;

export default class ClusteredRenderer extends BaseRenderer {
constructor(xSlices, ySlices, zSlices) {
Expand All @@ -29,8 +29,14 @@ export default class ClusteredRenderer extends BaseRenderer {
this._progShade = loadShaderProgram(QuadVertSource, fsSource({
numLights: NUM_LIGHTS,
numGBuffers: NUM_GBUFFERS,
numLights_perCluster: MAX_LIGHTS_PER_CLUSTER,
slices_x: xSlices,
slices_y: ySlices,
slices_z: zSlices
}), {
uniforms: ['u_gbuffers[0]', 'u_gbuffers[1]', 'u_gbuffers[2]', 'u_gbuffers[3]'],
uniforms: ['u_gbuffers[0]', 'u_gbuffers[1]', 'u_gbuffers[2]', 'u_gbuffers[3]',
'u_view_matrix', 'u_view_matrix_inverse', 'u_screen_dimensions', 'u_near_clip', 'u_far_clip',
'u_clusterbuffer', 'u_lightbuffer'],
attribs: ['a_uv'],
});

Expand Down Expand Up @@ -107,6 +113,8 @@ export default class ClusteredRenderer extends BaseRenderer {
// Update the camera matrices
camera.updateMatrixWorld();
mat4.invert(this._viewMatrix, camera.matrixWorld.elements);
let inv_mat = mat4.create();
mat4.invert(inv_mat, this._viewMatrix);
mat4.copy(this._projectionMatrix, camera.projectionMatrix.elements);
mat4.multiply(this._viewProjectionMatrix, this._projectionMatrix, this._viewMatrix);

Expand All @@ -124,6 +132,7 @@ export default class ClusteredRenderer extends BaseRenderer {

// Upload the camera matrix
gl.uniformMatrix4fv(this._progCopy.u_viewProjectionMatrix, false, this._viewProjectionMatrix);
gl.uniformMatrix4fv(this._progCopy.u_view_matrix, false, this._viewMatrix);

// Draw the scene. This function takes the shader program so that the model's textures can be bound to the right inputs
scene.draw(this._progCopy);
Expand Down Expand Up @@ -154,7 +163,22 @@ export default class ClusteredRenderer extends BaseRenderer {
// Use this shader program
gl.useProgram(this._progShade.glShaderProgram);

// TODO: Bind any other shader inputs
// Set the light texture as a uniform input to the shader
gl.activeTexture(gl.TEXTURE2);
gl.bindTexture(gl.TEXTURE_2D, this._lightTexture.glTexture);
gl.uniform1i(this._progShade.u_lightbuffer, 2);

// Set the cluster texture as a uniform input to the shader
gl.activeTexture(gl.TEXTURE3);
gl.bindTexture(gl.TEXTURE_2D, this._clusterTexture.glTexture);
gl.uniform1i(this._progShade.u_clusterbuffer, 3);

// additional attributes
gl.uniformMatrix4fv(this._progShade.u_view_matrix, false, this._viewMatrix);
gl.uniform1f(this._progShade.u_near_clip, false, camera.near);
gl.uniform1f(this._progShade.u_far_clip, false, camera.far);
gl.uniform2f(this._progShade.u_screen_dimensions, false, canvas.width, canvas.height);
gl.uniformMatrix4fv(this._progShade.u_view_matrix_inverse, false, inv_mat);

// Bind g-buffers
const firstGBufferBinding = 0; // You may have to change this if you use other texture slots
Expand Down
2 changes: 1 addition & 1 deletion src/renderers/forwardPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ export default class ForwardPlusRenderer extends BaseRenderer {
slices_z: zSlices
}), {
uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap', 'u_lightbuffer', 'u_clusterbuffer',
'u_view_matrix', 'u_screen_dimensions', 'u_near_clip', 'u_far_clip', 'u_slices'],
'u_view_matrix', 'u_screen_dimensions', 'u_near_clip', 'u_far_clip'],
attribs: ['a_position', 'a_normal', 'a_uv'],
});

Expand Down
Loading

0 comments on commit 6f0d1e6

Please sign in to comment.