Skip to content

Commit f65b324

Browse files
author
Hannah Bollar
committed
fixed - flickering from f+ fshader &updateClusters
1 parent 05b33ca commit f65b324

File tree

3 files changed

+163
-71
lines changed

3 files changed

+163
-71
lines changed

src/renderers/base.js

Lines changed: 118 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,26 @@
1-
import { mat4, vec4, vec3 } from 'gl-matrix';
1+
import { mat4, vec4, vec3, vec2 } from 'gl-matrix';
22
import { NUM_LIGHTS } from '../scene';
33
import { LIGHT_RADIUS } from '../scene';
44
import TextureBuffer from './textureBuffer';
55

66
export const MAX_LIGHTS_PER_CLUSTER = 100;
7-
87
const PI_DIV_360 = 0.00872664625;
98

9+
function sin_atan(angle) {
10+
return angle / Math.sqrt(1.0 + angle * angle);
11+
}
12+
function cos_atan(angle) {
13+
return 1.0 / Math.sqrt(1.0 + angle * angle);
14+
}
15+
16+
function getNormalComponents(angle) {
17+
18+
let bigHypot = Math.sqrt(1 + angle*angle);
19+
let normSide1 = 1 / bigHypot;
20+
let normSide2 = -angle*normSide1;
21+
return vec2.fromValues(normSide1, normSide2);
22+
}
23+
1024
export default class BaseRenderer {
1125
constructor(xSlices, ySlices, zSlices) {
1226
// Create a texture to store cluster data. Each cluster stores the number of lights followed by the light indices
@@ -32,71 +46,133 @@ export default class BaseRenderer {
3246
}
3347

3448
// calculate frustum dimensions (proportional based on depth adjustment added later)
35-
let frustum_height = Math.abs(2.0 * Math.tan(camera.fov * PI_DIV_360));
36-
let frustum_width = Math.abs(camera.aspect * frustum_height);
37-
// total depth and z stride are unaffected by depth of light
38-
let frustum_total_depth = camera.far - camera.near;
39-
let stride_z = this._zSlices / frustum_total_depth;
49+
const half_frustum_height = Math.tan(camera.fov * PI_DIV_360);
50+
const frustum_height = 2.0 * half_frustum_height;
51+
const frustum_width = camera.aspect * frustum_height;
52+
const half_frustum_width = 0.5 * frustum_width;
53+
const frustum_depth = camera.far - camera.near;
54+
55+
const stride_x = frustum_width / this._xSlices;
56+
const stride_y = frustum_height / this._ySlices;
57+
const stride_z = frustum_depth / this._zSlices;
58+
59+
const height_starting_index = -half_frustum_height;
60+
const width_starting_index = -half_frustum_width;
61+
62+
// predeclaring some variables to prevent javascript memory overhead
63+
let light_radius = 0; let light_position = vec4.create();
64+
let found_min = false;
4065

41-
let light_position = vec4.create();
4266
// Loop through lights counting number of lights at each buffer index
4367
// and placing light in appropr loc in buffer for calcs
4468
for (let on_light = 0; on_light < NUM_LIGHTS; ++on_light) {
4569
// create variables of light's information
46-
let light = scene.lights[on_light];
47-
let light_radius = light.radius;
48-
let light_position = vec4.fromValues(light.position[0], light.position[1], light.position[2], 1);
70+
light_radius = scene.lights[on_light].radius;
71+
light_position = vec4.fromValues(scene.lights[on_light].position[0],
72+
scene.lights[on_light].position[1],
73+
scene.lights[on_light].position[2],
74+
1);
4975
vec4.transformMat4(light_position, light_position, viewMatrix);
5076

5177
// for calculations need (-) of curr depth value bc of coordinate system
52-
light_position[2] *= -1;
78+
light_position[2] *= -1.0;
5379

54-
// frustum dimensions and values affected by light's depth
55-
let frustum_height_at_depth = frustum_height * light_position[2];
56-
let frustum_width_at_depth = frustum_width * light_position[2];
57-
let stride_y = this._ySlices / frustum_height_at_depth;
58-
let stride_x = this._xSlices / frustum_width_at_depth;
5980

81+
/*
82+
* Calculate relevant z depth frustum bounds
83+
*/
6084
// check which cluster slices would actually be influenced by this light
61-
let cluster_z_min = Math.floor((light_position[2] - light_radius - camera.near) * stride_z);
62-
let cluster_z_max = Math.floor((light_position[2] + light_radius - camera.near) * stride_z);
63-
let cluster_y_min = Math.floor((light_position[1] - light_radius + frustum_height_at_depth * 0.5) * stride_y);
64-
let cluster_y_max = Math.floor((light_position[1] + light_radius + frustum_height_at_depth * 0.5) * stride_y);
65-
let cluster_x_min = Math.floor((light_position[0] - light_radius + frustum_width_at_depth * 0.5) * stride_x);
66-
let cluster_x_max = Math.floor((light_position[0] + light_radius + frustum_width_at_depth * 0.5) * stride_x);
67-
85+
// using math.floor bc these are cluster indices
86+
let cluster_z_min = Math.floor((light_position[2] - light_radius - camera.near) / stride_z);
87+
let cluster_z_max = Math.floor((light_position[2] + light_radius - camera.near) / stride_z) + 1;
6888
// check if valid index locations for cluster structure dimensions - if not, then not visible so ignore
69-
if ( (cluster_x_min >= this._xSlices || cluster_x_max < 0)
70-
|| (cluster_y_min >= this._ySlices || cluster_y_max < 0)
71-
|| (cluster_z_min >= this._zSlices || cluster_z_max < 0) ) {
89+
if (cluster_z_min >= this._zSlices || cluster_z_max < 0) {
7290
continue;
7391
}
74-
7592
// cluster ranges can go outside bounds as long as overlapping with in-bounds locations
7693
// clamp cluster range to 0 -> slice bounds for each dimension
77-
// using sliceCount - 1, because indexing domain is [0, length - 1]
78-
cluster_x_min = Math.max(cluster_x_min, 0); cluster_x_max = Math.min(cluster_x_max, this._xSlices - 1);
79-
cluster_y_min = Math.max(cluster_y_min, 0); cluster_y_max = Math.min(cluster_y_max, this._ySlices - 1);
80-
cluster_z_min = Math.max(cluster_z_min, 0); cluster_z_max = Math.min(cluster_z_max, this._zSlices - 1);
94+
cluster_z_min = Math.max(cluster_z_min, 0); cluster_z_max = Math.min(cluster_z_max, this._zSlices);
95+
96+
/*
97+
* Calculate relevant x width frustum bounds
98+
*/
99+
let cluster_x_min = this._xSlices;
100+
let cluster_x_max = this._xSlices;
101+
for(let x = 0; x <= this._xSlices; ++x) {
102+
let angle = width_starting_index + stride_x * x;
103+
104+
// normal here: cosatan(angle), 0, -sinatan(angle)
105+
// dot between light position and normal
106+
// dot simplified below
107+
108+
let dot = light_position[0] * cos_atan(angle) - light_position[2] * sin_atan(angle);
109+
if(dot < light_radius) {
110+
cluster_x_min = Math.max(0, x - 1);
111+
break;
112+
}
113+
}
114+
for(let x = cluster_x_min + 1; x <= this._xSlices; ++x) {
115+
let angle = width_starting_index + stride_x * x;
116+
117+
// normal here: cosatan(angle), 0, -sinatan(angle)
118+
// dot between light position and normal
119+
// dot simplified below
120+
let dot = light_position[0] * cos_atan(angle) - light_position[2] * sin_atan(angle);
121+
if(dot < -light_radius) {
122+
cluster_x_max = Math.max(0, x - 1);
123+
break;
124+
}
125+
}
126+
127+
/*
128+
* Calculate relevant y height frustum bounds
129+
*/
130+
let cluster_y_min = this._ySlices;
131+
let cluster_y_max = this._ySlices;
132+
for(let y = 0; y <= this._ySlices; ++y) {
133+
let angle = height_starting_index + stride_y * y;
134+
135+
// normal here: 0, cosatan(angle), -sinatan(angle)
136+
// dot between light position and normal
137+
// dot simplified below
138+
let dot = light_position[1] * cos_atan(angle) - light_position[2] * sin_atan(angle);
139+
if(dot < light_radius) {
140+
cluster_y_min = Math.max(0, y - 1);
141+
break;
142+
}
143+
}
144+
for (let y = 0; y <= this._ySlices; ++y) {
145+
let angle = height_starting_index + stride_y * y;
146+
147+
// normal here: 0, cosatan(angle), -sinatan(angle)
148+
// dot between light position and normal
149+
// dot simplified below
150+
let dot = light_position[1] * cos_atan(angle) - light_position[2] * sin_atan(angle);
151+
if(dot < -light_radius) {
152+
cluster_y_max = Math.max(0, y - 1);
153+
break;
154+
}
155+
}
81156

82157
// fill in buffer locations where this light's influence should be included
83-
for (let z = cluster_z_min; z <= cluster_z_max; ++z) {
84-
for (let y = cluster_y_min; y <= cluster_y_max; ++y) {
85-
for (let x = cluster_x_min; x <= cluster_x_max; ++x) {
86-
let index_1D = x + y*this._xSlices + z*this._xSlices*this._ySlices;
158+
for (let z = cluster_z_min; z < cluster_z_max; ++z) {
159+
for (let y = cluster_y_min; y < cluster_y_max; ++y) {
160+
for (let x = cluster_x_min; x < cluster_x_max; ++x) {
161+
let index_1D = x
162+
+ y * this._xSlices
163+
+ z * this._xSlices * this._ySlices;
87164
let index_light_count = this._clusterTexture.bufferIndex(index_1D, 0);
88165

89166
// new light count with this light added to this cluster
90-
let num_lights_in_cluster = 1 + this._clusterTexture.buffer[index_light_count];
167+
let num_lights_in_cluster = 1.0 + this._clusterTexture.buffer[index_light_count];
91168

92169
// check if updating count based on this light
93170
if (num_lights_in_cluster <= MAX_LIGHTS_PER_CLUSTER) {
94-
this._clusterTexture.buffer[index_light_count] = num_lights_in_cluster;
171+
let tex_pixel = Math.floor(num_lights_in_cluster * 0.25);
172+
let index_to_fill = this._clusterTexture.bufferIndex(index_1D, tex_pixel);
173+
let this_index = num_lights_in_cluster - tex_pixel * 4;
95174

96-
let row = Math.floor(num_lights_in_cluster * 0.25);
97-
let distance_to_pixel_baseline = num_lights_in_cluster - 4 * row;
98-
let index_to_fill = this._clusterTexture.bufferIndex(index_1D, row) + distance_to_pixel_baseline;
99-
this._clusterTexture.buffer[index_to_fill] = on_light;
175+
this._clusterTexture.buffer[index_to_fill + this_index] = on_light;
100176
this._clusterTexture.buffer[index_light_count] = num_lights_in_cluster;
101177
}
102178

src/renderers/forwardPlus.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { gl } from '../init';
2-
import { mat4, vec4, vec3 } from 'gl-matrix';
2+
import { mat4, vec4, vec3, vec2 } from 'gl-matrix';
33
import { loadShaderProgram } from '../utils';
44
import { NUM_LIGHTS } from '../scene';
55
import { MAX_LIGHTS_PER_CLUSTER } from './base.js'
@@ -18,9 +18,12 @@ export default class ForwardPlusRenderer extends BaseRenderer {
1818
this._shaderProgram = loadShaderProgram(vsSource, fsSource({
1919
numLights: NUM_LIGHTS,
2020
numLights_perCluster: MAX_LIGHTS_PER_CLUSTER,
21+
slices_x: xSlices,
22+
slices_y: ySlices,
23+
slices_z: zSlices
2124
}), {
2225
uniforms: ['u_viewProjectionMatrix', 'u_colmap', 'u_normap', 'u_lightbuffer', 'u_clusterbuffer',
23-
'u_slice_dimensions', 'u_resolution', 'u_near_clip', 'u_far_clip', 'u_camera_position'],
26+
'u_view_matrix', 'u_screen_dimensions', 'u_near_clip', 'u_far_clip', 'u_slices'],
2427
attribs: ['a_position', 'a_normal', 'a_uv'],
2528
});
2629

@@ -80,11 +83,9 @@ export default class ForwardPlusRenderer extends BaseRenderer {
8083
gl.uniform1i(this._shaderProgram.u_clusterbuffer, 3);
8184

8285
// Bind any other shader inputs
83-
gl.uniform3f(this._shaderProgram.u_slice_dimensions, this._xSlices, this._ySlices, this._zSlices);
84-
gl.uniform2f(this._shaderProgram.u_resolution, canvas.width, canvas.height);
86+
gl.uniform2f(this._shaderProgram.u_screen_dimensions, canvas.width, canvas.height);
8587
gl.uniform1f(this._shaderProgram.u_near_clip, camera.near);
8688
gl.uniform1f(this._shaderProgram.u_far_clip, camera.far);
87-
gl.uniform3f(this._shaderProgram.u_camera_position, camera.position.x, camera.position.y, camera.position.z);
8889

8990
// Draw the scene. This function takes the shader program so that the model's textures can be bound to the right inputs
9091
scene.draw(this._shaderProgram);

src/shaders/forwardPlus.frag.glsl.js

Lines changed: 39 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,9 @@ export default function(params) {
1313
uniform sampler2D u_clusterbuffer;
1414
1515
uniform mat4 u_view_matrix;
16-
uniform vec3 u_slice_dimensions;
17-
uniform vec2 u_resolution;
16+
uniform vec2 u_screen_dimensions;
1817
uniform float u_near_clip;
1918
uniform float u_far_clip;
20-
uniform vec3 u_camera_position;
2119
2220
varying vec3 v_position;
2321
varying vec3 v_normal;
@@ -86,37 +84,54 @@ export default function(params) {
8684
vec3 normap = texture2D(u_normap, v_uv).xyz;
8785
vec3 normal = applyNormalMap(v_normal, normap);
8886
89-
vec4 pos = u_view_matrix * vec4(v_position, 1.0);
87+
int u_slices_x = ${params.slices_x};
88+
int u_slices_y = ${params.slices_y};
89+
int u_slices_z = ${params.slices_z};
90+
91+
vec4 camera_pos4 = u_view_matrix * vec4(v_position, 1.0);
92+
vec3 camera_pos3 = vec3(camera_pos4);
9093
9194
// locate fragment's cluster
92-
vec3 loc = vec3(floor(gl_FragCoord.x * u_slice_dimensions.x / u_resolution.x),
93-
floor(gl_FragCoord.y * u_slice_dimensions.y / u_resolution.y),
94-
floor((-pos.z - u_near_clip) * u_slice_dimensions.z / (u_far_clip - u_near_clip))
95-
);
95+
int loc_x = int(gl_FragCoord.x * float(u_slices_x) / u_screen_dimensions.x);
96+
int loc_y = int(gl_FragCoord.y * float(u_slices_y) / u_screen_dimensions.y);
97+
int loc_z = int((-camera_pos4.z - u_near_clip) * float(u_slices_z) / (u_far_clip - u_near_clip));
9698
9799
// get rest of cluster information - left as floats for math ease
98-
float index_of_cluster =
99-
loc.x + loc.y * u_slice_dimensions.x + loc.z * u_slice_dimensions.x * u_slice_dimensions.y;
100-
float num_clusters = u_slice_dimensions.x * u_slice_dimensions.y * u_slice_dimensions.z;
100+
int index_of_cluster = loc_x
101+
+ loc_y * u_slices_x
102+
+ loc_z * u_slices_x * u_slices_y;
103+
int num_clusters = u_slices_x * u_slices_y * u_slices_z;
101104
102105
// offset by 1 for both bc indexing in [0, length - 1]
103-
float row = (index_of_cluster + 1.0) / (num_clusters + 1.0);
106+
vec2 tex_uv = vec2( float(index_of_cluster + 1) / float(num_clusters + 1),
107+
0);
104108
105-
int light_count = int(texture2D(u_clusterbuffer, vec2(row, 0))[0]);
109+
int light_count = int(texture2D(u_clusterbuffer, tex_uv)[0]);
110+
float texture_height = floor(float(${params.numLights_perCluster} + 1) * 0.25) + 1.0;
106111
107112
// begin color calculation based on cluster information
108113
vec3 fragColor = vec3(0.0);
109-
for (int i = 0; i < ${params.numLights_perCluster}; ++i) {
110-
// check
114+
for (int i = 0; i < ${params.numLights}; ++i) {
111115
if (i >= light_count) {
112116
break;
113117
}
114118
115-
float light_index = ExtractFloat( u_clusterbuffer,
116-
int(num_clusters),
117-
${Math.floor((params.numLights_perCluster + 1) / 4)},
118-
int(index_of_cluster),
119-
int(i + 1) );
119+
float next = float(i + 1);
120+
121+
// texel: 'pixel' in the texture
122+
// find texel's information
123+
// having indexing issue in method so rewriting impl here
124+
float texel_idx = floor(next * 0.25);
125+
tex_uv[1] = (texel_idx + 1.0) / (texture_height + 1.0);
126+
vec4 texel = texture2D(u_clusterbuffer, tex_uv);
127+
int texel_component = int(next - 4.0 * texel_idx);
128+
// note: cant just call array loc on texel_component value bc non const, so doesnt compile
129+
float light_index = (texel_component == 0) ? texel[0] :
130+
(texel_component == 1) ? texel[1] :
131+
(texel_component == 2) ? texel[2] :
132+
texel[3];
133+
134+
// doing the lighting calculations
120135
Light light = UnpackLight(int(light_index));
121136
float lightDistance = distance(light.position, v_position);
122137
vec3 L = (light.position - v_position) / lightDistance;
@@ -125,15 +140,15 @@ export default function(params) {
125140
float lambertTerm = max(dot(L, normal), 0.0);
126141
127142
// regular shading
128-
//fragColor += albedo * lambertTerm * light.color * vec3(lightIntensity);
143+
// fragColor += albedo * lambertTerm * light.color * vec3(lightIntensity);
129144
130145
// blinn-phong
131-
vec3 view_dir = normalize(u_camera_position - v_position);
132-
vec3 half_vec_for_calc = normalize(L + view_dir);
133-
float specularTerm = pow(max(dot(normal, half_vec_for_calc), 0.0), 50.0);
146+
vec3 half_vec_for_calc = normalize(L + camera_pos3 - v_position);
147+
float specularTerm = pow(max(dot(normal, half_vec_for_calc), 0.0), 200.0);
134148
fragColor += (albedo + vec3(specularTerm)) * lambertTerm * light.color * lightIntensity;
135149
}
136150
151+
137152
const vec3 ambientLight = vec3(0.025);
138153
fragColor += albedo * ambientLight;
139154

0 commit comments

Comments
 (0)