-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Update filenames, instructions, and variables with clearer language
- Loading branch information
1 parent
e0e02e2
commit 59445f2
Showing
12 changed files
with
218 additions
and
219 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
node_modules | ||
node_modules | ||
.idea |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
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; | ||
} | ||
|
||
updateClusters(camera, viewMatrix, 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... | ||
|
||
for (let z = 0; z < this._zSlices; ++z) { | ||
for (let y = 0; y < this._ySlices; ++y) { | ||
for (let x = 0; x < this._xSlices; ++x) { | ||
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; | ||
} | ||
} | ||
} | ||
|
||
this._clusterTexture.update(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,168 @@ | ||
import { mat4, vec4, vec3 } from 'gl-matrix'; | ||
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 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 MAX_LIGHTS_PER_CLUSTER = 100; | ||
export const NUM_GBUFFERS = 4; | ||
|
||
export default class ClusteredRenderer { | ||
export default class ClusteredRenderer extends 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; | ||
super(xSlices, ySlices, zSlices); | ||
|
||
this.setupDrawBuffers(canvas.width, canvas.height); | ||
|
||
// Create a texture to store light data | ||
this._lightTexture = new TextureBuffer(NUM_LIGHTS, 8); | ||
|
||
this._progCopy = loadShaderProgram(toTextureVert, toTextureFrag, { | ||
uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap'], | ||
attribs: ['a_position', 'a_normal', 'a_uv'], | ||
}); | ||
|
||
this._progShade = loadShaderProgram(QuadVertSource, fsSource({ | ||
numLights: NUM_LIGHTS, | ||
numGBuffers: NUM_GBUFFERS, | ||
}), { | ||
uniforms: ['u_gbuffers[0]', 'u_gbuffers[1]', 'u_gbuffers[2]', 'u_gbuffers[3]'], | ||
attribs: ['a_uv'], | ||
}); | ||
|
||
this._projectionMatrix = mat4.create(); | ||
this._viewMatrix = mat4.create(); | ||
this._viewProjectionMatrix = mat4.create(); | ||
} | ||
|
||
setupDrawBuffers(width, height) { | ||
this._width = width; | ||
this._height = height; | ||
|
||
this._fbo = gl.createFramebuffer(); | ||
|
||
//Create, bind, and store a depth target texture for the FBO | ||
this._depthTex = gl.createTexture(); | ||
gl.bindTexture(gl.TEXTURE_2D, this._depthTex); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null); | ||
gl.bindTexture(gl.TEXTURE_2D, null); | ||
|
||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo); | ||
gl.framebufferTexture2D(gl.FRAMEBUFFER, gl.DEPTH_ATTACHMENT, gl.TEXTURE_2D, this._depthTex, 0); | ||
|
||
// Create, bind, and store "color" target textures for the FBO | ||
this._gbuffers = new Array(NUM_GBUFFERS); | ||
let attachments = new Array(NUM_GBUFFERS); | ||
for (let i = 0; i < NUM_GBUFFERS; i++) { | ||
attachments[i] = WEBGL_draw_buffers[`COLOR_ATTACHMENT${i}_WEBGL`]; | ||
this._gbuffers[i] = gl.createTexture(); | ||
gl.bindTexture(gl.TEXTURE_2D, this._gbuffers[i]); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE); | ||
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE); | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null); | ||
gl.bindTexture(gl.TEXTURE_2D, null); | ||
|
||
gl.framebufferTexture2D(gl.FRAMEBUFFER, attachments[i], gl.TEXTURE_2D, this._gbuffers[i], 0); | ||
} | ||
|
||
if (gl.checkFramebufferStatus(gl.FRAMEBUFFER) != gl.FRAMEBUFFER_COMPLETE) { | ||
throw "Framebuffer incomplete"; | ||
} | ||
|
||
// Tell the WEBGL_draw_buffers extension which FBO attachments are | ||
// being used. (This extension allows for multiple render targets.) | ||
WEBGL_draw_buffers.drawBuffersWEBGL(attachments); | ||
|
||
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | ||
} | ||
|
||
resize(width, height) { | ||
this._width = width; | ||
this._height = height; | ||
|
||
gl.bindTexture(gl.TEXTURE_2D, this._depthTex); | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.DEPTH_COMPONENT, width, height, 0, gl.DEPTH_COMPONENT, gl.UNSIGNED_SHORT, null); | ||
for (let i = 0; i < NUM_GBUFFERS; i++) { | ||
gl.bindTexture(gl.TEXTURE_2D, this._gbuffers[i]); | ||
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, width, height, 0, gl.RGBA, gl.FLOAT, null); | ||
} | ||
gl.bindTexture(gl.TEXTURE_2D, null); | ||
} | ||
|
||
updateClusters(camera, viewMatrix, 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... | ||
|
||
for (let z = 0; z < this._zSlices; ++z) { | ||
for (let y = 0; y < this._ySlices; ++y) { | ||
for (let x = 0; x < this._xSlices; ++x) { | ||
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; | ||
} | ||
} | ||
render(camera, scene) { | ||
if (canvas.width != this._width || canvas.height != this._height) { | ||
this.resize(canvas.width, canvas.height); | ||
} | ||
|
||
// Update the camera matrices | ||
camera.updateMatrixWorld(); | ||
mat4.invert(this._viewMatrix, camera.matrixWorld.elements); | ||
mat4.copy(this._projectionMatrix, camera.projectionMatrix.elements); | ||
mat4.multiply(this._viewProjectionMatrix, this._projectionMatrix, this._viewMatrix); | ||
|
||
// Render to the whole screen | ||
gl.viewport(0, 0, canvas.width, canvas.height); | ||
|
||
// Bind the framebuffer | ||
gl.bindFramebuffer(gl.FRAMEBUFFER, this._fbo); | ||
|
||
// Clear the frame | ||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | ||
|
||
// Use the shader program to copy to the draw buffers | ||
gl.useProgram(this._progCopy.glShaderProgram); | ||
|
||
// Upload the camera matrix | ||
gl.uniformMatrix4fv(this._progCopy.u_viewProjectionMatrix, false, this._viewProjectionMatrix); | ||
|
||
// 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); | ||
|
||
// Update the buffer used to populate the texture packed with light data | ||
for (let i = 0; i < NUM_LIGHTS; ++i) { | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 0) + 0] = scene.lights[i].position[0]; | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 0) + 1] = scene.lights[i].position[1]; | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 0) + 2] = scene.lights[i].position[2]; | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 0) + 3] = scene.lights[i].radius; | ||
|
||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 1) + 0] = scene.lights[i].color[0]; | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 1) + 1] = scene.lights[i].color[1]; | ||
this._lightTexture.buffer[this._lightTexture.bufferIndex(i, 1) + 2] = scene.lights[i].color[2]; | ||
} | ||
// Update the light texture | ||
this._lightTexture.update(); | ||
|
||
// Update the clusters for the frame | ||
this.updateClusters(camera, this._viewMatrix, scene); | ||
|
||
// Bind the default null framebuffer which is the screen | ||
gl.bindFramebuffer(gl.FRAMEBUFFER, null); | ||
|
||
// Clear the frame | ||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); | ||
|
||
// Use this shader program | ||
gl.useProgram(this._progShade.glShaderProgram); | ||
|
||
// TODO: Bind any other shader inputs | ||
|
||
// Bind g-buffers | ||
const firstGBufferBinding = 0; // You may have to change this if you use other texture slots | ||
for (let i = 0; i < NUM_GBUFFERS; i++) { | ||
gl.activeTexture(gl[`TEXTURE${i + firstGBufferBinding}`]); | ||
gl.bindTexture(gl.TEXTURE_2D, this._gbuffers[i]); | ||
gl.uniform1i(this._progShade[`u_gbuffers[${i}]`], i + firstGBufferBinding); | ||
} | ||
|
||
this._clusterTexture.update(); | ||
renderFullscreenQuad(this._progShade); | ||
} | ||
} | ||
}; |
Oops, something went wrong.