1- import { mat4 , vec4 , vec3 } from 'gl-matrix' ;
1+ import { mat4 , vec4 , vec3 , vec2 } from 'gl-matrix' ;
22import { NUM_LIGHTS } from '../scene' ;
33import { LIGHT_RADIUS } from '../scene' ;
44import TextureBuffer from './textureBuffer' ;
55
66export const MAX_LIGHTS_PER_CLUSTER = 100 ;
7-
87const 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+
1024export 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
0 commit comments