Render environment probe at player position

This commit is contained in:
Magnus Norddahl 2025-02-16 19:03:45 +01:00
commit 3f01630b8a
10 changed files with 131 additions and 44 deletions

View file

@ -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<void(IntRect& bounds, int side)> renderFunc) {}
// Screen wiping
virtual FTexture *WipeStartScreen();
virtual FTexture *WipeEndScreen();

View file

@ -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<uint16_t>
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<uint16_t>&
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<uint16_t>&
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, &region);
int mipwidth = std::max(w >> level, 1);
int mipheight = std::max(h >> level, 1);
offset += mipwidth * mipheight * count * pixelsize;
}

View file

@ -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<void(IntRect&, int)> 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<void(IntRect&, int)> 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<uint8_t> VkLightprober::GenerateIrradianceMap()
TArray<uint16_t> VkLightprober::GenerateIrradianceMap()
{
auto staging = BufferBuilder()
.Size(32 * 32 * 8 * 6)
@ -304,7 +310,7 @@ std::vector<uint8_t> 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<uint8_t> VkLightprober::GenerateIrradianceMap()
fb->GetCommands()->WaitForCommands(false);
std::vector<uint8_t> 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<uint16_t> 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<uint8_t> VkLightprober::GeneratePrefilterMap()
TArray<uint16_t> 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<uint8_t> 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<uint8_t> VkLightprober::GeneratePrefilterMap()
fb->GetCommands()->WaitForCommands(false);
std::vector<uint8_t> 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<uint16_t> 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;
}

View file

@ -35,8 +35,8 @@ public:
~VkLightprober();
void RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc);
std::vector<uint8_t> GenerateIrradianceMap();
std::vector<uint8_t> GeneratePrefilterMap();
TArray<uint16_t> GenerateIrradianceMap();
TArray<uint16_t> 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<VulkanShader> shader;
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;

View file

@ -321,6 +321,15 @@ void VulkanRenderDevice::RenderTextureView(FCanvasTexture* tex, std::function<vo
tex->SetUpdated(true);
}
void VulkanRenderDevice::RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc)
{
mLightprober->RenderEnvironmentMap(std::move(renderFunc));
TArray<uint16_t> irradianceMap = mLightprober->GenerateIrradianceMap();
TArray<uint16_t> 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<void()> &afterBloomDrawEndScene2D)
{
if (!swscene) mPostprocess->BlitSceneToPostprocess(); // Copy the resulting scene to the current post process texture

View file

@ -104,6 +104,7 @@ public:
private:
void RenderTextureView(FCanvasTexture* tex, std::function<void(IntRect &)> renderFunc) override;
void RenderEnvironmentMap(std::function<void(IntRect& bounds, int side)> renderFunc) override;
void PrintStartupLog();
void CopyScreenToBuffer(int w, int h, uint8_t *data) override;

View file

@ -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

View file

@ -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);

View file

@ -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

View file

@ -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);