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: Matt Elser #4

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 12 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,22 @@ WebGL Forward+ and Clustered 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)
* Matt Elser
* Tested on: **Google Chrome 95.0.4638** on 2019 MacBook Pro

### Live Online
[![](img/demo.gif)](https://mattelser.github.io/Project5-WebGL-Forward-Plus-and-Clustered-Deferred/index.html)

[![](img/thumb.png)](http://TODO.github.io/Project5-WebGL-Forward-Plus-and-Clustered-Deferred)
### What's implemented
The cluster frustums are successfully created, as can be seen when turning wireframe on:
![](img/frustum_side.png)
![](img/frustum_lookthrough.png)

### Demo Video/GIF

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

### (TODO: Your README)

*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 assignment has a considerable amount of performance analysis compared
to implementation work. Complete the implementation early to leave time!
lights are correctly associated to the intersection frustum(s), and stored in the light cluster texture.

### Hypothetical performance comparison
Based on what was shown in class, Forward+ should be significantly faster than forward shading for non-trivial numbers of lights. Forward+ can be made slower by larger light radii (a larger radius means lights will intersect more frustums, counteracting the optimization), but it should still generally be notably faster than forward.
Clustered shading would further optimize forward+, particularly for scenes with significant distances between objects in the foreground and background, by splitting the clusters up along the camera's z-axis.
Both of these algorithms trade a more complicated shading calculation for more efficient scene processing.

### Credits

Expand Down
Binary file added img/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 img/frustum_lookthrough.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 img/frustum_side.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
22 changes: 17 additions & 5 deletions src/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function setRenderer(renderer) {
params._renderer = new ForwardRenderer();
break;
case FORWARD_PLUS:
params._renderer = new ForwardPlusRenderer(15, 15, 15);
params._renderer = new ForwardPlusRenderer(3, 3, 3);
break;
case CLUSTERED:
params._renderer = new ClusteredDeferredRenderer(15, 15, 15);
Expand All @@ -41,15 +41,18 @@ scene.loadGLTF('models/sponza/sponza.gltf');
// sure that they are in the right place.
const wireframe = new Wireframe();

var segmentStart = [-14.0, 0.0, -6.0];
var segmentEnd = [14.0, 20.0, 6.0];
//var segmentStart = [-14.0, 0.0, -6.0];
//var segmentEnd = [14.0, 20.0, 6.0];
var segmentColor = [1.0, 0.0, 0.0];
wireframe.addLineSegment(segmentStart, segmentEnd, segmentColor);
wireframe.addLineSegment([-14.0, 1.0, -6.0], [14.0, 21.0, 6.0], [0.0, 1.0, 0.0]);
//wireframe.addLineSegment(segmentStart, segmentEnd, segmentColor);
//wireframe.addLineSegment([-14.0, 1.0, -6.0], [14.0, 21.0, 6.0], [0.0, 1.0, 0.0]);
//console.log(LINES.length);
//console.table(LINES);

camera.position.set(-10, 8, 0);
cameraControls.target.set(0, 2, 0);
gl.enable(gl.DEPTH_TEST);
var haveDrawnLines = false;

function render() {
scene.update();
Expand All @@ -59,6 +62,15 @@ function render() {
// If you would like the wireframe to render behind and in front
// of objects based on relative depths in the scene, comment out /
//the gl.disable(gl.DEPTH_TEST) and gl.enable(gl.DEPTH_TEST) lines.
var lines = params._renderer.lines;
var segmentColor = [1.0, 0.0, 1.0];
//console.log(lines);
if (!haveDrawnLines){
for (let i = 0; i < lines.length; i++){
wireframe.addLineSegment(lines[i][0], lines[i][1], segmentColor);
}
haveDrawnLines = true;
}
gl.disable(gl.DEPTH_TEST);
wireframe.render(camera);
gl.enable(gl.DEPTH_TEST);
Expand Down
124 changes: 122 additions & 2 deletions src/renderers/base.js
Original file line number Diff line number Diff line change
@@ -1,30 +1,150 @@
import { mat4 } from 'gl-matrix';
import { Vector3 , Vector4, Matrix4, LineSegments, Quaternion, Plane, Frustum, Sphere} from 'three';
import Wireframe from '../wireframe';
import TextureBuffer from './textureBuffer';

export const MAX_LIGHTS_PER_CLUSTER = 100;



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
this._clusterTexture = new TextureBuffer(xSlices * ySlices * zSlices, MAX_LIGHTS_PER_CLUSTER + 1);
this._xSlices = xSlices;
this._ySlices = ySlices;
this._zSlices = zSlices;

this.lines = [];
}

frustumPointsToWireframe(points){
// store frustum edges in list of lines to
// be drawn in wireframe
for (let i = 1; i < 8; i++){
let p1 = [points[i-1].x, points[i-1].y, points[i-1].z]
let p2 = [points[i].x, points[i].y, points[i].z]
this.lines.push([p1, p2]);
}
let p1 = [points[0].x, points[0].y, points[0].z]
let p2 = [points[3].x, points[3].y, points[3].z]
this.lines.push([p1, p2]);

for (let i = 0; i < 4; i++){
let p1 = [points[i].x, points[i].y, points[i].z]
let p2 = [points[i+4].x, points[i+4].y, points[i+4].z]
this.lines.push([p1, p2]);
}
//console.log(this.lines);

}

getFrustumPoints(ivp, nearClipDist, farClipDist, xPos, yPos, zPos, xScale, yScale, zScale){

// set up the corners of our frustum
var frustumCornersRaw = [new Vector4(-1.0, 1.0, nearClipDist, 1),
new Vector4( 1.0, 1.0, nearClipDist, 1),
new Vector4( 1.0, -1.0, nearClipDist, 1),
new Vector4(-1.0, -1.0, nearClipDist, 1),
new Vector4(-1.0, 1.0, farClipDist, 1),
new Vector4( 1.0, 1.0, farClipDist, 1),
new Vector4( 1.0, -1.0, farClipDist, 1),
new Vector4(-1.0, -1.0, farClipDist, 1)];
var frustumCorners = [];

//let frustumCorners = [];
let transScale = new Matrix4;
transScale.compose(new Vector3(xPos, yPos, zPos),
new Quaternion(),
new Vector3(xScale, yScale, zScale));

updateClusters(camera, viewMatrix, scene) {
// translate those corners into world space
//console.table(transScale);
for (let i = 0; i < 8; i++){
frustumCornersRaw[i].applyMatrix4(transScale);
frustumCornersRaw[i].applyMatrix4(ivp);
//frustumCorners[i] = new Vector3(frustumCorners[i]/frustumCorners[i][3]);
//console.log(frustumCorners[i]);
frustumCornersRaw[i].divideScalar(frustumCornersRaw[i].w);
frustumCorners.push(new Vector3(frustumCornersRaw[i].x,
frustumCornersRaw[i].y,
frustumCornersRaw[i].z));
}

return frustumCorners;
}

updateClusters(camera, viewMatrix, inverseViewProjection, scene) {
// TODO: Update the cluster texture with the count and indices of the lights in each cluster
// This will take some time. The math is nontrivial...

var nearClipDist = 0.0001;
var farClipDist = 1;

var ivp = new Matrix4;
ivp.fromArray(inverseViewProjection);
//ivp.set(inverseViewProjection);

//var foo = mat4.fromValues(1, 1, 1,1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4);
//console.table(ivp);

this.lines.length = 0;

//console.log(this._zSlices);
let xScale = 1.0 / this._xSlices;
let yScale = 1.0 / this._ySlices;
let zScale = 1.0 / this._zSlices;

for (let z = 0; z < this._zSlices; ++z) {
let zPos = (z * zScale);
for (let y = 0; y < this._ySlices; ++y) {
let yPos = (2 * y * yScale) - (1 - yScale);
for (let x = 0; x < this._xSlices; ++x) {
let xPos =(2 * x * xScale) - (1 - xScale);
//console.log("x: " + xPos + " y: " + yPos + " z: " + zPos);

let i = x + y * this._xSlices + z * this._xSlices * this._ySlices;
// Reset the light count to 0 for every cluster
this._clusterTexture.buffer[this._clusterTexture.bufferIndex(i, 0)] = 0;

// build a subfrustum for this x,y,z combination
let fp = this.getFrustumPoints(ivp, nearClipDist, farClipDist, xPos, yPos, zPos, xScale, yScale, zScale);
// store the frustum edges in our wireframe buffer
this.frustumPointsToWireframe(fp);

// build planes for three.js frustum
let p0 = new Plane;
let p1 = new Plane;
let p2 = new Plane;
let p3 = new Plane;
let p4 = new Plane;
let p5 = new Plane;
p0.setFromCoplanarPoints(fp[0], fp[1], fp[2]);
p1.setFromCoplanarPoints(fp[4], fp[0], fp[3]);
p2.setFromCoplanarPoints(fp[4], fp[5], fp[1]);
p3.setFromCoplanarPoints(fp[5], fp[6], fp[2]);
p4.setFromCoplanarPoints(fp[6], fp[2], fp[3]);
p5.setFromCoplanarPoints(fp[5], fp[4], fp[7]);

let f = new Frustum(p0, p1, p2, p3, p4, p5);

let li = 0;
let numLightsThisCell = 0;
scene.lights.forEach(l => {
// be sure to check each light, since they can intersect multiple frustums
let p = new Vector3(l.position[0], l.position[1], l.position[2]);
let s = new Sphere(p, l.radius);
if(f.intersectsSphere(s)){
// append the light to this frustum's cluster texture cell
this._clusterTexture.buffer[this._clusterTexture.bufferIndex(i, 0) + numLightsThisCell] = li;
numLightsThisCell++;
}
li++;
});

}
}
}

this._clusterTexture.update();
}
}
5 changes: 4 additions & 1 deletion src/renderers/forwardPlus.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export default class ForwardPlusRenderer extends BaseRenderer {
this._projectionMatrix = mat4.create();
this._viewMatrix = mat4.create();
this._viewProjectionMatrix = mat4.create();
this._inverseViewProjection = mat4.create();
}

render(camera, scene) {
Expand All @@ -32,9 +33,11 @@ export default class ForwardPlusRenderer extends BaseRenderer {
mat4.invert(this._viewMatrix, camera.matrixWorld.elements);
mat4.copy(this._projectionMatrix, camera.projectionMatrix.elements);
mat4.multiply(this._viewProjectionMatrix, this._projectionMatrix, this._viewMatrix);
mat4.invert(this._inverseViewProjection, this._viewProjectionMatrix);

//console.table(this._viewMatrix);
// Update cluster texture which maps from cluster index to light list
this.updateClusters(camera, this._viewMatrix, scene);
this.updateClusters(camera, this._viewMatrix, this._inverseViewProjection, scene);

// Update the buffer used to populate the texture packed with light data
for (let i = 0; i < NUM_LIGHTS; ++i) {
Expand Down