From bab3e0720f82be00deb828c4feb8c82b036f90a6 Mon Sep 17 00:00:00 2001 From: Bill Currie Date: Sat, 20 Mar 2021 16:08:44 +0900 Subject: [PATCH] [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. --- include/QF/Vulkan/qf_lighting.h | 13 ++- libs/video/renderer/vulkan/qfpipeline.plist | 6 ++ .../renderer/vulkan/shader/lighting.frag | 10 +-- libs/video/renderer/vulkan/vulkan_lighting.c | 86 +++++++++++++++++-- libs/video/renderer/vulkan/vulkan_main.c | 2 +- 5 files changed, 104 insertions(+), 13 deletions(-) diff --git a/include/QF/Vulkan/qf_lighting.h b/include/QF/Vulkan/qf_lighting.h index cd9e7d46c..c19f6ae80 100644 --- a/include/QF/Vulkan/qf_lighting.h +++ b/include/QF/Vulkan/qf_lighting.h @@ -46,8 +46,10 @@ typedef struct qfv_light_s { } qfv_light_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 { int lightCount; @@ -64,6 +66,11 @@ typedef struct lightingframe_s { VkDescriptorImageInfo imageInfo[LIGHTING_IMAGE_INFOS]; VkWriteDescriptorSet descriptors[LIGHTING_BUFFER_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; typedef struct lightingframeset_s @@ -75,6 +82,7 @@ typedef struct lightingctx_s { VkPipelineLayout layout; VkDeviceMemory light_memory; qfv_lightset_t lights; + qfv_lightleafset_t lightleafs; } lightingctx_t; struct vulkan_ctx_s; @@ -82,6 +90,7 @@ struct vulkan_ctx_s; void Vulkan_Lighting_Init (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_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 diff --git a/libs/video/renderer/vulkan/qfpipeline.plist b/libs/video/renderer/vulkan/qfpipeline.plist index 536c98a05..523edfbad 100644 --- a/libs/video/renderer/vulkan/qfpipeline.plist +++ b/libs/video/renderer/vulkan/qfpipeline.plist @@ -856,6 +856,12 @@ stage = fragment; name = main; module = $builtin/lighting.frag; + specializationInfo = { + mapEntries = ( + { size = 4; offset = 0; constantID = 0; }, + ); + data = <00000100>; + }; }, ); vertexInput = { diff --git a/libs/video/renderer/vulkan/shader/lighting.frag b/libs/video/renderer/vulkan/shader/lighting.frag index 7468e4dec..c48f54a2f 100644 --- a/libs/video/renderer/vulkan/shader/lighting.frag +++ b/libs/video/renderer/vulkan/shader/lighting.frag @@ -26,15 +26,15 @@ vec3 calc_light (LightData light, vec3 position, vec3 normal) { 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)); + vec3 incoming = dist / d; + float spotdot = dot (incoming, light.direction); + float lightdot = dot (incoming, normal); float r = light.radius; float intensity = light.intensity * step (d, r); intensity *= step (spotdot, light.cone) * clamp (lightdot, 0, 1); - return light.color * intensity * (r - d); + return light.color * intensity * (r - d) / 255.0; } void @@ -44,7 +44,7 @@ main (void) vec3 c = subpassLoad (color).rgb; vec3 n = subpassLoad (normal).rgb; vec3 p = subpassLoad (position).rgb; - vec3 light = vec3 (0.8); + vec3 light = vec3 (0); if (MaxLights > 0) { for (int i = 0; i < lightCount; i++) { diff --git a/libs/video/renderer/vulkan/vulkan_lighting.c b/libs/video/renderer/vulkan/vulkan_lighting.c index 07f0f9927..4df8475c7 100644 --- a/libs/video/renderer/vulkan/vulkan_lighting.c +++ b/libs/video/renderer/vulkan/vulkan_lighting.c @@ -61,6 +61,51 @@ #include "r_internal.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 update_lights (vulkan_ctx_t *ctx) { @@ -69,6 +114,8 @@ update_lights (vulkan_ctx_t *ctx) lightingctx_t *lctx = ctx->lighting_context; lightingframe_t *lframe = &lctx->frames.a[ctx->curFrame]; + find_visible_lights (ctx); + dlight_t *lights[NUM_LIGHTS]; qfv_packet_t *packet = QFV_PacketAcquire (ctx->staging); 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); 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[] = { { VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER, 0, @@ -208,6 +261,7 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx) ctx->lighting_context = lctx; DARRAY_INIT (&lctx->lights, 16); + DARRAY_INIT (&lctx->lightleafs, 16); size_t frames = ctx->frames.size; DARRAY_INIT (&lctx->frames, frames); @@ -258,6 +312,8 @@ Vulkan_Lighting_Init (vulkan_ctx_t *ctx) for (size_t i = 0; i < frames; i++) { __auto_type lframe = &lctx->frames.a[i]; + DARRAY_INIT (&lframe->lightvis, 16); + QFV_AllocateCommandBuffers (device, ctx->cmdpool, 1, cmdSet); 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++) { lightingframe_t *lframe = &lctx->frames.a[i]; dfunc->vkDestroyBuffer (device->dev, lframe->light_buffer, 0); + DARRAY_CLEAR (&lframe->lightvis); } dfunc->vkFreeMemory (device->dev, lctx->light_memory, 0); dfunc->vkDestroyPipeline (device->dev, lctx->pipeline, 0); DARRAY_CLEAR (&lctx->lights); + DARRAY_CLEAR (&lctx->lightleafs); free (lctx->frames.a); free (lctx); } @@ -312,6 +370,14 @@ parse_light (qfv_light_t *light, const plitem_t *entity, { 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")))) { 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; - if ((str = PL_String (PL_ObjectForKey (entity, "light"))) + if ((str = PL_String (PL_ObjectForKey (entity, "light_lev"))) || (str = PL_String (PL_ObjectForKey (entity, "_light")))) { light->radius = atof (str); } @@ -345,12 +411,13 @@ parse_light (qfv_light_t *light, const plitem_t *entity, } 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; plitem_t *entities = 0; lctx->lights.size = 0; + lctx->lightleafs.size = 0; script_t *script = Script_New (); 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 = {}; 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.position), light.radius, - VectorExpand (light.direction), light.cone); - DARRAY_APPEND (&lctx->lights, light); + VectorExpand (light.direction), light.cone, + 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 // freeing targets for (int i = PL_D_NumKeys (targets); i-- > 0; ) { diff --git a/libs/video/renderer/vulkan/vulkan_main.c b/libs/video/renderer/vulkan/vulkan_main.c index a7ed49669..bf1cd3ff2 100644 --- a/libs/video/renderer/vulkan/vulkan_main.c +++ b/libs/video/renderer/vulkan/vulkan_main.c @@ -216,7 +216,7 @@ Vulkan_NewMap (model_t *worldmodel, struct model_s **models, int num_models, Vulkan_RegisterTextures (models, num_models, ctx); Vulkan_BuildLightmaps (models, num_models, ctx); Vulkan_BuildDisplayLists (models, num_models, ctx); - Vulkan_LoadLights (worldmodel->brush.entities, ctx); + Vulkan_LoadLights (worldmodel, worldmodel->brush.entities, ctx); } /*void