Skip to content
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
21 changes: 9 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,26 +3,23 @@ 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)

### Live Online

[![](img/thumb.png)](http://TODO.github.io/Project5-WebGL-Forward-Plus-and-Clustered-Deferred)
* Name: Gizem Dal
* [LinkedIn](https://www.linkedin.com/in/gizemdal), [personal website](https://www.gizemdal.com/)
* Tested on: Predator G3-571 Intel(R) Core(TM) i7-7700HQ CPU @ 2.80 GHz 2.81 GHz - Personal computer

### Demo Video/GIF

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

### (TODO: Your README)
## Project Description ##

*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 focuses on implementing different rendering techniques to improve efficiency for scenes with many dynamic light sources. Due to time constraints, this project is still in the works but I will be submitting my progress so far.

This assignment has a considerable amount of performance analysis compared
to implementation work. Complete the implementation early to leave time!
### Features (In progress)

* Forward Rendering
* Forward+ Rendering
* Deferred Rendering

### Credits

Expand Down
149 changes: 145 additions & 4 deletions src/main.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { makeRenderLoop, camera, cameraControls, gui, gl } from './init';
import { makeRenderLoop, camera, cameraControls, gui, gl, canvas } from './init';
import ForwardRenderer from './renderers/forward';
import ForwardPlusRenderer from './renderers/forwardPlus';
import ClusteredDeferredRenderer from './renderers/clusteredDeferred';
import Scene from './scene';
import Wireframe from './wireframe';
import { mat4, vec3 } from 'gl-matrix';

const FORWARD = 'Forward';
const FORWARD_PLUS = 'Forward+';
Expand Down Expand Up @@ -35,6 +36,9 @@ gui.add(params, 'renderer', [FORWARD, FORWARD_PLUS, CLUSTERED]).onChange(setRend
const scene = new Scene();
scene.loadGLTF('models/sponza/sponza.gltf');

camera.position.set(-10, 8, 0);
cameraControls.target.set(0, 2, 0);

// LOOK: The Wireframe class is for debugging.
// It lets you draw arbitrary lines in the scene.
// This may be helpful for visualizing your frustum clusters so you can make
Expand All @@ -45,10 +49,147 @@ 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([-14.0, 1.0, -6.0], [14.0, 21.0, 6.0], [0.0, 1.0, 1.0]);

// create test frustum
var x_size = canvas.clientWidth / params._renderer._xSlices;
var y_size = canvas.clientHeight / params._renderer._ySlices;
var z_size = 10 / params._renderer._zSlices; // hardcode 200 - maybe change it later

// Frustum plane normals
//var nor_top = new Vector3(0, -1, 0);
//var nor_bottom = new Vector3(0, 1, 0);
//var nor_left = new Vector3(-1, 0, 0);
//var nor_right = new Vector3(1, 0, 0);
//var nor_near = new Vector3(0, 0, 1);
//var nor_far = new Vector3(0, 0, -1);

// Compute view and projection matrices
var view_translate = mat4.fromValues(1, 0, 0, 0,
0, 1, 0, 0,
0, 0, 1, 0,
-camera.position.x, -camera.position.y, -camera.position.z, 1);
var look = vec3.normalize(vec3.fromValues(0, 0, 0), vec3.fromValues(cameraControls.target.x - camera.position.x, cameraControls.target.y - camera.position.y, cameraControls.target.z - camera.position.z));
var world_up = vec3.fromValues(0, 1, 0);
var right = vec3.cross(vec3.fromValues(0, 0, 0), look, world_up); // camera's right vector
var up = vec3.cross(vec3.fromValues(0, 0, 0), right, look); // camera's up vector
var view_orient = mat4.fromValues(right[0], up[0], look[0], 0,
right[1], up[1], look[1], 0,
right[2], up[2], look[2], 0,
0, 0, 0, 1);

var view_mat = mat4.multiply(mat4.create(), view_orient, view_translate);
var proj_mat = mat4.fromValues(camera.projectionMatrix.elements[0], camera.projectionMatrix.elements[1], camera.projectionMatrix.elements[2], camera.projectionMatrix.elements[3],
camera.projectionMatrix.elements[4], camera.projectionMatrix.elements[5], camera.projectionMatrix.elements[6], camera.projectionMatrix.elements[7],
camera.projectionMatrix.elements[8], camera.projectionMatrix.elements[9], camera.projectionMatrix.elements[10], camera.projectionMatrix.elements[11],
camera.projectionMatrix.elements[12], camera.projectionMatrix.elements[13], camera.projectionMatrix.elements[14], camera.projectionMatrix.elements[15]);
/*var proj_mat = mat4.fromValues(1.0 / (camera.aspect * Math.tan(camera.fov / 2)), 0, 0, 0,
0, 1.0 / Math.tan(camera.fov / 2), 0, 0,
0, 0, camera.far / (camera.far - camera.near), 1.0,
0, 0, -(camera.far * camera.near) / (camera.far - camera.near), 0);*/
var proj_mat_inv = mat4.invert(mat4.create(), proj_mat);
var view_mat_inv = mat4.invert(mat4.create(), view_mat);
for (let z = 0; z < 10 - z_size; z += z_size) {
for (let y = 0; y < canvas.clientHeight - y_size; y += y_size) {
for (let x = 0; x < canvas.clientWidth - x_size; x += x_size) {
// screen space coordinates
var sx0 = (x / canvas.clientWidth) * 2 - 1;
var sy0 = 1 - (y / canvas.clientHeight) * 2;
var sx1 = ((x + x_size) / canvas.clientWidth) * 2 - 1;
var sy1 = 1 - ((y + y_size) / canvas.clientHeight) * 2;
// bottom edges
var segment0 = vec3.fromValues(sx0, sy0, z);
var segment1 = vec3.fromValues(sx0, sy0, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx1, sy0, z);
segment1 = vec3.fromValues(sx1, sy0, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx0, sy0, z);
segment1 = vec3.fromValues(sx1, sy0, z);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx0, sy0, z + z_size);
segment1 = vec3.fromValues(sx1, sy0, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 1.0, 0.0]);

// sides
segment0 = vec3.fromValues(sx0, sy0, z);
segment1 = vec3.fromValues(sx0, sy1, z);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 0.0, 1.0]);
segment0 = vec3.fromValues(sx0, sy0, z + z_size);
segment1 = vec3.fromValues(sx0, sy1, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 0.0, 1.0]);
segment0 = vec3.fromValues(sx1, sy0, z);
segment1 = vec3.fromValues(sx1, sy1, z);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 0.0, 1.0]);
segment0 = vec3.fromValues(sx1, sy0, z + z_size);
segment1 = vec3.fromValues(sx1, sy1, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [1.0, 0.0, 1.0]);

// top edges
segment0 = vec3.fromValues(sx0, sy1, z);
segment1 = vec3.fromValues(sx0, sy1, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [0.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx1, sy1, z);
segment1 = vec3.fromValues(sx1, sy1, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [0.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx0, sy1, z);
segment1 = vec3.fromValues(sx1, sy1, z);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [0.0, 1.0, 0.0]);
segment0 = vec3.fromValues(sx0, sy1, z + z_size);
segment1 = vec3.fromValues(sx1, sy1, z + z_size);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, proj_mat_inv);
segment0 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment0, view_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, proj_mat_inv);
segment1 = vec3.transformMat4(vec3.fromValues(0, 0, 0), segment1, view_mat_inv);
wireframe.addLineSegment([segment0[0], segment0[1], segment0[2]], [segment1[0], segment1[1], segment1[2]], [0.0, 1.0, 0.0]);
}
}
}

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

function render() {
Expand Down
21 changes: 21 additions & 0 deletions src/renderers/base.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { canvas } from '../init';
import TextureBuffer from './textureBuffer';
import { Plane, Sphere, Frustum, Vector3 } from 'three';

export const MAX_LIGHTS_PER_CLUSTER = 100;

Expand All @@ -25,6 +27,25 @@ export default class BaseRenderer {
}
}

var x_size = canvas.clientWidth / this._xSlices;
var y_size = canvas.clientHeight / this._ySlices;
var z_size = 200 / this._zSlices; // hardcode 200 - maybe change it later

// Frustum plane normals
var nor_top = new Vector3(0, -1, 0);
var nor_bottom = new Vector3(0, 1, 0);
var nor_left = new Vector3(-1, 0, 0);
var nor_right = new Vector3(1, 0, 0);
var nor_near = new Vector3(0, 0, 1);
var nor_far = new Vector3(0, 0, -1);

for (let z = 0; z < 200 - z_size; z += z_size) {
for (let y = 0; y < canvas.clientHeight - y_size; y += y_size) {
for (let x = 0; x < canvas.clientWidth - x_size; x += x_size) {
}
}
}

this._clusterTexture.update();
}
}
6 changes: 5 additions & 1 deletion src/renderers/clusteredDeferred.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export default class ClusteredDeferredRenderer extends BaseRenderer {
this._lightTexture = new TextureBuffer(NUM_LIGHTS, 8);

this._progCopy = loadShaderProgram(toTextureVert, toTextureFrag, {
uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap'],
uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap', 'u_lightbuffer'],
attribs: ['a_position', 'a_normal', 'a_uv'],
});

Expand Down Expand Up @@ -154,6 +154,10 @@ export default class ClusteredDeferredRenderer extends BaseRenderer {
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);

// Bind g-buffers
const firstGBufferBinding = 0; // You may have to change this if you use other texture slots
Expand Down
78 changes: 73 additions & 5 deletions src/shaders/deferred.frag.glsl.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,85 @@ export default function(params) {
precision highp float;

uniform sampler2D u_gbuffers[${params.numGBuffers}];
uniform sampler2D u_lightbuffer;

varying vec2 v_uv;

struct Light {
vec3 position;
float radius;
vec3 color;
};

float ExtractFloat(sampler2D texture, int textureWidth, int textureHeight, int index, int component) {
float u = float(index + 1) / float(textureWidth + 1);
int pixel = component / 4;
float v = float(pixel + 1) / float(textureHeight + 1);
vec4 texel = texture2D(texture, vec2(u, v));
int pixelComponent = component - pixel * 4;
if (pixelComponent == 0) {
return texel[0];
} else if (pixelComponent == 1) {
return texel[1];
} else if (pixelComponent == 2) {
return texel[2];
} else if (pixelComponent == 3) {
return texel[3];
}
}

Light UnpackLight(int index) {
Light light;
float u = float(index + 1) / float(${params.numLights + 1});
vec4 v1 = texture2D(u_lightbuffer, vec2(u, 0.0));
vec4 v2 = texture2D(u_lightbuffer, vec2(u, 0.5));
light.position = v1.xyz;

// LOOK: This extracts the 4th float (radius) of the (index)th light in the buffer
// Note that this is just an example implementation to extract one float.
// There are more efficient ways if you need adjacent values
light.radius = ExtractFloat(u_lightbuffer, ${params.numLights}, 2, index, 3);

light.color = v2.rgb;
return light;
}

// Cubic approximation of gaussian curve so we falloff to exactly 0 at the light radius
float cubicGaussian(float h) {
if (h < 1.0) {
return 0.25 * pow(2.0 - h, 3.0) - pow(1.0 - h, 3.0);
} else if (h < 2.0) {
return 0.25 * pow(2.0 - h, 3.0);
} else {
return 0.0;
}
}

void main() {
// TODO: extract data from g buffers and do lighting
// vec4 gb0 = texture2D(u_gbuffers[0], v_uv);
// vec4 gb1 = texture2D(u_gbuffers[1], v_uv);
// vec4 gb2 = texture2D(u_gbuffers[2], v_uv);
// vec4 gb3 = texture2D(u_gbuffers[3], v_uv);
vec4 v_pos = texture2D(u_gbuffers[0], v_uv);
vec4 v_nor = texture2D(u_gbuffers[1], v_uv);
vec4 v_col = texture2D(u_gbuffers[2], v_uv);
vec4 v_copy_uv = texture2D(u_gbuffers[3], v_uv);

vec3 fragColor = vec3(0.0);

for (int i = 0; i < ${params.numLights}; ++i) {
Light light = UnpackLight(i);
float lightDistance = distance(light.position, v_pos.xyz);
vec3 L = (light.position - v_pos.xyz) / lightDistance;
float lightIntensity = cubicGaussian(2.0 * lightDistance / light.radius);
float lambertTerm = max(dot(L, v_nor.xyz), 0.0);

fragColor += v_col.xyz * lambertTerm * light.color * vec3(lightIntensity);
}

const vec3 ambientLight = vec3(0.025);
fragColor += v_col.xyz * ambientLight;

//fragColor = clamp(v_pos.xyz / 20.0, 0.0, 1.0);

gl_FragColor = vec4(v_uv, 0.0, 1.0);
gl_FragColor = vec4(fragColor, 1.0);
}
`;
}
12 changes: 6 additions & 6 deletions src/shaders/deferredToTexture.frag.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ vec3 applyNormalMap(vec3 geomnor, vec3 normap) {
}

void main() {
vec3 norm = applyNormalMap(v_normal, vec3(texture2D(u_normap, v_uv)));
vec3 col = vec3(texture2D(u_colmap, v_uv));
vec3 norm = applyNormalMap(v_normal, texture2D(u_normap, v_uv).xyz);
vec3 col = texture2D(u_colmap, v_uv).rgb;

// TODO: populate your g buffer
// gl_FragData[0] = ??
// gl_FragData[1] = ??
// gl_FragData[2] = ??
// gl_FragData[3] = ??
gl_FragData[0] = vec4(v_position, 1.0);
gl_FragData[1] = vec4(norm, 1.0);
gl_FragData[2] = vec4(col, 1.0);
gl_FragData[3] = vec4(v_uv, 1.0, 1.0);
}