Add the shaders needed to create convoluted PBR maps

This commit is contained in:
Magnus Norddahl 2024-10-04 02:46:07 +02:00
commit 7df842bbed
7 changed files with 761 additions and 0 deletions

View file

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

View 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, &region);
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, &region);
}
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, &region);
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());
}

View 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

Binary file not shown.

View 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;
}

View file

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

View file

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