From 3f01630b8aea87d88079edbcba89b63a982271fd Mon Sep 17 00:00:00 2001 From: Magnus Norddahl Date: Sun, 16 Feb 2025 19:03:45 +0100 Subject: [PATCH] Render environment probe at player position --- src/common/rendering/v_video.h | 2 + .../rendering/vulkan/textures/vk_texture.cpp | 44 ++++++++-------- .../rendering/vulkan/vk_lightprober.cpp | 50 ++++++++++++++----- src/common/rendering/vulkan/vk_lightprober.h | 6 +-- .../rendering/vulkan/vk_renderdevice.cpp | 9 ++++ src/common/rendering/vulkan/vk_renderdevice.h | 1 + src/rendering/hwrenderer/hw_entrypoint.cpp | 21 +++++++- src/rendering/hwrenderer/scene/hw_drawinfo.h | 2 +- src/rendering/r_utility.cpp | 37 +++++++++++++- src/rendering/r_utility.h | 3 +- 10 files changed, 131 insertions(+), 44 deletions(-) diff --git a/src/common/rendering/v_video.h b/src/common/rendering/v_video.h index c3971c8c4..594598cf2 100644 --- a/src/common/rendering/v_video.h +++ b/src/common/rendering/v_video.h @@ -221,6 +221,8 @@ public: // Get the array index for the material in the textures array accessible from shaders virtual int GetBindlessTextureIndex(FMaterial* material, int clampmode, int translation) { return -1; } + virtual void RenderEnvironmentMap(std::function renderFunc) {} + // Screen wiping virtual FTexture *WipeStartScreen(); virtual FTexture *WipeEndScreen(); diff --git a/src/common/rendering/vulkan/textures/vk_texture.cpp b/src/common/rendering/vulkan/textures/vk_texture.cpp index 0b86ae101..25d8809dc 100644 --- a/src/common/rendering/vulkan/textures/vk_texture.cpp +++ b/src/common/rendering/vulkan/textures/vk_texture.cpp @@ -212,10 +212,10 @@ void VkTextureManager::CreatePrefiltermap() int size = 1 << MAX_REFLECTION_LOD; for (int arrayIndex = 0; arrayIndex < 6; arrayIndex++) { - for (int level = 0; level < MAX_REFLECTION_LOD; level++) + for (int level = 0; level <= MAX_REFLECTION_LOD; level++) { int mipsize = size >> level; - for (int i = 0; i < mipsize; i++) + for (int i = 0; i < mipsize * mipsize; i++) { data.Push(0); data.Push(0); @@ -256,10 +256,10 @@ void VkTextureManager::CreateIrradiancemap(int size, int count, TArray auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); - if (count > 0 && newPixelData.Size() >= (size_t)w * h * count * 3) - { - assert(newPixelData.Size() == (size_t)w * h * count * 3); + assert(newPixelData.Size() == (size_t)w * h * count * 3); + if (count > 0 && newPixelData.Size() == (size_t)w * h * count * 3) + { int totalSize = w * h * count * pixelsize; auto stagingBuffer = BufferBuilder() @@ -333,28 +333,27 @@ void VkTextureManager::CreatePrefiltermap(int size, int count, TArray& auto cmdbuffer = fb->GetCommands()->GetTransferCommands(); - if (count > 0 && newPixelData.Size() >= (size_t)w * h * count * 3) + int totalSize = 0; + for (int level = 0; level < miplevels; level++) { - assert(newPixelData.Size() == (size_t)w * h * count * 3); - - int totalSize = 0; - for (int level = 0; level < miplevels; level++) - { - int mipwidth = std::max(w >> level, 1); - int mipheight = std::max(h >> level, 1); - totalSize += mipwidth * mipheight * count * pixelsize; - } + int mipwidth = std::max(w >> level, 1); + int mipheight = std::max(h >> level, 1); + totalSize += mipwidth * mipheight * count; + } + assert(newPixelData.Size() == (size_t)totalSize * 3); + if (count > 0 && newPixelData.Size() == (size_t)totalSize * 3) + { auto stagingBuffer = BufferBuilder() - .Size(totalSize) + .Size(totalSize * pixelsize) .Usage(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VMA_MEMORY_USAGE_CPU_ONLY) .DebugName("VkTextureManager.PrefiltermapStagingBuffer") .Create(fb->GetDevice()); uint16_t one = 0x3c00; // half-float 1.0 const uint16_t* src = newPixelData.Data(); - uint16_t* data = (uint16_t*)stagingBuffer->Map(0, totalSize); - for (int i = w * h * count; i > 0; i--) + uint16_t* data = (uint16_t*)stagingBuffer->Map(0, totalSize * pixelsize); + for (int i = 0; i < totalSize; i++) { *(data++) = *(src++); *(data++) = *(src++); @@ -370,18 +369,19 @@ void VkTextureManager::CreatePrefiltermap(int size, int count, TArray& int offset = 0; for (int level = 0; level < miplevels; level++) { + int mipwidth = std::max(w >> level, 1); + int mipheight = std::max(h >> level, 1); + VkBufferImageCopy region = {}; region.bufferOffset = offset; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = count; region.imageSubresource.mipLevel = level; region.imageExtent.depth = 1; - region.imageExtent.width = w; - region.imageExtent.height = h; + region.imageExtent.width = mipwidth; + region.imageExtent.height = mipheight; cmdbuffer->copyBufferToImage(stagingBuffer->buffer, Prefiltermap.Image.Image->image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); - int mipwidth = std::max(w >> level, 1); - int mipheight = std::max(h >> level, 1); offset += mipwidth * mipheight * count * pixelsize; } diff --git a/src/common/rendering/vulkan/vk_lightprober.cpp b/src/common/rendering/vulkan/vk_lightprober.cpp index 50fb87d89..14f75a860 100644 --- a/src/common/rendering/vulkan/vk_lightprober.cpp +++ b/src/common/rendering/vulkan/vk_lightprober.cpp @@ -14,6 +14,7 @@ VkLightprober::VkLightprober(VulkanRenderDevice* fb) : fb(fb) { CreateIrradianceMap(); CreatePrefilterMap(); + CreateEnvironmentMap(); } VkLightprober::~VkLightprober() @@ -134,6 +135,7 @@ void VkLightprober::CreateEnvironmentMap() { environmentMap.renderTargets[i].View = ImageViewBuilder() .Image(environmentMap.cubeimage.get(), VK_FORMAT_R16G16B16A16_SFLOAT, VK_IMAGE_ASPECT_COLOR_BIT, 0, i, 1, 1) + .DebugName("VkLightprober.environmentMap.renderTargets[].View") .Create(fb->GetDevice()); } } @@ -149,7 +151,9 @@ void VkLightprober::RenderEnvironmentMap(std::function rend VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_ACCESS_SHADER_READ_BIT, - VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT) + VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + 0, 1, 0, 6) .Execute( fb->GetCommands()->GetDrawCommands(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, @@ -192,7 +196,9 @@ void VkLightprober::RenderEnvironmentMap(std::function rend VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_ACCESS_SHADER_READ_BIT) + VK_ACCESS_SHADER_READ_BIT, + VK_IMAGE_ASPECT_COLOR_BIT, + 0, 1, 0, 6) .Execute( fb->GetCommands()->GetDrawCommands(), VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, @@ -250,7 +256,7 @@ void VkLightprober::CreateIrradianceMap() } } -std::vector VkLightprober::GenerateIrradianceMap() +TArray VkLightprober::GenerateIrradianceMap() { auto staging = BufferBuilder() .Size(32 * 32 * 8 * 6) @@ -304,7 +310,7 @@ std::vector VkLightprober::GenerateIrradianceMap() push.up = up[i]; cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_COMPUTE, irradianceMap.pipelineLayout.get(), 0, irradianceMap.descriptorSets[i].get()); - cmdbuffer->pushConstants(irradianceMap.pipelineLayout.get(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, sizeof(PushConstants), &push); + cmdbuffer->pushConstants(irradianceMap.pipelineLayout.get(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(IrradianceMapPushConstants), &push); cmdbuffer->dispatch(32, 32, 1); } @@ -327,9 +333,18 @@ std::vector VkLightprober::GenerateIrradianceMap() fb->GetCommands()->WaitForCommands(false); - std::vector databuffer(32 * 32 * 8 * 6); - auto src = staging->Map(0, databuffer.size()); - memcpy(databuffer.data(), src, databuffer.size()); + // Copy while dropping the alpha channel + int texelCount = 32 * 32 * 6; + TArray databuffer(texelCount * 3, true); + auto dst = databuffer.data(); + auto src = (uint16_t*)staging->Map(0, texelCount * 8); + for (int i = 0; i < texelCount; i++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } staging->Unmap(); return databuffer; } @@ -383,10 +398,10 @@ void VkLightprober::CreatePrefilterMap() } } -std::vector VkLightprober::GeneratePrefilterMap() +TArray VkLightprober::GeneratePrefilterMap() { auto staging = BufferBuilder() - .Size(prefilterMap.levelsSize * 6) + .Size(prefilterMap.levelsSize * 6 * 8) .Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU) .Create(fb->GetDevice()); @@ -441,7 +456,7 @@ std::vector VkLightprober::GeneratePrefilterMap() push.roughness = (float)level / (float)(prefilterMap.maxlevels - 1); cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_COMPUTE, prefilterMap.pipelineLayout.get(), 0, prefilterMap.descriptorSets[i * prefilterMap.maxlevels + level].get()); - cmdbuffer->pushConstants(prefilterMap.pipelineLayout.get(), VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, 0, sizeof(PrefilterMapPushConstants), &push); + cmdbuffer->pushConstants(prefilterMap.pipelineLayout.get(), VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PrefilterMapPushConstants), &push); cmdbuffer->dispatch(128 >> level, 128 >> level, 1); } } @@ -468,9 +483,18 @@ std::vector VkLightprober::GeneratePrefilterMap() fb->GetCommands()->WaitForCommands(false); - std::vector databuffer(prefilterMap.levelsSize * 6); - auto src = staging->Map(0, databuffer.size()); - memcpy(databuffer.data(), src, databuffer.size()); + // Copy while dropping the alpha channel + int texelCount = prefilterMap.levelsSize * 6; + TArray databuffer(texelCount * 3, true); + auto dst = databuffer.data(); + auto src = (uint16_t*)staging->Map(0, texelCount * 8); + for (int i = 0; i < texelCount; i++) + { + *(dst++) = *(src++); + *(dst++) = *(src++); + *(dst++) = *(src++); + src++; + } staging->Unmap(); return databuffer; } diff --git a/src/common/rendering/vulkan/vk_lightprober.h b/src/common/rendering/vulkan/vk_lightprober.h index df9c81fe8..d3df9e12a 100644 --- a/src/common/rendering/vulkan/vk_lightprober.h +++ b/src/common/rendering/vulkan/vk_lightprober.h @@ -35,8 +35,8 @@ public: ~VkLightprober(); void RenderEnvironmentMap(std::function renderFunc); - std::vector GenerateIrradianceMap(); - std::vector GeneratePrefilterMap(); + TArray GenerateIrradianceMap(); + TArray GeneratePrefilterMap(); private: void CreateBrdfLutResources(); @@ -94,7 +94,7 @@ private: enum { maxlevels = 5, - levelsSize = (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8) * 8 + levelsSize = 128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8 }; std::unique_ptr shader; std::unique_ptr descriptorSetLayout; diff --git a/src/common/rendering/vulkan/vk_renderdevice.cpp b/src/common/rendering/vulkan/vk_renderdevice.cpp index 094ec9965..326718b5e 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.cpp +++ b/src/common/rendering/vulkan/vk_renderdevice.cpp @@ -321,6 +321,15 @@ void VulkanRenderDevice::RenderTextureView(FCanvasTexture* tex, std::functionSetUpdated(true); } +void VulkanRenderDevice::RenderEnvironmentMap(std::function renderFunc) +{ + mLightprober->RenderEnvironmentMap(std::move(renderFunc)); + TArray irradianceMap = mLightprober->GenerateIrradianceMap(); + TArray prefilterMap = mLightprober->GeneratePrefilterMap(); + mTextureManager->CreateIrradiancemap(32, 6, std::move(irradianceMap)); + mTextureManager->CreatePrefiltermap(128, 6, std::move(prefilterMap)); +} + void VulkanRenderDevice::PostProcessScene(bool swscene, int fixedcm, float flash, const std::function &afterBloomDrawEndScene2D) { if (!swscene) mPostprocess->BlitSceneToPostprocess(); // Copy the resulting scene to the current post process texture diff --git a/src/common/rendering/vulkan/vk_renderdevice.h b/src/common/rendering/vulkan/vk_renderdevice.h index 66cd2b3d2..350936d19 100644 --- a/src/common/rendering/vulkan/vk_renderdevice.h +++ b/src/common/rendering/vulkan/vk_renderdevice.h @@ -104,6 +104,7 @@ public: private: void RenderTextureView(FCanvasTexture* tex, std::function renderFunc) override; + void RenderEnvironmentMap(std::function renderFunc) override; void PrintStartupLog(); void CopyScreenToBuffer(int w, int h, uint8_t *data) override; diff --git a/src/rendering/hwrenderer/hw_entrypoint.cpp b/src/rendering/hwrenderer/hw_entrypoint.cpp index 2dd613ca2..6eaeb63ec 100644 --- a/src/rendering/hwrenderer/hw_entrypoint.cpp +++ b/src/rendering/hwrenderer/hw_entrypoint.cpp @@ -56,6 +56,7 @@ EXTERN_CVAR(Bool, gl_bandedswlight) EXTERN_CVAR(Bool, lm_dynlights); CVAR(Bool, gl_raytrace, false, 0/*CVAR_ARCHIVE | CVAR_GLOBALCONFIG*/) +CVAR(Bool, gl_lightprobe, false, 0/*CVAR_ARCHIVE | CVAR_GLOBALCONFIG*/) extern bool NoInterpolateView; @@ -108,11 +109,11 @@ void CollectLights(FLevelLocals* Level) // //----------------------------------------------------------------------------- -sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen) +sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen, int side) { auto& RenderState = *screen->RenderState(); - R_SetupFrame(mainvp, r_viewwindow, camera); + R_SetupFrame(mainvp, r_viewwindow, camera, side); if (mainview && toscreen && !(camera->Level->flags3 & LEVEL3_NOSHADOWMAP) && camera->Level->HasDynamicLights && gl_light_shadows > 0 && !lm_dynlights) { @@ -399,6 +400,22 @@ sector_t* RenderView(player_t* player) }); }); } + + if (gl_lightprobe) + { + // Render the light probes if not found in a lump + // To do: we need light probe actors + AActor* lightprobe = player->camera; + if (lightprobe) + { + screen->RenderEnvironmentMap([=](IntRect& bounds, int side) + { + FRenderViewpoint probevp; + RenderViewpoint(probevp, lightprobe, &bounds, 90.0, 1.0f, 1.0f, false, false, side); + }); + } + } + NoInterpolateView = saved_niv; // now render the main view diff --git a/src/rendering/hwrenderer/scene/hw_drawinfo.h b/src/rendering/hwrenderer/scene/hw_drawinfo.h index 29abb5dec..eb0026ed8 100644 --- a/src/rendering/hwrenderer/scene/hw_drawinfo.h +++ b/src/rendering/hwrenderer/scene/hw_drawinfo.h @@ -375,7 +375,7 @@ private: }; void CleanSWDrawer(); -sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen); +sector_t* RenderViewpoint(FRenderViewpoint& mainvp, AActor* camera, IntRect* bounds, float fov, float ratio, float fovratio, bool mainview, bool toscreen, int side = -1); void WriteSavePic(player_t* player, FileWriter* file, int width, int height); sector_t* RenderView(player_t* player); diff --git a/src/rendering/r_utility.cpp b/src/rendering/r_utility.cpp index 3a3fdd70e..5800f7c73 100644 --- a/src/rendering/r_utility.cpp +++ b/src/rendering/r_utility.cpp @@ -903,7 +903,7 @@ static void R_DoActorTickerAngleChanges(player_t* const player, DRotator& angles EXTERN_CVAR(Float, chase_dist) EXTERN_CVAR(Float, chase_height) -void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AActor* const actor) +void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AActor* const actor, int side) { viewPoint.TicFrac = I_GetTimeFrac(); if (cl_capfps || r_NoInterpolate) @@ -1067,6 +1067,41 @@ void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AA R_InterpolateView(viewPoint, player, viewPoint.TicFrac, iView); + if (side != -1) // Cubemap rendering + { + DRotator cubeside; + cubeside.Roll = DAngle::fromDeg(0); + switch (side) + { + default: + case 0: // +X + cubeside.Yaw = DAngle::fromDeg(0); + cubeside.Pitch = DAngle::fromDeg(0); + break; + case 1: // +Y + cubeside.Yaw = DAngle::fromDeg(90); + cubeside.Pitch = DAngle::fromDeg(0); + break; + case 2: // +Z + cubeside.Yaw = DAngle::fromDeg(0); + cubeside.Pitch = DAngle::fromDeg(90); + break; + case 3: // -X + cubeside.Yaw = DAngle::fromDeg(180); + cubeside.Pitch = DAngle::fromDeg(0); + break; + case 4: // -Y + cubeside.Yaw = DAngle::fromDeg(-90); + cubeside.Pitch = DAngle::fromDeg(0); + break; + case 5: // -Z + cubeside.Yaw = DAngle::fromDeg(0); + cubeside.Pitch = DAngle::fromDeg(-90); + break; + } + viewPoint.Angles = cubeside; + } + viewPoint.SetViewAngle(viewWindow); // Keep the view within the sector's floor and ceiling diff --git a/src/rendering/r_utility.h b/src/rendering/r_utility.h index 7beaf1440..bdcd007d8 100644 --- a/src/rendering/r_utility.h +++ b/src/rendering/r_utility.h @@ -126,8 +126,7 @@ void R_ClearInterpolationPath(); void R_AddInterpolationPoint(const DVector3a &vec); void R_SetViewSize (int blocks); void R_SetFOV (FRenderViewpoint &viewpoint, DAngle fov); -void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AActor* const camera); -void R_SetViewAngle (FRenderViewpoint &viewpoint, const FViewWindow &viewwindow); +void R_SetupFrame(FRenderViewpoint& viewPoint, const FViewWindow& viewWindow, AActor* const camera, int side = -1); // Called by startup code. void R_Init (void);