rtx: point/stop light clusters culling

This commit is contained in:
Ivan 'provod' Avdeev 2021-10-20 10:35:07 -07:00 committed by Ivan Avdeev
parent c1f93ffc54
commit 2ab07547fa
3 changed files with 193 additions and 127 deletions

View File

@ -34,7 +34,7 @@ layout(set = 0, binding = 2) uniform UBO {
layout (set = 0, binding = 7/*, align=4*/) uniform UBOLights { Lights lights; };
struct LightCluster {
uint8_t num_dlights;
uint8_t num_point_lights;
uint8_t num_emissive_surfaces;
uint8_t point_lights[MAX_VISIBLE_POINT_LIGHTS];
uint8_t emissive_surfaces[MAX_VISIBLE_SURFACE_LIGHTS];
@ -147,123 +147,133 @@ vec3 sampleSurfaceTriangle(vec3 color, vec3 view_dir, MaterialProperties materia
return color;
}
vec3 computeLighting(vec3 throughput, vec3 view_dir, MaterialProperties material) {
vec3 computePointLights(uint cluster_index, vec3 throughput, vec3 view_dir, MaterialProperties material) {
vec3 C = vec3(0.);
const ivec3 light_cell = ivec3(floor(payload.hit_pos_t.xyz / LIGHT_GRID_CELL_SIZE)) - light_grid.grid_min;
const uint cluster_index = uint(dot(light_cell, ivec3(1, light_grid.grid_size.x, light_grid.grid_size.x * light_grid.grid_size.y)));
if (any(greaterThanEqual(light_cell, light_grid.grid_size)) || cluster_index >= MAX_LIGHT_CLUSTERS) {
C = vec3(1., 0., 0.);
} else {
// const uint cluster_offset = cluster_index * LIGHT_CLUSTER_SIZE + HACK_OFFSET;
// const int num_dlights = int(light_grid.clusters_data[cluster_offset + LIGHT_CLUSTER_NUM_DLIGHTS_OFFSET]);
// const int num_emissive_surfaces = int(light_grid.clusters_data[cluster_offset + LIGHT_CLUSTER_NUM_EMISSIVE_SURFACES_OFFSET]);
// const uint emissive_surfaces_offset = cluster_offset + LIGHT_CLUSTER_EMISSIVE_SURFACES_DATA_OFFSET;
//C = vec3(float(num_emissive_surfaces));
//C = vec3(float(int(light_grid.clusters[cluster_index].num_emissive_surfaces)));
//C += .3 * fract(vec3(light_cell) / 4.);
const uint num_point_lights = uint(light_grid.clusters[cluster_index].num_point_lights);
for (uint j = 0; j < num_point_lights; ++j) {
const uint i = uint(light_grid.clusters[cluster_index].point_lights[j]);
const uint num_emissive_kusochki = uint(light_grid.clusters[cluster_index].num_emissive_surfaces);
float sampling_light_scale = 1.;
#if 0
const uint max_lights_per_frame = 4;
uint begin_i = 0, end_i = num_emissive_kusochki;
if (end_i > max_lights_per_frame) {
begin_i = rand() % (num_emissive_kusochki - max_lights_per_frame);
end_i = begin_i + max_lights_per_frame;
sampling_light_scale = float(num_emissive_kusochki) / float(max_lights_per_frame);
}
for (uint i = begin_i; i < end_i; ++i) {
#else
vec3 color = lights.point_lights[i].color_stopdot.rgb;
color *= throughput * payload.base_color;
if (dot(color,color) < color_culling_threshold)
continue;
for (uint i = 0; i < num_emissive_kusochki; ++i) {
#endif
const uint index_into_emissive_kusochki = uint(light_grid.clusters[cluster_index].emissive_surfaces[i]);
const vec4 origin_r = lights.point_lights[i].origin_r;
const float stopdot = lights.point_lights[i].color_stopdot.a;
const vec3 dir = lights.point_lights[i].dir_stopdot2.xyz;
const float stopdot2 = lights.point_lights[i].dir_stopdot2.a;
if (push_constants.debug_light_index_begin < push_constants.debug_light_index_end) {
if (index_into_emissive_kusochki < push_constants.debug_light_index_begin || index_into_emissive_kusochki >= push_constants.debug_light_index_end)
continue;
}
const vec3 light_dir = origin_r.xyz - payload.hit_pos_t.xyz;
const vec3 light_dir_norm = normalize(light_dir);
const float light_dot = dot(light_dir_norm, payload.normal);
if (light_dot < 1e-5)
continue;
const EmissiveKusok ek = lights.kusochki[index_into_emissive_kusochki];
const uint emissive_kusok_index = lights.kusochki[index_into_emissive_kusochki].kusok_index;
const Kusok ekusok = kusochki[emissive_kusok_index];
const vec3 emissive = ekusok.emissive;
const float spot_dot = -dot(light_dir_norm, dir);
if (spot_dot < stopdot2)
continue;
// TODO streamline matrices layouts
const mat4x3 emissive_transform = mat4x3(
vec3(ek.tx_row_x.x, ek.tx_row_y.x, ek.tx_row_z.x),
vec3(ek.tx_row_x.y, ek.tx_row_y.y, ek.tx_row_z.y),
vec3(ek.tx_row_x.z, ek.tx_row_y.z, ek.tx_row_z.z),
vec3(ek.tx_row_x.w, ek.tx_row_y.w, ek.tx_row_z.w)
);
const mat3 emissive_transform_normal = transpose(inverse(mat3(emissive_transform)));
if (emissive_kusok_index == uint(payload.kusok_index))
continue;
const uint triangle_index = rand_range(ekusok.triangles);
C += sampling_light_scale * sampleSurfaceTriangle(throughput * payload.base_color * emissive, view_dir, material, emissive_transform, emissive_transform_normal, triangle_index, ekusok.index_offset, ekusok.vertex_offset);
} // for all emissive kusochki
for (uint i = 0; i < lights.num_point_lights; ++i) {
const vec4 origin_r = lights.point_lights[i].origin_r;
vec3 color = lights.point_lights[i].color_stopdot.rgb;
float stopdot = lights.point_lights[i].color_stopdot.a;
vec3 dir = lights.point_lights[i].dir_stopdot2.xyz;
float stopdot2 = lights.point_lights[i].dir_stopdot2.a;
color *= throughput * payload.base_color;
// if (dot(color,color) < color_culling_threshold)
// continue;
const vec3 light_dir = origin_r.xyz - payload.hit_pos_t.xyz;
const vec3 light_dir_norm = normalize(light_dir);
const float light_dot = dot(light_dir_norm, payload.normal);
if (light_dot < 1e-5)
continue;
const float spot_dot = -dot(light_dir_norm, dir);
if (spot_dot < stopdot2)
continue;
float spot_attenuation = 1.f;
if (spot_dot < stopdot)
spot_attenuation = (spot_dot - stopdot2) / (stopdot - stopdot2);
float spot_attenuation = 1.f;
if (spot_dot < stopdot)
spot_attenuation = (spot_dot - stopdot2) / (stopdot - stopdot2);
#if 1
const float d2 = dot(light_dir, light_dir);
const float r2 = origin_r.w * origin_r.w;
const float light_dist = sqrt(d2);
const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
const float pdf = 1.f / (fdist * light_dot * spot_attenuation);
const float d2 = dot(light_dir, light_dir);
const float r2 = origin_r.w * origin_r.w;
const float light_dist = sqrt(d2);
const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
const float pdf = 1.f / (fdist * light_dot * spot_attenuation);
#else
const float d2 = dot(light_dir, light_dir);
//const float r2 = origin_r.w * origin_r.w;
const float light_dist = sqrt(d2);
//const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
//const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
const float fdist = (light_dist > 1.) ? 1.f / d2 : 1.f; // qrad workaround
const float pdf = 1.f / (fdist * light_dot * spot_attenuation);
const float d2 = dot(light_dir, light_dir);
//const float r2 = origin_r.w * origin_r.w;
const float light_dist = sqrt(d2);
//const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
//const float fdist = 2.f / (r2 + d2 + light_dist * sqrt(d2 + r2));
const float fdist = (light_dist > 1.) ? 1.f / d2 : 1.f; // qrad workaround
const float pdf = 1.f / (fdist * light_dot * spot_attenuation);
#endif
color /= pdf;
// if (dot(color,color) < color_culling_threshold)
// continue;
color /= pdf;
// if (dot(color,color) < color_culling_threshold)
// continue;
color *= evalCombinedBRDF(payload.normal, light_dir_norm, view_dir, material);
if (dot(color,color) < color_culling_threshold)
continue;
color *= evalCombinedBRDF(payload.normal, light_dir_norm, view_dir, material);
if (dot(color,color) < color_culling_threshold)
continue;
if (shadowed(payload.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
continue;
if (shadowed(payload.hit_pos_t.xyz, light_dir_norm, light_dist + shadow_offset_fudge))
continue;
C += color;
} // for all lights
C += color;
} // for all lights
return C;
}
vec3 computeLighting(vec3 throughput, vec3 view_dir, MaterialProperties material) {
const ivec3 light_cell = ivec3(floor(payload.hit_pos_t.xyz / LIGHT_GRID_CELL_SIZE)) - light_grid.grid_min;
const uint cluster_index = uint(dot(light_cell, ivec3(1, light_grid.grid_size.x, light_grid.grid_size.x * light_grid.grid_size.y)));
if (any(greaterThanEqual(light_cell, light_grid.grid_size)) || cluster_index >= MAX_LIGHT_CLUSTERS)
return throughput * vec3(1., 0., 0.);
vec3 C = vec3(0.);
// const uint cluster_offset = cluster_index * LIGHT_CLUSTER_SIZE + HACK_OFFSET;
// const int num_dlights = int(light_grid.clusters_data[cluster_offset + LIGHT_CLUSTER_NUM_DLIGHTS_OFFSET]);
// const int num_emissive_surfaces = int(light_grid.clusters_data[cluster_offset + LIGHT_CLUSTER_NUM_EMISSIVE_SURFACES_OFFSET]);
// const uint emissive_surfaces_offset = cluster_offset + LIGHT_CLUSTER_EMISSIVE_SURFACES_DATA_OFFSET;
//C = vec3(float(num_emissive_surfaces));
//C = vec3(float(int(light_grid.clusters[cluster_index].num_emissive_surfaces)));
//C += .3 * fract(vec3(light_cell) / 4.);
const uint num_emissive_kusochki = uint(light_grid.clusters[cluster_index].num_emissive_surfaces);
float sampling_light_scale = 1.;
#if 0
const uint max_lights_per_frame = 4;
uint begin_i = 0, end_i = num_emissive_kusochki;
if (end_i > max_lights_per_frame) {
begin_i = rand() % (num_emissive_kusochki - max_lights_per_frame);
end_i = begin_i + max_lights_per_frame;
sampling_light_scale = float(num_emissive_kusochki) / float(max_lights_per_frame);
}
for (uint i = begin_i; i < end_i; ++i) {
#else
for (uint i = 0; i < num_emissive_kusochki; ++i) {
#endif
const uint index_into_emissive_kusochki = uint(light_grid.clusters[cluster_index].emissive_surfaces[i]);
if (push_constants.debug_light_index_begin < push_constants.debug_light_index_end) {
if (index_into_emissive_kusochki < push_constants.debug_light_index_begin || index_into_emissive_kusochki >= push_constants.debug_light_index_end)
continue;
}
const EmissiveKusok ek = lights.kusochki[index_into_emissive_kusochki];
const uint emissive_kusok_index = lights.kusochki[index_into_emissive_kusochki].kusok_index;
const Kusok ekusok = kusochki[emissive_kusok_index];
const vec3 emissive = ekusok.emissive;
// TODO streamline matrices layouts
const mat4x3 emissive_transform = mat4x3(
vec3(ek.tx_row_x.x, ek.tx_row_y.x, ek.tx_row_z.x),
vec3(ek.tx_row_x.y, ek.tx_row_y.y, ek.tx_row_z.y),
vec3(ek.tx_row_x.z, ek.tx_row_y.z, ek.tx_row_z.z),
vec3(ek.tx_row_x.w, ek.tx_row_y.w, ek.tx_row_z.w)
);
const mat3 emissive_transform_normal = transpose(inverse(mat3(emissive_transform)));
if (emissive_kusok_index == uint(payload.kusok_index))
continue;
const uint triangle_index = rand_range(ekusok.triangles);
C += sampling_light_scale * sampleSurfaceTriangle(throughput * payload.base_color * emissive, view_dir, material, emissive_transform, emissive_transform_normal, triangle_index, ekusok.index_offset, ekusok.vertex_offset);
} // for all emissive kusochki
C += computePointLights(cluster_index, throughput, view_dir, material);
return C;
}

View File

@ -14,8 +14,10 @@
// indexed by uint8_t
#define MAX_POINT_LIGHTS 255
// indexed by uint8_t
#define MAX_VISIBLE_POINT_LIGHTS 31
// indexed by uint8_t
#define MAX_VISIBLE_SURFACE_LIGHTS 255
#define MAX_LIGHT_CLUSTERS 262144 //131072 //32768
#define LIGHT_GRID_CELL_SIZE 128

View File

@ -479,13 +479,13 @@ static struct {
} g_lights_bsp = {0};
static void prepareLeafAccum( void ) {
static void leafAccumPrepare( void ) {
memset(&g_lights_bsp.accum, 0, sizeof(g_lights_bsp.accum));
}
#define LEAF_ADDED_BIT 0x8000000ul
static qboolean addLeafToAccum( uint16_t leaf_index ) {
static qboolean leafAccumAdd( uint16_t leaf_index ) {
// Check whether this leaf was already added
if (g_lights_bsp.accum.leafs[leaf_index] & LEAF_ADDED_BIT)
return false;
@ -496,9 +496,15 @@ static qboolean addLeafToAccum( uint16_t leaf_index ) {
return true;
}
static int addCompressedPVSLeafsToAccum(const model_t *const map, const byte *pvs, qboolean print_debug) {
static void leafAccumFinalize( void ) {
for (int i = 0; i < g_lights_bsp.accum.count; ++i)
g_lights_bsp.accum.leafs[i] &= 0xffffu;
}
static int leafAccumAddPotentiallyVisibleFromLeaf(const model_t *const map, const mleaf_t *leaf, qboolean print_debug) {
int pvs_leaf_index = 0;
int leafs_added = 0;
const byte *pvs = leaf->compressed_vis;
for (;pvs_leaf_index < map->numleafs; ++pvs) {
uint8_t bits = pvs[0];
@ -514,7 +520,7 @@ static int addCompressedPVSLeafsToAccum(const model_t *const map, const byte *pv
if ((bits&1) == 0)
continue;
if (addLeafToAccum( pvs_leaf_index + 1 )) {
if (leafAccumAdd( pvs_leaf_index + 1 )) {
leafs_added++;
if (print_debug)
gEngine.Con_Reportf(" .%d", pvs_leaf_index + 1);
@ -536,7 +542,7 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMapSurface( const msurface_t *surf ) {
// Check if PVL hasn't been collected yet
if (!smeta->potentially_visible_leafs) {
int leafs_direct = 0, leafs_pvs = 0;
prepareLeafAccum();
leafAccumPrepare();
// Enumerate all the map leafs and pick ones that have this surface referenced
if (verbose_debug)
@ -550,7 +556,7 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMapSurface( const msurface_t *surf ) {
// FIXME split direct leafs marking from pvs propagation
leafs_direct++;
if (addLeafToAccum( i )) {
if (leafAccumAdd( i )) {
if (verbose_debug)
gEngine.Con_Reportf(" %d", i);
} else {
@ -560,17 +566,19 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMapSurface( const msurface_t *surf ) {
}
// Get all PVS leafs
leafs_pvs += addCompressedPVSLeafsToAccum(map, leaf->compressed_vis, verbose_debug);
leafs_pvs += leafAccumAddPotentiallyVisibleFromLeaf(map, leaf, verbose_debug);
}
}
if (verbose_debug)
gEngine.Con_Reportf(" (sum=%d, direct=%d, pvs=%d)\n", g_lights_bsp.accum.count, leafs_direct, leafs_pvs);
leafAccumFinalize();
smeta->potentially_visible_leafs = (vk_light_leaf_set_t*)Mem_Malloc(vk_core.pool, sizeof(smeta->potentially_visible_leafs) + sizeof(int) * g_lights_bsp.accum.count);
smeta->potentially_visible_leafs->num = g_lights_bsp.accum.count;
for (int i = 0; i < g_lights_bsp.accum.count; ++i) {
smeta->potentially_visible_leafs->leafs[i] = g_lights_bsp.accum.leafs[i] & 0xffffu;
smeta->potentially_visible_leafs->leafs[i] = g_lights_bsp.accum.leafs[i];
}
}
@ -622,7 +630,6 @@ static int computeCellIndex( const int light_cell[3] ) {
return light_cell[0] + light_cell[1] * g_lights.map.grid_size[0] + light_cell[2] * g_lights.map.grid_size[0] * g_lights.map.grid_size[1];
}
//static qboolean dump_fatpvs = true;
static qboolean have_surf = false;
vk_light_leaf_set_t *getMapLeafsAffectedByMovingSurface( const msurface_t *surf, const matrix3x4 *transform_row ) {
@ -655,7 +662,7 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMovingSurface( const msurface_t *surf,
);
}
prepareLeafAccum();
leafAccumPrepare();
// TODO it's possible to somehow more efficiently traverse the bsp and collect only the affected leafs
// (origin + radius will accidentally touch leafs that are really should not be affected)
@ -671,7 +678,7 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMovingSurface( const msurface_t *surf,
leafs_direct++;
if (addLeafToAccum( i + 1 )) {
if (leafAccumAdd( i + 1 )) {
if (!have_surf || debug_dump_lights.enabled)
gEngine.Con_Reportf(" %d", i + 1);
} else {
@ -679,17 +686,12 @@ vk_light_leaf_set_t *getMapLeafsAffectedByMovingSurface( const msurface_t *surf,
// but it really should be counted as direct
leafs_pvs--;
}
// it seems that FatPVS already implements PVS walk leafs_pvs += addCompressedPVSLeafsToAccum(map, leaf->compressed_vis, debug_dump_lights.enabled);
}
if (!have_surf || debug_dump_lights.enabled)
gEngine.Con_Reportf(" (sum=%d, direct=%d, pvs=%d)\n", g_lights_bsp.accum.count, leafs_direct, leafs_pvs);
//dump_fatpvs = false;
for (int i = 0; i < g_lights_bsp.accum.count; ++i)
g_lights_bsp.accum.leafs[i] &= 0xffffu;
leafAccumFinalize();
// ...... oh no
return (vk_light_leaf_set_t*)&g_lights_bsp.accum.count;
@ -745,10 +747,7 @@ static void prepareSurfacesLeafVisibilityCache( void ) {
g_lights_bsp.surfaces[i].potentially_visible_leafs = NULL;
}
extern void traverseBSP( void );
void VK_LightsNewMap( void )
{
void VK_LightsNewMap( void ) {
const model_t *map = gEngine.pfnGetModelByIndex( 1 );
// 1. Determine map bounding box (and optimal grid size?)
@ -786,14 +785,12 @@ void VK_LightsNewMap( void )
clusterBitMapShutdown();
clusterBitMapInit();
//traverseBSP();
prepareSurfacesLeafVisibilityCache();
VK_LightsLoadMapStaticLights();
}
void VK_LightsLoadMapStaticLights( void )
{
void VK_LightsLoadMapStaticLights( void ) {
const model_t *map = gEngine.pfnGetModelByIndex( 1 );
parseStaticLightEntities();
@ -832,6 +829,16 @@ static qboolean addSurfaceLightToCell( int cell_index, int emissive_surface_inde
return true;
}
static qboolean addLightToCell( int cell_index, int light_index ) {
vk_lights_cell_t *const cluster = g_lights.cells + cell_index;
if (cluster->num_point_lights == MAX_VISIBLE_POINT_LIGHTS)
return false;
cluster->point_lights[cluster->num_point_lights++] = light_index;
return true;
}
static qboolean canSurfaceLightAffectAABB(const model_t *mod, const msurface_t *surf, const vec3_t emissive, const float minmax[6]) {
//APROF_SCOPE_BEGIN_EARLY(canSurfaceLightAffectAABB); // DO NOT DO THIS. We have like 600k of these calls per frame :feelsbadman:
qboolean retval = true;
@ -975,12 +982,57 @@ fin:
return retval;
}
static void addPointLightToClusters( int index ) {
vk_point_light_t *const light = g_lights.point_lights + index;
const model_t* const world = gEngine.pfnGetModelByIndex( 1 );
const mleaf_t* leaf = gEngine.Mod_PointInLeaf(light->origin, world->nodes);
const vk_light_leaf_set_t *const leafs = (vk_light_leaf_set_t*)&g_lights_bsp.accum.count;
leafAccumPrepare();
leafAccumAddPotentiallyVisibleFromLeaf( world, leaf, false);
leafAccumFinalize();
clusterBitMapClear();
for (int i = 0; i < leafs->num; ++i) {
const mleaf_t *const leaf = world->leafs + leafs->leafs[i];
const int min_x = floorf(leaf->minmaxs[0] / LIGHT_GRID_CELL_SIZE);
const int min_y = floorf(leaf->minmaxs[1] / LIGHT_GRID_CELL_SIZE);
const int min_z = floorf(leaf->minmaxs[2] / LIGHT_GRID_CELL_SIZE);
const int max_x = ceilf(leaf->minmaxs[3] / LIGHT_GRID_CELL_SIZE);
const int max_y = ceilf(leaf->minmaxs[4] / LIGHT_GRID_CELL_SIZE);
const int max_z = ceilf(leaf->minmaxs[5] / LIGHT_GRID_CELL_SIZE);
for (int x = min_x; x < max_x; ++x)
for (int y = min_y; y < max_y; ++y)
for (int z = min_z; z < max_z; ++z) {
const int cell[3] = {
x - g_lights.map.grid_min_cell[0],
y - g_lights.map.grid_min_cell[1],
z - g_lights.map.grid_min_cell[2]
};
const int cell_index = computeCellIndex( cell );
if (cell_index < 0)
continue;
if (clusterBitMapCheckOrSet( cell_index )) {
if (!addLightToCell(cell_index, index)) {
// gEngine.Con_Printf(S_ERROR "Cluster %d,%d,%d(%d) ran out of light slots\n",
// cell[0], cell[1], cell[2], cell_index);
}
}
}
}
}
static int addPointLight( const vec3_t origin, const vec3_t color, float radius, float hack_attenuation ) {
const int index = g_lights.num_point_lights;
vk_point_light_t *const plight = g_lights.point_lights + index;
if (g_lights.num_point_lights >= MAX_POINT_LIGHTS) {
gEngine.Con_Printf(S_ERROR "Too many dlights, MAX_POINT_LIGHTS=%d\n", MAX_POINT_LIGHTS);
gEngine.Con_Printf(S_ERROR "Too many lights, MAX_POINT_LIGHTS=%d\n", MAX_POINT_LIGHTS);
return -1;
}
@ -999,6 +1051,7 @@ static int addPointLight( const vec3_t origin, const vec3_t color, float radius,
plight->stopdot = plight->stopdot2 = -1.f;
VectorSet(plight->dir, 0, 0, 0);
addPointLightToClusters( index );
g_lights.num_point_lights++;
return index;
}
@ -1008,7 +1061,7 @@ static int addSpotLight( const vk_light_entity_t *le, float radius, float hack_a
vk_point_light_t *const plight = g_lights.point_lights + index;
if (g_lights.num_point_lights >= MAX_POINT_LIGHTS) {
gEngine.Con_Printf(S_ERROR "Too many dlights, MAX_POINT_LIGHTS=%d\n", MAX_POINT_LIGHTS);
gEngine.Con_Printf(S_ERROR "Too many lights, MAX_POINT_LIGHTS=%d\n", MAX_POINT_LIGHTS);
return -1;
}
@ -1028,6 +1081,7 @@ static int addSpotLight( const vk_light_entity_t *le, float radius, float hack_a
plight->stopdot = le->stopdot;
plight->stopdot2 = le->stopdot2;
addPointLightToClusters( index );
g_lights.num_point_lights++;
return index;
}