diff --git a/README.md b/README.md index a903608..2a6e8c5 100644 --- a/README.md +++ b/README.md @@ -3,25 +3,40 @@ 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) +* Fengkai Wu +* Tested on: **Google Chrome 61.0.3163.100 (64bit)** on + Windows 10, i7-4700HQ @ 2.40GHz 4GB, GT 745M 2048MB (Personal) -### Live Online +### Demo GIF -[![](img/thumb.png)](http://TODO.github.io/Project5B-WebGL-Deferred-Shading) +[![](https://github.com/wufk/Project5-WebGL-Clustered-Deferred-Forward-Plus/blob/master/img/mygif.gif)]() -### Demo Video/GIF +The gif shows when clustered foward+ mode is switched to the clustered deferred mode. -[![](img/video.png)](TODO) +### Analysis -### (TODO: Your README) +#### Clustered Forward+ +* Divides the space in clusters (or small blocks) and only render when lights overlapped on a cluster. +* Keeps track of the indices of lights based on a data structure to enable fast look up. -*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. +#### Clustered Deferred +* Using clusteres to keep track of lights. (Same as above) +* Stores attributes in g-buffers and renders on the last pass. +* Uses three g buffers to store the attributes. +* Adds Bling-Phong shading model. The effect is shown in the above gif when the reflectivity of lights are changed. -This assignment has a considerable amount of performance analysis compared -to implementation work. Complete the implementation early to leave time! +This figure shows the data g-buffers are storing, which corresponds to albedo, normal and position. +![g-buffers](https://github.com/wufk/Project5-WebGL-Clustered-Deferred-Forward-Plus/blob/master/img/gbuffers.png) + +Below shows the performance using different rendering strategies. + +![](https://github.com/wufk/Project5-WebGL-Clustered-Deferred-Forward-Plus/blob/master/img/numLights.PNG) + +As the graph shows, the rendering time varies significantly with different strategies. Naive forward is the simplest way, which iterates all the lights and does computation on all of them, which is very slow, and the running time increasing very fast when we have more lights to render. + +Clustered forward+ is a good technique, which only computes the lights are overlapped in clusteres, which greately reduces the amount of work. When the number of lights is relatively small. the speed is considerably reduced. + +Clustered deferred stores the vertex attributes in g-buffers and only do the computation at the last pass. This technique is much more faster than the previous two especially when number of lights is large. ### Credits diff --git a/img/albedo.PNG b/img/albedo.PNG new file mode 100644 index 0000000..1ab1ebc Binary files /dev/null and b/img/albedo.PNG differ diff --git a/img/gbuffers.png b/img/gbuffers.png new file mode 100644 index 0000000..1bebeda Binary files /dev/null and b/img/gbuffers.png differ diff --git a/img/mygif.gif b/img/mygif.gif new file mode 100644 index 0000000..492eec6 Binary files /dev/null and b/img/mygif.gif differ diff --git a/img/normal.PNG b/img/normal.PNG new file mode 100644 index 0000000..6ef2c82 Binary files /dev/null and b/img/normal.PNG differ diff --git a/img/numLights.PNG b/img/numLights.PNG new file mode 100644 index 0000000..843bf17 Binary files /dev/null and b/img/numLights.PNG differ diff --git a/img/position.PNG b/img/position.PNG new file mode 100644 index 0000000..e7b0747 Binary files /dev/null and b/img/position.PNG differ diff --git a/img/snapshot.PNG b/img/snapshot.PNG new file mode 100644 index 0000000..a3aa1b9 Binary files /dev/null and b/img/snapshot.PNG differ diff --git a/src/init.js b/src/init.js index 1b09377..d0229bc 100644 --- a/src/init.js +++ b/src/init.js @@ -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'; @@ -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); diff --git a/src/main.js b/src/main.js index 1cbbf9a..d2fe8ae 100644 --- a/src/main.js +++ b/src/main.js @@ -43,4 +43,4 @@ function render() { params._renderer.render(camera, scene); } -makeRenderLoop(render)(); \ No newline at end of file +makeRenderLoop(render)(); diff --git a/src/renderers/clustered.js b/src/renderers/clustered.js index 9521fbd..b913559 100644 --- a/src/renderers/clustered.js +++ b/src/renderers/clustered.js @@ -1,8 +1,8 @@ -import { mat4, vec4, vec3 } from 'gl-matrix'; +import { mat4, vec4, vec3, vec2 } from 'gl-matrix'; import { NUM_LIGHTS } from '../scene'; import TextureBuffer from './textureBuffer'; -export const MAX_LIGHTS_PER_CLUSTER = 100; +export const MAX_LIGHTS_PER_CLUSTER = 1000; export default class ClusteredRenderer { constructor(xSlices, ySlices, zSlices) { @@ -13,7 +13,7 @@ export default class ClusteredRenderer { this._zSlices = zSlices; } - updateClusters(camera, viewMatrix, scene) { + updateClusters(camera, viewMatrix, projectionMatrix, 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... @@ -27,6 +27,72 @@ export default class ClusteredRenderer { } } + const radian = Math.PI / 180.0; + const y_len = Math.tan(camera.fov * 0.5 * radian); + const x_len = y_len * camera.aspect; + const z_step = (camera.far - camera.near) / this._zSlices; + + for(let i = 0; i < NUM_LIGHTS; i++) { + let radius = scene.lights[i].radius; + let light_pos = vec4.fromValues(scene.lights[i].position[0], + scene.lights[i].position[1], + scene.lights[i].position[2], 1.0); + vec4.transformMat4(light_pos, light_pos, viewMatrix); + light_pos[2] *= -1.0; + + let light_x_len = y_len*light_pos[2]; + let light_y_len = x_len*light_pos[2]; + let y_step = 2 * light_x_len / this._ySlices; + let x_step = 2 * light_y_len / this._xSlices; + let lightPosNewY = light_x_len + light_pos[1]; + let lightPosNewX = light_y_len + light_pos[0]; + + + let lightStartY = lightPosNewY - radius; + let lightStopY = lightPosNewY + radius; + let lightStartX = lightPosNewX - radius; + let lightStopX = lightPosNewX + radius; + + let y_lo = Math.floor(lightStartY / y_step); + let y_hi = Math.floor(lightStopY / y_step); + let x_lo = Math.floor(lightStartX / x_step); + let x_hi = Math.floor(lightStopX / x_step); + + if(y_lo > this._ySlices-1 || y_hi < 0) continue; + if(x_lo > this._xSlices-1 || x_hi < 0) { continue; } + y_lo = Math.max(0, y_lo); + x_lo = Math.max(0, x_lo); + y_hi = Math.min(this._ySlices, y_hi); + x_hi = Math.min(this._xSlices, x_hi); + + let lightPosNewZ = light_pos[2] - camera.near; + let lightStartZ = lightPosNewZ - radius; + let lightStopZ = lightPosNewZ + radius; + let z_lo = Math.floor(lightStartZ / z_step); + let z_hi = Math.floor(lightStopZ / z_step)+1; + if(z_lo > this._zSlices-1 || z_hi < 0) { continue; } + z_lo = Math.max(0, z_lo); + z_hi = Math.min(this._zSlices, z_hi); + + for(let x = x_lo; x <= x_hi; ++x) { + for(let y = y_lo; y < y_hi; ++y) { + for(let z = z_lo; z < z_hi; ++z) { + let cluster_idx = x + y*this._xSlices + z*this._xSlices*this._ySlices; + let light_num_idx = this._clusterTexture.bufferIndex(cluster_idx, 0); + let light_nums = 1 + this._clusterTexture.buffer[light_num_idx]; + + if(light_nums < MAX_LIGHTS_PER_CLUSTER) { + this._clusterTexture.buffer[light_num_idx] = light_nums; + let light_texel = Math.floor(light_nums*0.25); + let texel_idx = this._clusterTexture.bufferIndex(cluster_idx, light_texel); + let comp_idx = light_nums - light_texel*4; + this._clusterTexture.buffer[texel_idx+comp_idx] = i; + } + } + } + } + } + this._clusterTexture.update(); } -} \ No newline at end of file +} diff --git a/src/renderers/clusteredDeferred.js b/src/renderers/clusteredDeferred.js index 5e28e84..607ec71 100644 --- a/src/renderers/clusteredDeferred.js +++ b/src/renderers/clusteredDeferred.js @@ -1,5 +1,5 @@ import { gl, WEBGL_draw_buffers, canvas } from '../init'; -import { mat4, vec4 } from 'gl-matrix'; +import { mat4, vec4, vec3 } from 'gl-matrix'; import { loadShaderProgram, renderFullscreenQuad } from '../utils'; import { NUM_LIGHTS } from '../scene'; import toTextureVert from '../shaders/deferredToTexture.vert.glsl'; @@ -8,8 +8,9 @@ import QuadVertSource from '../shaders/quad.vert.glsl'; import fsSource from '../shaders/deferred.frag.glsl.js'; import TextureBuffer from './textureBuffer'; import ClusteredRenderer from './clustered'; +import {MAX_LIGHTS_PER_CLUSTER} from './clustered'; -export const NUM_GBUFFERS = 4; +export const NUM_GBUFFERS = 3; export default class ClusteredDeferredRenderer extends ClusteredRenderer { constructor(xSlices, ySlices, zSlices) { @@ -21,15 +22,23 @@ export default class ClusteredDeferredRenderer extends ClusteredRenderer { this._lightTexture = new TextureBuffer(NUM_LIGHTS, 8); this._progCopy = loadShaderProgram(toTextureVert, toTextureFrag, { - uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap'], + uniforms: ['u_viewProjectionMatrix', 'u_viewMatrix', 'u_colmap', 'u_normap'], attribs: ['a_position', 'a_normal', 'a_uv'], }); this._progShade = loadShaderProgram(QuadVertSource, fsSource({ - numLights: NUM_LIGHTS, - numGBuffers: NUM_GBUFFERS, + num_lights: NUM_LIGHTS, + num_gbuffers: NUM_GBUFFERS, + xSlices: xSlices, + ySlices: ySlices, + zSlices: zSlices, + maxLightsPerCluster: MAX_LIGHTS_PER_CLUSTER, }), { - uniforms: ['u_gbuffers[0]', 'u_gbuffers[1]', 'u_gbuffers[2]', 'u_gbuffers[3]'], + uniforms: [ + 'u_gbuffers[0]', 'u_gbuffers[1]', 'u_gbuffers[2]', + 'u_viewMatrix', 'u_invViewMatrix', + 'u_clusterbuffer', 'u_lightbuffer', + 'u_width', 'u_height','u_near','u_far'], attribs: ['a_uv'], }); @@ -123,6 +132,7 @@ export default class ClusteredDeferredRenderer extends ClusteredRenderer { // Upload the camera matrix gl.uniformMatrix4fv(this._progCopy.u_viewProjectionMatrix, false, this._viewProjectionMatrix); + gl.uniformMatrix4fv(this._progCopy.u_viewMatrix, 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); @@ -142,7 +152,7 @@ export default class ClusteredDeferredRenderer extends ClusteredRenderer { this._lightTexture.update(); // Update the clusters for the frame - this.updateClusters(camera, this._viewMatrix, scene); + this.updateClusters(camera, this._viewMatrix, this._projectionMatrix, scene); // Bind the default null framebuffer which is the screen gl.bindFramebuffer(gl.FRAMEBUFFER, null); @@ -154,6 +164,22 @@ export default class ClusteredDeferredRenderer extends ClusteredRenderer { gl.useProgram(this._progShade.glShaderProgram); // TODO: Bind any other shader inputs + gl.uniformMatrix4fv(this._progShade.u_viewMatrix, false, this._viewMatrix); + gl.uniformMatrix4fv(this._progShade.u_invViewMatrix, false, camera.matrixWorld.elements); + + gl.activeTexture(gl.TEXTURE3); + gl.bindTexture(gl.TEXTURE_2D, this._clusterTexture.glTexture); + gl.uniform1i(this._progShade.u_clusterbuffer, 3); + + gl.activeTexture(gl.TEXTURE5); + gl.bindTexture(gl.TEXTURE_2D, this._lightTexture.glTexture); + gl.uniform1i(this._progShade.u_lightbuffer, 5); + + gl.uniform1f(this._progShade.u_width, canvas.width); + gl.uniform1f(this._progShade.u_height, canvas.height); + + gl.uniform1f(this._progShade.u_near, camera.near); + gl.uniform1f(this._progShade.u_far, camera.far); // Bind g-buffers const firstGBufferBinding = 0; // You may have to change this if you use other texture slots diff --git a/src/renderers/clusteredForwardPlus.js b/src/renderers/clusteredForwardPlus.js index 9e8afbe..a224428 100644 --- a/src/renderers/clusteredForwardPlus.js +++ b/src/renderers/clusteredForwardPlus.js @@ -6,6 +6,7 @@ import vsSource from '../shaders/clusteredForward.vert.glsl'; import fsSource from '../shaders/clusteredForward.frag.glsl.js'; import TextureBuffer from './textureBuffer'; import ClusteredRenderer from './clustered'; +import { MAX_LIGHTS_PER_CLUSTER } from './clustered'; export default class ClusteredForwardPlusRenderer extends ClusteredRenderer { constructor(xSlices, ySlices, zSlices) { @@ -14,13 +15,24 @@ export default class ClusteredForwardPlusRenderer extends ClusteredRenderer { // Create a texture to store light data this._lightTexture = new TextureBuffer(NUM_LIGHTS, 8); + //NOTE: add more params and unifs here: this._shaderProgram = loadShaderProgram(vsSource, fsSource({ - numLights: NUM_LIGHTS, - }), { - uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap', 'u_lightbuffer', 'u_clusterbuffer'], + num_lights: NUM_LIGHTS, + xSlices: xSlices, + ySlices: ySlices, + zSlices: zSlices, + maxLightsPerCluster: MAX_LIGHTS_PER_CLUSTER, + }), { + uniforms: [ + 'u_viewProjectionMatrix', + 'u_viewMatrix', + 'u_colmap', 'u_normap', + 'u_lightbuffer', 'u_clusterbuffer', + 'u_width', 'u_height', 'u_near', 'u_far'], attribs: ['a_position', 'a_normal', 'a_uv'], }); + this._projectionMatrix = mat4.create(); this._viewMatrix = mat4.create(); this._viewProjectionMatrix = mat4.create(); @@ -34,7 +46,7 @@ export default class ClusteredForwardPlusRenderer extends ClusteredRenderer { mat4.multiply(this._viewProjectionMatrix, this._projectionMatrix, 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._projectionMatrix, scene); // Update the buffer used to populate the texture packed with light data for (let i = 0; i < NUM_LIGHTS; ++i) { @@ -76,8 +88,13 @@ export default class ClusteredForwardPlusRenderer extends ClusteredRenderer { gl.uniform1i(this._shaderProgram.u_clusterbuffer, 3); // TODO: Bind any other shader inputs + gl.uniformMatrix4fv(this._shaderProgram.u_viewMatrix, false, this._viewMatrix); + gl.uniform1f(this._shaderProgram.u_width, canvas.width); + gl.uniform1f(this._shaderProgram.u_height, canvas.height); + gl.uniform1f(this._shaderProgram.u_near, camera.near); + gl.uniform1f(this._shaderProgram.u_far, camera.far); // 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._shaderProgram); } -}; \ No newline at end of file +}; diff --git a/src/scene.js b/src/scene.js index 35f6700..2d38c22 100644 --- a/src/scene.js +++ b/src/scene.js @@ -8,7 +8,7 @@ export const LIGHT_RADIUS = 5.0; export const LIGHT_DT = -0.03; // TODO: This controls the number of lights -export const NUM_LIGHTS = 100; +export const NUM_LIGHTS = 500; class Scene { constructor() { diff --git a/src/shaders/clusteredForward.frag.glsl.js b/src/shaders/clusteredForward.frag.glsl.js index 022fda7..371dd16 100644 --- a/src/shaders/clusteredForward.frag.glsl.js +++ b/src/shaders/clusteredForward.frag.glsl.js @@ -5,17 +5,23 @@ export default function(params) { #version 100 precision highp float; + // TODO: Read this buffer to determine the lights influencing a cluster uniform sampler2D u_colmap; uniform sampler2D u_normap; uniform sampler2D u_lightbuffer; - - // TODO: Read this buffer to determine the lights influencing a cluster uniform sampler2D u_clusterbuffer; + uniform float u_width; + uniform float u_height; + uniform float u_near; + uniform float u_far; + + uniform mat4 u_viewMatrix; varying vec3 v_position; varying vec3 v_normal; varying vec2 v_uv; + vec3 applyNormalMap(vec3 geomnor, vec3 normap) { normap = normap * 2.0 - 1.0; vec3 up = normalize(vec3(0.001, 1, 0.001)); @@ -49,7 +55,7 @@ export default function(params) { Light UnpackLight(int index) { Light light; - float u = float(index + 1) / float(${params.numLights + 1}); + float u = float(index + 1) / float(${params.num_lights + 1}); vec4 v1 = texture2D(u_lightbuffer, vec2(u, 0.3)); vec4 v2 = texture2D(u_lightbuffer, vec2(u, 0.6)); light.position = v1.xyz; @@ -57,7 +63,7 @@ export default function(params) { // 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.radius = v1.w;//ExtractFloat(u_lightbuffer, ${params.num_lights}, 2, index, 3); light.color = v2.rgb; return light; @@ -74,15 +80,54 @@ export default function(params) { } } + const int xSlices = ${params.xSlices}; + const int ySlices = ${params.ySlices}; + const int zSlices = ${params.zSlices}; + const int num_clusters = xSlices * ySlices * zSlices; + void main() { vec3 albedo = texture2D(u_colmap, v_uv).rgb; vec3 normap = texture2D(u_normap, v_uv).xyz; vec3 normal = applyNormalMap(v_normal, normap); + float x_spacing = float(u_width) / float(xSlices); + float y_spacing = float(u_height)/ float(ySlices); + int cluster_x = int( gl_FragCoord.x / x_spacing); + int cluster_y = int( gl_FragCoord.y / y_spacing); + + vec4 fragCamPos = u_viewMatrix * vec4(v_position, 1.0); + float z_spacing = float(u_far - u_near) / float(zSlices); + int cluster_z = - int((fragCamPos.z + u_near) / z_spacing); + + int cluster_idx = cluster_x + cluster_y * xSlices + cluster_z * xSlices * ySlices; + float Ucoord = float(cluster_idx + 1) / float(num_clusters + 1); + int clusterLightCount = int(texture2D(u_clusterbuffer, vec2(Ucoord, 0))[0]); + + int num_texels_col = 1 + int(float(${params.maxLightsPerCluster} + 1) * 0.25); + vec3 fragColor = vec3(0.0); - for (int i = 0; i < ${params.numLights}; ++i) { - Light light = UnpackLight(i); + for (int i = 0; i < ${params.num_lights}; i++) { + + if(i >= clusterLightCount) break; + int texel_idx = int(float(i + 1) * 0.25); + float Vcoord = float(texel_idx + 1) / float(num_texels_col + 1); + vec4 texel = texture2D(u_clusterbuffer, vec2(Ucoord, Vcoord)); + + int light_idx; + int texelComponent = (i + 1) - (texel_idx * 4); + + if (texelComponent == 0) { + light_idx = int(texel[0]); + } else if (texelComponent == 1) { + light_idx = int(texel[1]); + } else if (texelComponent == 2) { + light_idx = int(texel[2]); + } else if (texelComponent == 3) { + light_idx = int(texel[3]); + } + + Light light = UnpackLight(light_idx); float lightDistance = distance(light.position, v_position); vec3 L = (light.position - v_position) / lightDistance; diff --git a/src/shaders/deferred.frag.glsl.js b/src/shaders/deferred.frag.glsl.js index 50f1e75..ff898fb 100644 --- a/src/shaders/deferred.frag.glsl.js +++ b/src/shaders/deferred.frag.glsl.js @@ -2,19 +2,148 @@ export default function(params) { return ` #version 100 precision highp float; + + uniform mat4 u_viewMatrix; + uniform mat4 u_invViewMatrix; + uniform sampler2D u_lightbuffer; + uniform sampler2D u_clusterbuffer; + uniform float u_width; + uniform float u_height; + uniform float u_near; + uniform float u_far; - uniform sampler2D u_gbuffers[${params.numGBuffers}]; + uniform sampler2D u_gbuffers[${params.num_gbuffers}]; 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.num_lights + 1}); + vec4 v1 = texture2D(u_lightbuffer, vec2(u, 0.3)); + vec4 v2 = texture2D(u_lightbuffer, vec2(u, 0.6)); + 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 = v1.w;//ExtractFloat(u_lightbuffer, ${params.num_lights}, 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; + } + } + + const int xSlices = ${params.xSlices}; + const int ySlices = ${params.ySlices}; + const int zSlices = ${params.zSlices}; + const int num_clusters = xSlices * ySlices * zSlices; + 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); - gl_FragColor = vec4(v_uv, 0.0, 1.0); + vec4 buf0val = texture2D(u_gbuffers[0], v_uv); + vec4 buf1val = texture2D(u_gbuffers[1], v_uv); + vec4 buf2val = texture2D(u_gbuffers[2], v_uv); + vec3 albedo = buf0val.rgb; + + vec3 normal = buf2val.xyz; + vec3 v_position = buf1val.xyz; + + float x_spacing = float(u_width) / float(xSlices); + float y_spacing = float(u_height)/ float(ySlices); + int cluster_x = int( gl_FragCoord.x / x_spacing); + int cluster_y = int( gl_FragCoord.y / y_spacing); + + vec4 fragCamPos = u_viewMatrix * vec4(v_position, 1.0); + float z_spacing = float(u_far - u_near) / float(zSlices); + int cluster_z = - int((fragCamPos.z + u_near) / z_spacing); + + int cluster_idx = cluster_x + cluster_y * xSlices + cluster_z * xSlices * ySlices; + + float Ucoord = float(cluster_idx + 1) / float(num_clusters + 1); + int clusterLightCount = int(texture2D(u_clusterbuffer, vec2(Ucoord, 0))[0]); + + int num_texels_col = 1 + int(float(${params.maxLightsPerCluster} + 1) * 0.25); + + vec3 fragColor = vec3(0.0); + + for (int i = 0; i < ${params.num_lights}; ++i) { + + if(i >= clusterLightCount) break; + + int texel_idx = int(float(i + 1) * 0.25); + float Vcoord = float(texel_idx + 1) / float(num_texels_col + 1); + vec4 texel = texture2D(u_clusterbuffer, vec2(Ucoord, Vcoord)); + + int light_idx; + int texelComponent = (i + 1) - (texel_idx * 4); + + if (texelComponent == 0) { + light_idx = int(texel[0]); + } else if (texelComponent == 1) { + light_idx = int(texel[1]); + } else if (texelComponent == 2) { + light_idx = int(texel[2]); + } else if (texelComponent == 3) { + light_idx = int(texel[3]); + } + + Light light = UnpackLight(light_idx); + float lightDistance = distance(light.position, v_position); + vec3 L = (light.position - v_position) / lightDistance; + + float lightIntensity = cubicGaussian(2.0 * lightDistance / light.radius); + float lambertTerm = max(dot(L, normal), 0.0); + + vec4 cam_word_pos = u_invViewMatrix * vec4(0.0, 0.0, 0.0, 1.0); + vec3 view_vec = normalize(cam_word_pos.xyz - v_position); + float specular = max(dot(normalize(L + view_vec), normal), 0.0); + specular = pow(specular, 50.0); + + fragColor += (vec3(specular) + albedo) * lambertTerm * light.color * vec3(lightIntensity); + //fragColor += albedo * lambertTerm * light.color * vec3(lightIntensity); + } + const vec3 ambientLight = vec3(0.025); + fragColor += albedo * ambientLight; + + gl_FragColor = vec4(fragColor, 1.0); + //gl_FragColor = vec4(albedo, 1.0); + //gl_FragColor = vec4(normal, 1.0); + //gl_FragColor = vec4(v_position, 1.0); } `; -} \ No newline at end of file +} diff --git a/src/shaders/deferredToTexture.frag.glsl b/src/shaders/deferredToTexture.frag.glsl index bafc086..7963d04 100644 --- a/src/shaders/deferredToTexture.frag.glsl +++ b/src/shaders/deferredToTexture.frag.glsl @@ -2,6 +2,8 @@ #extension GL_EXT_draw_buffers: enable precision highp float; +uniform mat4 u_viewMatrix; + uniform sampler2D u_colmap; uniform sampler2D u_normap; @@ -22,8 +24,7 @@ void main() { vec3 col = vec3(texture2D(u_colmap, v_uv)); // TODO: populate your g buffer - // gl_FragData[0] = ?? - // gl_FragData[1] = ?? - // gl_FragData[2] = ?? - // gl_FragData[3] = ?? + gl_FragData[0] = vec4(col, 0); + gl_FragData[1] = vec4(v_position, 0); + gl_FragData[2] = vec4(norm, 0); } \ No newline at end of file