[vulkan] Implement real-time lighting

Light styles and shadows aren't implemented yet.

The map's entities are used to create the lights, and the PVS used to
determine which lights might be visible (ie, the surfaces they light).
That could do with some more improvements (eg, checking if a leaf is
outside a spotlight's cone), but the concept seems to work.
This commit is contained in:
Bill Currie 2021-03-20 16:08:44 +09:00
parent e0eacf4014
commit bab3e0720f
5 changed files with 104 additions and 13 deletions

View file

@ -46,8 +46,10 @@ typedef struct qfv_light_s {
} qfv_light_t; } qfv_light_t;
typedef struct qfv_lightset_s DARRAY_TYPE (qfv_light_t) qfv_lightset_t; typedef struct qfv_lightset_s DARRAY_TYPE (qfv_light_t) qfv_lightset_t;
typedef struct qfv_lightleafset_s DARRAY_TYPE (int) qfv_lightleafset_t;
typedef struct qfv_lightvisset_s DARRAY_TYPE (byte) qfv_lightvisset_t;
#define NUM_LIGHTS 128 #define NUM_LIGHTS 256
typedef struct qfv_light_buffer_s { typedef struct qfv_light_buffer_s {
int lightCount; int lightCount;
@ -64,6 +66,11 @@ typedef struct lightingframe_s {
VkDescriptorImageInfo imageInfo[LIGHTING_IMAGE_INFOS]; VkDescriptorImageInfo imageInfo[LIGHTING_IMAGE_INFOS];
VkWriteDescriptorSet descriptors[LIGHTING_BUFFER_INFOS VkWriteDescriptorSet descriptors[LIGHTING_BUFFER_INFOS
+ LIGHTING_IMAGE_INFOS]; + LIGHTING_IMAGE_INFOS];
// A fat PVS of leafs visible from visible leafs so hidden lights can
// illuminate the leafs visible to the player
byte pvs[MAP_PVS_BYTES];
struct mleaf_s *leaf; // the last leaf used to generate the pvs
qfv_lightleafset_t lightvis;
} lightingframe_t; } lightingframe_t;
typedef struct lightingframeset_s typedef struct lightingframeset_s
@ -75,6 +82,7 @@ typedef struct lightingctx_s {
VkPipelineLayout layout; VkPipelineLayout layout;
VkDeviceMemory light_memory; VkDeviceMemory light_memory;
qfv_lightset_t lights; qfv_lightset_t lights;
qfv_lightleafset_t lightleafs;
} lightingctx_t; } lightingctx_t;
struct vulkan_ctx_s; struct vulkan_ctx_s;
@ -82,6 +90,7 @@ struct vulkan_ctx_s;
void Vulkan_Lighting_Init (struct vulkan_ctx_s *ctx); void Vulkan_Lighting_Init (struct vulkan_ctx_s *ctx);
void Vulkan_Lighting_Shutdown (struct vulkan_ctx_s *ctx); void Vulkan_Lighting_Shutdown (struct vulkan_ctx_s *ctx);
void Vulkan_Lighting_Draw (struct vulkan_ctx_s *ctx); void Vulkan_Lighting_Draw (struct vulkan_ctx_s *ctx);
void Vulkan_LoadLights (const char *entity_data, struct vulkan_ctx_s *ctx); void Vulkan_LoadLights (model_t *model, const char *entity_data,
struct vulkan_ctx_s *ctx);
#endif//__QF_Vulkan_qf_lighting_h #endif//__QF_Vulkan_qf_lighting_h

View file

@ -856,6 +856,12 @@
stage = fragment; stage = fragment;
name = main; name = main;
module = $builtin/lighting.frag; module = $builtin/lighting.frag;
specializationInfo = {
mapEntries = (
{ size = 4; offset = 0; constantID = 0; },
);
data = <00000100>;
};
}, },
); );
vertexInput = { vertexInput = {

View file

@ -26,15 +26,15 @@ vec3
calc_light (LightData light, vec3 position, vec3 normal) calc_light (LightData light, vec3 position, vec3 normal)
{ {
vec3 dist = light.position - position; vec3 dist = light.position - position;
vec3 incoming = normalize (dist);
float spotdot = -dot (incoming, light.direction);
float lightdot = dot (incoming, normal);
float d = sqrt (dot (dist, dist)); float d = sqrt (dot (dist, dist));
vec3 incoming = dist / d;
float spotdot = dot (incoming, light.direction);
float lightdot = dot (incoming, normal);
float r = light.radius; float r = light.radius;
float intensity = light.intensity * step (d, r); float intensity = light.intensity * step (d, r);
intensity *= step (spotdot, light.cone) * clamp (lightdot, 0, 1); intensity *= step (spotdot, light.cone) * clamp (lightdot, 0, 1);
return light.color * intensity * (r - d); return light.color * intensity * (r - d) / 255.0;
} }
void void
@ -44,7 +44,7 @@ main (void)
vec3 c = subpassLoad (color).rgb; vec3 c = subpassLoad (color).rgb;
vec3 n = subpassLoad (normal).rgb; vec3 n = subpassLoad (normal).rgb;
vec3 p = subpassLoad (position).rgb; vec3 p = subpassLoad (position).rgb;
vec3 light = vec3 (0.8); vec3 light = vec3 (0);
if (MaxLights > 0) { if (MaxLights > 0) {
for (int i = 0; i < lightCount; i++) { for (int i = 0; i < lightCount; i++) {

View file

@ -61,6 +61,51 @@
#include "r_internal.h" #include "r_internal.h"
#include "vid_vulkan.h" #include "vid_vulkan.h"
static void
find_visible_lights (vulkan_ctx_t *ctx)
{
//qfv_device_t *device = ctx->device;
//qfv_devfuncs_t *dfunc = device->funcs;
lightingctx_t *lctx = ctx->lighting_context;
lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame];
mleaf_t *leaf = r_viewleaf;
model_t *model = r_worldentity.renderer.model;
if (!leaf || !model) {
return;
}
if (leaf != lframe->leaf) {
double start = Sys_DoubleTime ();
byte pvs[MAP_PVS_BYTES];
Mod_LeafPVS_set (leaf, model, 0, pvs);
memcpy (lframe->pvs, pvs, sizeof (pvs));
for (int i = 0; i < model->brush.numleafs; i++) {
if (pvs[i / 8] & (1 << (i % 8))) {
Mod_LeafPVS_mix (model->brush.leafs + i, model, 0, lframe->pvs);
}
}
lframe->leaf = leaf;
double end = Sys_DoubleTime ();
Sys_Printf ("find_visible_lights: %.5gus\n", (end - start) * 1e6);
int visible = 0;
memset (lframe->lightvis.a, 0, lframe->lightvis.size * sizeof (byte));
for (size_t i = 0; i < lctx->lightleafs.size; i++) {
int l = lctx->lightleafs.a[i];
if (lframe->pvs[l / 8] & (1 << (l % 8))) {
lframe->lightvis.a[i] = 1;
visible++;
}
}
Sys_Printf ("find_visible_lights: %d / %zd visible\n", visible,
lframe->lightvis.size);
}
}
static void static void
update_lights (vulkan_ctx_t *ctx) update_lights (vulkan_ctx_t *ctx)
{ {
@ -69,6 +114,8 @@ update_lights (vulkan_ctx_t *ctx)
lightingctx_t *lctx = ctx->lighting_context; lightingctx_t *lctx = ctx->lighting_context;
lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame]; lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame];
find_visible_lights (ctx);
dlight_t *lights[NUM_LIGHTS]; dlight_t *lights[NUM_LIGHTS];
qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging);
qfv_light_buffer_t *light_data = QFV_PacketExtend (packet, qfv_light_buffer_t *light_data = QFV_PacketExtend (packet,
@ -88,6 +135,12 @@ update_lights (vulkan_ctx_t *ctx)
VectorZero (light_data->lights[i].direction); VectorZero (light_data->lights[i].direction);
light_data->lights[i].cone = 1; light_data->lights[i].cone = 1;
} }
for (size_t i = 0;
i < lframe->lightvis.size && light_data->lightCount < NUM_LIGHTS; i++) {
if (lframe->lightvis.a[i]) {
light_data->lights[light_data->lightCount++] = lctx->lights.a[i];
}
}
VkBufferMemoryBarrier wr_barriers[] = { VkBufferMemoryBarrier wr_barriers[] = {
{ VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0,
@ -208,6 +261,7 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx)
ctx->lighting_context = lctx; ctx->lighting_context = lctx;
DARRAY_INIT (&lctx->lights, 16); DARRAY_INIT (&lctx->lights, 16);
DARRAY_INIT (&lctx->lightleafs, 16);
size_t frames = ctx->frames.size; size_t frames = ctx->frames.size;
DARRAY_INIT (&lctx->frames, frames); DARRAY_INIT (&lctx->frames, frames);
@ -258,6 +312,8 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx)
for (size_t i = 0; i < frames; i++) { for (size_t i = 0; i < frames; i++) {
__auto_type lframe = &lctx->frames.a[i]; __auto_type lframe = &lctx->frames.a[i];
DARRAY_INIT (&lframe->lightvis, 16);
QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, cmdSet); QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, cmdSet);
lframe->cmd = cmdSet->a[0]; lframe->cmd = cmdSet->a[0];
@ -298,10 +354,12 @@ Vulkan_Lighting_Shutdown (vulkan_ctx_t *ctx)
for (size_t i = 0; i < lctx->frames.size; i++) { for (size_t i = 0; i < lctx->frames.size; i++) {
lightingframe_t *lframe = &lctx->frames.a[i]; lightingframe_t *lframe = &lctx->frames.a[i];
dfunc->vkDestroyBuffer (device->dev, lframe->light_buffer, 0); dfunc->vkDestroyBuffer (device->dev, lframe->light_buffer, 0);
DARRAY_CLEAR (&lframe->lightvis);
} }
dfunc->vkFreeMemory (device->dev, lctx->light_memory, 0); dfunc->vkFreeMemory (device->dev, lctx->light_memory, 0);
dfunc->vkDestroyPipeline (device->dev, lctx->pipeline, 0); dfunc->vkDestroyPipeline (device->dev, lctx->pipeline, 0);
DARRAY_CLEAR (&lctx->lights); DARRAY_CLEAR (&lctx->lights);
DARRAY_CLEAR (&lctx->lightleafs);
free (lctx->frames.a); free (lctx->frames.a);
free (lctx); free (lctx);
} }
@ -312,6 +370,14 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
{ {
const char *str; const char *str;
Sys_Printf ("{\n");
for (int i = PL_D_NumKeys (entity); i-- > 0; ) {
const char *field = PL_KeyAtIndex (entity, i);
const char *value = PL_String (PL_ObjectForKey (entity, field));
Sys_Printf ("\t%s = %s\n", field, value);
}
Sys_Printf ("}\n");
if ((str = PL_String (PL_ObjectForKey (entity, "origin")))) { if ((str = PL_String (PL_ObjectForKey (entity, "origin")))) {
sscanf (str, "%f %f %f", VectorExpandAddr (light->position)); sscanf (str, "%f %f %f", VectorExpandAddr (light->position));
} }
@ -336,7 +402,7 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
} }
light->intensity = 1; light->intensity = 1;
if ((str = PL_String (PL_ObjectForKey (entity, "light"))) if ((str = PL_String (PL_ObjectForKey (entity, "light_lev")))
|| (str = PL_String (PL_ObjectForKey (entity, "_light")))) { || (str = PL_String (PL_ObjectForKey (entity, "_light")))) {
light->radius = atof (str); light->radius = atof (str);
} }
@ -345,12 +411,13 @@ parse_light (qfv_light_t *light, const plitem_t *entity,
} }
void void
Vulkan_LoadLights (const char *entity_data, vulkan_ctx_t *ctx) Vulkan_LoadLights (model_t *model, const char *entity_data, vulkan_ctx_t *ctx)
{ {
lightingctx_t *lctx = ctx->lighting_context; lightingctx_t *lctx = ctx->lighting_context;
plitem_t *entities = 0; plitem_t *entities = 0;
lctx->lights.size = 0; lctx->lights.size = 0;
lctx->lightleafs.size = 0;
script_t *script = Script_New (); script_t *script = Script_New ();
Script_Start (script, "ent data", entity_data); Script_Start (script, "ent data", entity_data);
@ -389,13 +456,22 @@ Vulkan_LoadLights (const char *entity_data, vulkan_ctx_t *ctx)
qfv_light_t light = {}; qfv_light_t light = {};
parse_light (&light, entity, targets); parse_light (&light, entity, targets);
printf ("[%g, %g, %g] %g, [%g %g %g] %g, [%g %g %g] %g\n", DARRAY_APPEND (&lctx->lights, light);
mleaf_t *leaf = Mod_PointInLeaf (&light.position[0],
model);
DARRAY_APPEND (&lctx->lightleafs, leaf - model->brush.leafs);
printf ("[%g, %g, %g] %g, [%g %g %g] %g, [%g %g %g] %g, %zd\n",
VectorExpand (light.color), light.intensity, VectorExpand (light.color), light.intensity,
VectorExpand (light.position), light.radius, VectorExpand (light.position), light.radius,
VectorExpand (light.direction), light.cone); VectorExpand (light.direction), light.cone,
DARRAY_APPEND (&lctx->lights, light); leaf - model->brush.leafs);
} }
} }
printf ("%zd frames\n", ctx->frames.size);
for (size_t i = 0; i < ctx->frames.size; i++) {
lightingframe_t *lframe = &lctx->frames.a[i];
DARRAY_RESIZE (&lframe->lightvis, lctx->lights.size);
}
// targets does not own the objects, so need to remove them before // targets does not own the objects, so need to remove them before
// freeing targets // freeing targets
for (int i = PL_D_NumKeys (targets); i-- > 0; ) { for (int i = PL_D_NumKeys (targets); i-- > 0; ) {

View file

@ -216,7 +216,7 @@ Vulkan_NewMap (model_t *worldmodel, struct model_s **models, int num_models,
Vulkan_RegisterTextures (models, num_models, ctx); Vulkan_RegisterTextures (models, num_models, ctx);
Vulkan_BuildLightmaps (models, num_models, ctx); Vulkan_BuildLightmaps (models, num_models, ctx);
Vulkan_BuildDisplayLists (models, num_models, ctx); Vulkan_BuildDisplayLists (models, num_models, ctx);
Vulkan_LoadLights (worldmodel->brush.entities, ctx); Vulkan_LoadLights (worldmodel, worldmodel->brush.entities, ctx);
} }
/*void /*void