Add the shaders needed to create convoluted PBR maps
This commit is contained in:
parent
b06649097f
commit
7df842bbed
7 changed files with 761 additions and 0 deletions
|
|
@ -739,6 +739,7 @@ set (VULKAN_SOURCES
|
|||
common/rendering/vulkan/vk_pprenderstate.cpp
|
||||
common/rendering/vulkan/vk_levelmesh.cpp
|
||||
common/rendering/vulkan/vk_lightmapper.cpp
|
||||
common/rendering/vulkan/vk_lightprober.cpp
|
||||
common/rendering/vulkan/commands/vk_commandbuffer.cpp
|
||||
common/rendering/vulkan/buffers/vk_rsbuffers.cpp
|
||||
common/rendering/vulkan/buffers/vk_hwbuffer.cpp
|
||||
|
|
|
|||
426
src/common/rendering/vulkan/vk_lightprober.cpp
Normal file
426
src/common/rendering/vulkan/vk_lightprober.cpp
Normal file
|
|
@ -0,0 +1,426 @@
|
|||
|
||||
#include "vk_lightprober.h"
|
||||
#include "vulkan/vk_renderdevice.h"
|
||||
#include "vulkan/textures/vk_texture.h"
|
||||
#include "vulkan/commands/vk_commandbuffer.h"
|
||||
#include "vulkan/descriptorsets/vk_descriptorset.h"
|
||||
#include "zvulkan/vulkanbuilders.h"
|
||||
#include "filesystem.h"
|
||||
#include "cmdlib.h"
|
||||
|
||||
VkLightprober::VkLightprober(VulkanRenderDevice* fb) : fb(fb)
|
||||
{
|
||||
CreateIrradianceMap();
|
||||
CreatePrefilterMap();
|
||||
}
|
||||
|
||||
VkLightprober::~VkLightprober()
|
||||
{
|
||||
}
|
||||
|
||||
void VkLightprober::CreateBrdfLutResources()
|
||||
{
|
||||
brdfLut.shader = CompileShader("comp_brdf_convolute.glsl", "shaders/lightprobe/comp_brdf_convolute.glsl", "VkLightprober.BrdfLut");
|
||||
|
||||
brdfLut.descriptorSetLayout = DescriptorSetLayoutBuilder()
|
||||
.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
brdfLut.pipelineLayout = PipelineLayoutBuilder()
|
||||
.AddSetLayout(brdfLut.descriptorSetLayout.get())
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
brdfLut.pipeline = ComputePipelineBuilder()
|
||||
.ComputeShader(brdfLut.shader.get())
|
||||
.Layout(brdfLut.pipelineLayout.get())
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
brdfLut.descriptorPool = DescriptorPoolBuilder()
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1)
|
||||
.MaxSets(1)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
brdfLut.descriptorSet = brdfLut.descriptorPool->allocate(brdfLut.descriptorSetLayout.get());
|
||||
|
||||
brdfLut.image = ImageBuilder()
|
||||
.Size(512, 512)
|
||||
.Format(VK_FORMAT_R16G16_SFLOAT)
|
||||
.Usage(VK_IMAGE_USAGE_STORAGE_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
brdfLut.view = ImageViewBuilder()
|
||||
.Image(brdfLut.image.get(), VK_FORMAT_R16G16_SFLOAT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
WriteDescriptors()
|
||||
.AddStorageImage(brdfLut.descriptorSet.get(), 0, brdfLut.view.get(), VK_IMAGE_LAYOUT_GENERAL)
|
||||
.Execute(fb->GetDevice());
|
||||
}
|
||||
|
||||
void VkLightprober::GenerateBrdfLut()
|
||||
{
|
||||
auto staging = BufferBuilder()
|
||||
.Size(512 * 512 * 2 * sizeof(uint16_t))
|
||||
.Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
auto cmdbuffer = fb->GetCommands()->GetTransferCommands();
|
||||
|
||||
PipelineBarrier()
|
||||
.AddImage(brdfLut.image.get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 0, VK_ACCESS_SHADER_WRITE_BIT)
|
||||
.Execute(cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
|
||||
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, brdfLut.pipeline.get());
|
||||
cmdbuffer->bindDescriptorSet(VK_PIPELINE_BIND_POINT_COMPUTE, brdfLut.pipelineLayout.get(), 0, brdfLut.descriptorSet.get());
|
||||
cmdbuffer->dispatch(512, 512, 1);
|
||||
|
||||
PipelineBarrier()
|
||||
.AddImage(brdfLut.image.get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT)
|
||||
.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
VkBufferImageCopy region = {};
|
||||
region.imageExtent.width = brdfLut.image->width;
|
||||
region.imageExtent.height = brdfLut.image->height;
|
||||
region.imageExtent.depth = 1;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
cmdbuffer->copyImageToBuffer(brdfLut.image->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion);
|
||||
|
||||
fb->GetCommands()->WaitForCommands(false);
|
||||
|
||||
std::vector<uint16_t> databuffer(512 * 512 * 2);
|
||||
auto src = staging->Map(0, databuffer.size() * sizeof(uint16_t));
|
||||
memcpy(databuffer.data(), src, databuffer.size());
|
||||
staging->Unmap();
|
||||
|
||||
std::unique_ptr<FileWriter> file(FileWriter::Open("bdrf.lut"));
|
||||
file->Write(databuffer.data(), databuffer.size() * sizeof(uint16_t));
|
||||
}
|
||||
|
||||
void VkLightprober::CreateIrradianceMap()
|
||||
{
|
||||
irradianceMap.shader = CompileShader("comp_irradiance_convolute.glsl", "shaders/lightprobe/comp_irradiance_convolute.glsl", "VkLightprober.IrradianceMap");
|
||||
|
||||
irradianceMap.descriptorSetLayout = DescriptorSetLayoutBuilder()
|
||||
.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.descriptorSetLayout = DescriptorSetLayoutBuilder()
|
||||
.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.pipelineLayout = PipelineLayoutBuilder()
|
||||
.AddSetLayout(irradianceMap.descriptorSetLayout.get())
|
||||
.AddPushConstantRange(VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(IrradianceMapPushConstants))
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.pipeline = ComputePipelineBuilder()
|
||||
.ComputeShader(irradianceMap.shader.get())
|
||||
.Layout(irradianceMap.pipelineLayout.get())
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.sampler = SamplerBuilder()
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.descriptorPool = DescriptorPoolBuilder()
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 6)
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6)
|
||||
.MaxSets(6)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
irradianceMap.images[i] = ImageBuilder()
|
||||
.Size(32, 32)
|
||||
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
|
||||
.Usage(VK_IMAGE_USAGE_STORAGE_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.views[i] = ImageViewBuilder()
|
||||
.Image(irradianceMap.images[i].get(), VK_FORMAT_R16G16B16A16_SFLOAT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
irradianceMap.descriptorSets[i] = irradianceMap.descriptorPool->allocate(irradianceMap.descriptorSetLayout.get());
|
||||
}
|
||||
}
|
||||
|
||||
void VkLightprober::GenerateIrradianceMap(VulkanImageView* environmentcubemap)
|
||||
{
|
||||
auto staging = BufferBuilder()
|
||||
.Size(32 * 32 * 8 * 6)
|
||||
.Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
WriteDescriptors write;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
write.AddStorageImage(irradianceMap.descriptorSets[i].get(), 0, irradianceMap.views[i].get(), VK_IMAGE_LAYOUT_GENERAL);
|
||||
write.AddCombinedImageSampler(irradianceMap.descriptorSets[i].get(), 1, environmentcubemap, irradianceMap.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
write.Execute(fb->GetDevice());
|
||||
|
||||
auto cmdbuffer = fb->GetCommands()->GetDrawCommands();
|
||||
|
||||
PipelineBarrier barrier0;
|
||||
for (int i = 0; i < 6; i++)
|
||||
barrier0.AddImage(irradianceMap.images[i].get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 0, VK_ACCESS_SHADER_WRITE_BIT);
|
||||
barrier0.Execute(cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
|
||||
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, irradianceMap.pipeline.get());
|
||||
|
||||
// +x, +y, +z, -x, -y, -z
|
||||
|
||||
FVector3 dir[6] = {
|
||||
FVector3( 1.0f, 0.0f, 0.0f),
|
||||
FVector3( 0.0f, 1.0f, 0.0f),
|
||||
FVector3( 0.0f, 0.0f, 1.0f),
|
||||
FVector3(-1.0f, 0.0f, 0.0f),
|
||||
FVector3( 0.0f, -1.0f, 0.0f),
|
||||
FVector3( 0.0f, 0.0f, -1.0f)
|
||||
};
|
||||
|
||||
FVector3 up[6] = {
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, 0.0f, 1.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, 0.0f, -1.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f)
|
||||
};
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
FVector3 side = dir[i] ^ up[i];
|
||||
|
||||
IrradianceMapPushConstants push;
|
||||
push.topLeft = FVector4(dir[i] - up[i] - side, 0.0f);
|
||||
push.bottomRight = FVector4(dir[i] + up[i] + side, 0.0f);
|
||||
|
||||
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->dispatch(32, 32, 1);
|
||||
}
|
||||
|
||||
PipelineBarrier barrier1;
|
||||
for (int i = 0; i < 6; i++)
|
||||
barrier1.AddImage(irradianceMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
VkBufferImageCopy region = { };
|
||||
region.bufferOffset = 32 * 32 * 8 * i;
|
||||
region.imageExtent.width = irradianceMap.images[i]->width;
|
||||
region.imageExtent.height = irradianceMap.images[i]->height;
|
||||
region.imageExtent.depth = 1;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
cmdbuffer->copyImageToBuffer(irradianceMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion);
|
||||
}
|
||||
|
||||
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());
|
||||
staging->Unmap();
|
||||
}
|
||||
|
||||
void VkLightprober::CreatePrefilterMap()
|
||||
{
|
||||
prefilterMap.shader = CompileShader("comp_prefilter_convolute.glsl", "shaders/lightprobe/comp_prefilter_convolute.glsl", "VkLightprober.PrefilterMap");
|
||||
|
||||
prefilterMap.descriptorSetLayout = DescriptorSetLayoutBuilder()
|
||||
.AddBinding(0, VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.AddBinding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.pipelineLayout = PipelineLayoutBuilder()
|
||||
.AddSetLayout(prefilterMap.descriptorSetLayout.get())
|
||||
.AddPushConstantRange(VK_SHADER_STAGE_COMPUTE_BIT, 0, sizeof(PrefilterMapPushConstants))
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.pipeline = ComputePipelineBuilder()
|
||||
.ComputeShader(prefilterMap.shader.get())
|
||||
.Layout(prefilterMap.pipelineLayout.get())
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.sampler = SamplerBuilder()
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.descriptorPool = DescriptorPoolBuilder()
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 6 * prefilterMap.maxlevels)
|
||||
.AddPoolSize(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 6 * prefilterMap.maxlevels)
|
||||
.MaxSets(6 * prefilterMap.maxlevels)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
for (int level = 0; level < prefilterMap.maxlevels; level++)
|
||||
{
|
||||
int idx = i * prefilterMap.maxlevels + level;
|
||||
|
||||
prefilterMap.images[idx] = ImageBuilder()
|
||||
.Size(128 >> level, 128 >> level)
|
||||
.Format(VK_FORMAT_R16G16B16A16_SFLOAT)
|
||||
.Usage(VK_IMAGE_USAGE_STORAGE_BIT | VK_BUFFER_USAGE_TRANSFER_SRC_BIT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.views[idx] = ImageViewBuilder()
|
||||
.Image(prefilterMap.images[idx].get(), VK_FORMAT_R16G16B16A16_SFLOAT)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
prefilterMap.descriptorSets[idx] = prefilterMap.descriptorPool->allocate(prefilterMap.descriptorSetLayout.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void VkLightprober::GeneratePrefilterMap(VulkanImageView* environmentcubemap)
|
||||
{
|
||||
auto staging = BufferBuilder()
|
||||
.Size(prefilterMap.levelsSize * 6)
|
||||
.Usage(VK_BUFFER_USAGE_TRANSFER_DST_BIT, VMA_MEMORY_USAGE_GPU_TO_CPU)
|
||||
.Create(fb->GetDevice());
|
||||
|
||||
WriteDescriptors write;
|
||||
for (int i = 0; i < 6 * prefilterMap.maxlevels; i++)
|
||||
{
|
||||
write.AddStorageImage(prefilterMap.descriptorSets[i].get(), 0, prefilterMap.views[i].get(), VK_IMAGE_LAYOUT_GENERAL);
|
||||
write.AddCombinedImageSampler(prefilterMap.descriptorSets[i].get(), 1, environmentcubemap, prefilterMap.sampler.get(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
|
||||
}
|
||||
write.Execute(fb->GetDevice());
|
||||
|
||||
auto cmdbuffer = fb->GetCommands()->GetDrawCommands();
|
||||
|
||||
PipelineBarrier barrier0;
|
||||
for (int i = 0; i < 6 * prefilterMap.maxlevels; i++)
|
||||
barrier0.AddImage(prefilterMap.images[i].get(), VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_GENERAL, 0, VK_ACCESS_SHADER_WRITE_BIT);
|
||||
barrier0.Execute(cmdbuffer, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT);
|
||||
|
||||
cmdbuffer->bindPipeline(VK_PIPELINE_BIND_POINT_COMPUTE, prefilterMap.pipeline.get());
|
||||
|
||||
// +x, +y, +z, -x, -y, -z
|
||||
|
||||
FVector3 dir[6] = {
|
||||
FVector3( 1.0f, 0.0f, 0.0f),
|
||||
FVector3( 0.0f, 1.0f, 0.0f),
|
||||
FVector3( 0.0f, 0.0f, 1.0f),
|
||||
FVector3(-1.0f, 0.0f, 0.0f),
|
||||
FVector3( 0.0f, -1.0f, 0.0f),
|
||||
FVector3( 0.0f, 0.0f, -1.0f)
|
||||
};
|
||||
|
||||
FVector3 up[6] = {
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, 0.0f, 1.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f),
|
||||
FVector3(0.0f, 0.0f, -1.0f),
|
||||
FVector3(0.0f, -1.0f, 0.0f)
|
||||
};
|
||||
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
FVector3 side = dir[i] ^ up[i];
|
||||
|
||||
PrefilterMapPushConstants push;
|
||||
push.topLeft = FVector4(dir[i] - up[i] - side, 0.0f);
|
||||
push.bottomRight = FVector4(dir[i] + up[i] + side, 0.0f);
|
||||
|
||||
for (int level = 0; level < prefilterMap.maxlevels; level++)
|
||||
{
|
||||
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->dispatch(128 >> level, 128 >> level, 1);
|
||||
}
|
||||
}
|
||||
|
||||
PipelineBarrier barrier1;
|
||||
for (int i = 0; i < 6 * prefilterMap.maxlevels; i++)
|
||||
barrier1.AddImage(prefilterMap.images[i].get(), VK_IMAGE_LAYOUT_GENERAL, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_ACCESS_SHADER_WRITE_BIT, VK_ACCESS_TRANSFER_READ_BIT);
|
||||
barrier1.Execute(cmdbuffer, VK_PIPELINE_STAGE_COMPUTE_SHADER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
|
||||
int offset = 0;
|
||||
for (int i = 0; i < 6 * prefilterMap.maxlevels; i++)
|
||||
{
|
||||
VkBufferImageCopy region = { };
|
||||
region.bufferOffset = offset;
|
||||
region.imageExtent.width = prefilterMap.images[i]->width;
|
||||
region.imageExtent.height = prefilterMap.images[i]->height;
|
||||
region.imageExtent.depth = 1;
|
||||
region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
region.imageSubresource.layerCount = 1;
|
||||
cmdbuffer->copyImageToBuffer(prefilterMap.images[i]->image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, staging->buffer, 1, ®ion);
|
||||
|
||||
offset += prefilterMap.images[i]->width * prefilterMap.images[i]->height * 8;
|
||||
}
|
||||
|
||||
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());
|
||||
staging->Unmap();
|
||||
}
|
||||
|
||||
std::unique_ptr<VulkanShader> VkLightprober::CompileShader(const std::string& name, const std::string& filename, const char* debugName)
|
||||
{
|
||||
std::string prefix = "#version 460\r\n";
|
||||
prefix += "#extension GL_GOOGLE_include_directive : enable\n";
|
||||
prefix += "#extension GL_ARB_separate_shader_objects : enable\n";
|
||||
|
||||
auto onIncludeLocal = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, false); };
|
||||
auto onIncludeSystem = [](std::string headerName, std::string includerName, size_t depth) { return OnInclude(headerName.c_str(), includerName.c_str(), depth, true); };
|
||||
|
||||
return ShaderBuilder()
|
||||
.Type(ShaderType::Compute)
|
||||
.AddSource("VersionBlock", prefix)
|
||||
.AddSource(name, LoadPrivateShaderLump(filename.c_str()).GetChars())
|
||||
.OnIncludeLocal(onIncludeLocal)
|
||||
.OnIncludeSystem(onIncludeSystem)
|
||||
.DebugName(debugName)
|
||||
.Create(debugName, fb->GetDevice());
|
||||
}
|
||||
|
||||
FString VkLightprober::LoadPrivateShaderLump(const char* lumpname)
|
||||
{
|
||||
int lump = fileSystem.CheckNumForFullName(lumpname, 0);
|
||||
if (lump == -1) I_Error("Unable to load '%s'", lumpname);
|
||||
return GetStringFromLump(lump);
|
||||
}
|
||||
|
||||
FString VkLightprober::LoadPublicShaderLump(const char* lumpname)
|
||||
{
|
||||
int lump = fileSystem.CheckNumForFullName(lumpname, 0);
|
||||
if (lump == -1) lump = fileSystem.CheckNumForFullName(lumpname);
|
||||
if (lump == -1) I_Error("Unable to load '%s'", lumpname);
|
||||
return GetStringFromLump(lump);
|
||||
}
|
||||
|
||||
ShaderIncludeResult VkLightprober::OnInclude(FString headerName, FString includerName, size_t depth, bool system)
|
||||
{
|
||||
if (depth > 8)
|
||||
I_Error("Too much include recursion!");
|
||||
|
||||
FString includeguardname;
|
||||
includeguardname << "_HEADERGUARD_" << headerName.GetChars();
|
||||
includeguardname.ReplaceChars("/\\.", '_');
|
||||
|
||||
FString code;
|
||||
code << "#ifndef " << includeguardname.GetChars() << "\n";
|
||||
code << "#define " << includeguardname.GetChars() << "\n";
|
||||
code << "#line 1\n";
|
||||
|
||||
if (system)
|
||||
code << LoadPrivateShaderLump(headerName.GetChars()).GetChars() << "\n";
|
||||
else
|
||||
code << LoadPublicShaderLump(headerName.GetChars()).GetChars() << "\n";
|
||||
|
||||
code << "#endif\n";
|
||||
|
||||
return ShaderIncludeResult(headerName.GetChars(), code.GetChars());
|
||||
}
|
||||
88
src/common/rendering/vulkan/vk_lightprober.h
Normal file
88
src/common/rendering/vulkan/vk_lightprober.h
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
|
||||
#include "zvulkan/vulkanobjects.h"
|
||||
#include "vectors.h"
|
||||
|
||||
class VulkanRenderDevice;
|
||||
class FString;
|
||||
class ShaderIncludeResult;
|
||||
|
||||
struct IrradianceMapPushConstants
|
||||
{
|
||||
FVector4 topLeft;
|
||||
FVector4 bottomRight;
|
||||
};
|
||||
|
||||
struct PrefilterMapPushConstants
|
||||
{
|
||||
FVector4 topLeft;
|
||||
FVector4 bottomRight;
|
||||
float roughness;
|
||||
float padding1, padding2, padding3;
|
||||
};
|
||||
|
||||
class VkLightprober
|
||||
{
|
||||
public:
|
||||
VkLightprober(VulkanRenderDevice* fb);
|
||||
~VkLightprober();
|
||||
|
||||
private:
|
||||
void CreateBrdfLutResources();
|
||||
void CreateIrradianceMap();
|
||||
void CreatePrefilterMap();
|
||||
void GenerateBrdfLut();
|
||||
void GenerateIrradianceMap(VulkanImageView* environmentcubemap);
|
||||
void GeneratePrefilterMap(VulkanImageView* environmentcubemap);
|
||||
|
||||
std::unique_ptr<VulkanShader> CompileShader(const std::string& name, const std::string& filename, const char* debugName);
|
||||
|
||||
static FString LoadPrivateShaderLump(const char* lumpname);
|
||||
static FString LoadPublicShaderLump(const char* lumpname);
|
||||
static ShaderIncludeResult OnInclude(FString headerName, FString includerName, size_t depth, bool system);
|
||||
|
||||
struct
|
||||
{
|
||||
std::unique_ptr<VulkanShader> shader;
|
||||
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
|
||||
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
|
||||
std::unique_ptr<VulkanDescriptorSet> descriptorSet;
|
||||
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
|
||||
std::unique_ptr<VulkanPipeline> pipeline;
|
||||
std::unique_ptr<VulkanImage> image;
|
||||
std::unique_ptr<VulkanImageView> view;
|
||||
} brdfLut;
|
||||
|
||||
struct
|
||||
{
|
||||
std::unique_ptr<VulkanShader> shader;
|
||||
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
|
||||
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
|
||||
std::unique_ptr<VulkanDescriptorSet> descriptorSets[6];
|
||||
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
|
||||
std::unique_ptr<VulkanPipeline> pipeline;
|
||||
std::unique_ptr<VulkanSampler> sampler;
|
||||
std::unique_ptr<VulkanImage> images[6];
|
||||
std::unique_ptr<VulkanImageView> views[6];
|
||||
} irradianceMap;
|
||||
|
||||
struct
|
||||
{
|
||||
enum
|
||||
{
|
||||
maxlevels = 5,
|
||||
levelsSize = (128 * 128 + 64 * 64 + 32 * 32 + 16 * 16 + 8 * 8) * 8
|
||||
};
|
||||
std::unique_ptr<VulkanShader> shader;
|
||||
std::unique_ptr<VulkanDescriptorSetLayout> descriptorSetLayout;
|
||||
std::unique_ptr<VulkanDescriptorPool> descriptorPool;
|
||||
std::unique_ptr<VulkanDescriptorSet> descriptorSets[6 * maxlevels];
|
||||
std::unique_ptr<VulkanPipelineLayout> pipelineLayout;
|
||||
std::unique_ptr<VulkanPipeline> pipeline;
|
||||
std::unique_ptr<VulkanSampler> sampler;
|
||||
std::unique_ptr<VulkanImage> images[6 * maxlevels];
|
||||
std::unique_ptr<VulkanImageView> views[6 * maxlevels];
|
||||
} prefilterMap;
|
||||
|
||||
VulkanRenderDevice* fb = nullptr;
|
||||
};
|
||||
BIN
wadsrc/static/bdrf.lut
Normal file
BIN
wadsrc/static/bdrf.lut
Normal file
Binary file not shown.
109
wadsrc/static/shaders/lightprobe/comp_brdf_convolute.glsl
Normal file
109
wadsrc/static/shaders/lightprobe/comp_brdf_convolute.glsl
Normal file
|
|
@ -0,0 +1,109 @@
|
|||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
layout(binding = 0, rg16f) writeonly uniform image2D colorBuffer;
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness);
|
||||
float GeometrySchlickGGX(float NdotV, float roughness);
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
|
||||
vec2 Hammersley(uint i, uint N);
|
||||
float RadicalInverse_VdC(uint bits);
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 colorBufferSize = imageSize(colorBuffer);
|
||||
if (gl_GlobalInvocationID.x >= colorBufferSize.x || gl_GlobalInvocationID.y >= colorBufferSize.y)
|
||||
return;
|
||||
|
||||
vec2 uv = (vec2(gl_GlobalInvocationID.xy) + 0.5) / vec2(colorBufferSize);
|
||||
|
||||
float NdotV = uv.x;
|
||||
float roughness = uv.y;
|
||||
|
||||
const uint SAMPLE_COUNT = 1024u;
|
||||
|
||||
vec3 V = vec3(sqrt(1.0 - NdotV*NdotV), 0.0, NdotV);
|
||||
vec3 N = vec3(0.0, 0.0, 1.0);
|
||||
|
||||
float A = 0.0;
|
||||
float B = 0.0;
|
||||
|
||||
for(uint i = 0u; i < SAMPLE_COUNT; ++i)
|
||||
{
|
||||
vec2 Xi = Hammersley(i, SAMPLE_COUNT);
|
||||
vec3 H = ImportanceSampleGGX(Xi, N, roughness);
|
||||
vec3 L = normalize(2.0 * dot(V, H) * H - V);
|
||||
|
||||
float NdotL = max(L.z, 0.0);
|
||||
float NdotH = max(H.z, 0.0);
|
||||
float VdotH = max(dot(V, H), 0.0);
|
||||
|
||||
if (NdotL > 0.0)
|
||||
{
|
||||
float G = GeometrySmith(N, V, L, roughness);
|
||||
float G_Vis = (G * VdotH) / (NdotH * NdotV);
|
||||
float Fc = pow(1.0 - VdotH, 5.0);
|
||||
|
||||
A += (1.0 - Fc) * G_Vis;
|
||||
B += Fc * G_Vis;
|
||||
}
|
||||
}
|
||||
|
||||
A /= float(SAMPLE_COUNT);
|
||||
B /= float(SAMPLE_COUNT);
|
||||
|
||||
imageStore(colorBuffer, ivec2(gl_GlobalInvocationID.xy), vec4(A, B, 0.0, 1.0));
|
||||
}
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
|
||||
}
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
|
||||
{
|
||||
float a = roughness*roughness;
|
||||
|
||||
float phi = 2.0 * PI * Xi.x;
|
||||
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
|
||||
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
|
||||
|
||||
// from spherical coordinates to cartesian coordinates
|
||||
vec3 H = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
|
||||
|
||||
// from tangent-space vector to world-space sample vector
|
||||
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 tangent = normalize(cross(up, N));
|
||||
vec3 bitangent = cross(N, tangent);
|
||||
|
||||
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||
return normalize(sampleVec);
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float NdotV, float roughness)
|
||||
{
|
||||
float a = roughness;
|
||||
float k = (a * a) / 2.0;
|
||||
float nom = NdotV;
|
||||
float denom = NdotV * (1.0 - k) + k;
|
||||
return nom / denom;
|
||||
}
|
||||
|
||||
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
|
||||
{
|
||||
float NdotV = max(dot(N, V), 0.0);
|
||||
float NdotL = max(dot(N, L), 0.0);
|
||||
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
|
||||
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
|
||||
return ggx1 * ggx2;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
layout(binding = 0, rgba16f) writeonly uniform image2D colorBuffer;
|
||||
layout(binding = 1) uniform samplerCube EnvironmentMap;
|
||||
|
||||
layout(push_constant) uniform PushConstants
|
||||
{
|
||||
vec4 topLeft;
|
||||
vec4 bottomRight;
|
||||
};
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 colorBufferSize = imageSize(colorBuffer);
|
||||
if (gl_GlobalInvocationID.x >= colorBufferSize.x || gl_GlobalInvocationID.y >= colorBufferSize.y)
|
||||
return;
|
||||
|
||||
vec2 uv = (vec2(gl_GlobalInvocationID.xy) + 0.5) / vec2(colorBufferSize);
|
||||
vec3 FragPos = mix(topLeft.xyz, bottomRight.xyz, uv);
|
||||
|
||||
vec3 normal = normalize(FragPos);
|
||||
vec3 right = normalize(cross(vec3(0.0, 1.0, 0.0), normal));
|
||||
vec3 up = normalize(cross(normal, right));
|
||||
|
||||
float sampleDelta = 0.025;
|
||||
float sampleCount = 0.0;
|
||||
|
||||
vec4 irradiance = vec4(0.0);
|
||||
|
||||
for (float phi = 0.0; phi < 2.0 * PI; phi += sampleDelta)
|
||||
{
|
||||
for (float theta = 0.0; theta < 0.5 * PI; theta += sampleDelta)
|
||||
{
|
||||
// spherical to cartesian (in tangent space)
|
||||
vec3 tangentSample = vec3(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta));
|
||||
|
||||
// tangent space to world
|
||||
vec3 sampleVec = tangentSample.x * right + tangentSample.y * up + tangentSample.z * normal;
|
||||
|
||||
irradiance += texture(EnvironmentMap, sampleVec) * cos(theta) * sin(theta);
|
||||
sampleCount++;
|
||||
}
|
||||
}
|
||||
|
||||
irradiance = PI * irradiance * (1.0 / sampleCount);
|
||||
|
||||
imageStore(colorBuffer, ivec2(gl_GlobalInvocationID.xy), irradiance);
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
|
||||
layout(local_size_x = 16, local_size_y = 16) in;
|
||||
layout(binding = 0, rgba16f) writeonly uniform image2D colorBuffer;
|
||||
layout(binding = 1) uniform samplerCube EnvironmentMap;
|
||||
|
||||
layout(push_constant) uniform PushConstants
|
||||
{
|
||||
vec4 topLeft;
|
||||
vec4 bottomRight;
|
||||
float roughness;
|
||||
float padding1, padding2, padding3;
|
||||
};
|
||||
|
||||
const float PI = 3.14159265359;
|
||||
|
||||
float RadicalInverse_VdC(uint bits);
|
||||
vec2 Hammersley(uint i, uint N);
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness);
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 colorBufferSize = imageSize(colorBuffer);
|
||||
if (gl_GlobalInvocationID.x >= colorBufferSize.x || gl_GlobalInvocationID.y >= colorBufferSize.y)
|
||||
return;
|
||||
|
||||
vec2 uv = (vec2(gl_GlobalInvocationID.xy) + 0.5) / vec2(colorBufferSize);
|
||||
vec3 FragPos = mix(topLeft.xyz, bottomRight.xyz, uv);
|
||||
|
||||
vec3 normal = normalize(FragPos);
|
||||
|
||||
const int SAMPLE_COUNT = 1024;
|
||||
|
||||
float totalWeight = 0.0;
|
||||
vec4 prefilteredColor = vec4(0.0);
|
||||
|
||||
for (int i = 0; i < SAMPLE_COUNT; i++)
|
||||
{
|
||||
vec2 Xi = Hammersley(uint(i), uint(SAMPLE_COUNT));
|
||||
vec3 H = ImportanceSampleGGX(Xi, normal, roughness);
|
||||
vec3 L = normalize(2.0 * dot(normal, H) * H - normal);
|
||||
|
||||
float NdotL = clamp(dot(normal, L), 0.0, 1.0);
|
||||
if(NdotL > 0.0)
|
||||
{
|
||||
prefilteredColor += texture(EnvironmentMap, L) * NdotL;
|
||||
totalWeight += NdotL;
|
||||
}
|
||||
}
|
||||
|
||||
prefilteredColor = prefilteredColor / totalWeight;
|
||||
|
||||
imageStore(colorBuffer, ivec2(gl_GlobalInvocationID.xy), prefilteredColor);
|
||||
}
|
||||
|
||||
float RadicalInverse_VdC(uint bits)
|
||||
{
|
||||
bits = (bits << 16u) | (bits >> 16u);
|
||||
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
|
||||
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
|
||||
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
|
||||
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
|
||||
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
|
||||
}
|
||||
vec2 Hammersley(uint i, uint N)
|
||||
{
|
||||
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
|
||||
}
|
||||
|
||||
vec3 ImportanceSampleGGX(vec2 Xi, vec3 N, float roughness)
|
||||
{
|
||||
float a = roughness*roughness;
|
||||
|
||||
float phi = 2.0 * PI * Xi.x;
|
||||
float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a*a - 1.0) * Xi.y));
|
||||
float sinTheta = sqrt(1.0 - cosTheta*cosTheta);
|
||||
|
||||
// from spherical coordinates to cartesian coordinates
|
||||
vec3 H = vec3(cos(phi) * sinTheta, sin(phi) * sinTheta, cosTheta);
|
||||
|
||||
// from tangent-space vector to world-space sample vector
|
||||
vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0);
|
||||
vec3 tangent = normalize(cross(up, N));
|
||||
vec3 bitangent = cross(N, tangent);
|
||||
|
||||
vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z;
|
||||
return normalize(sampleVec);
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue