IBL Specular complete.

This commit is contained in:
Anish Bhobe 2024-08-01 01:37:25 +02:00
parent b9d5ba56d4
commit e82b37b2d9
17 changed files with 454 additions and 133 deletions

View File

@ -40,6 +40,24 @@
"name": "windows-reldebug", "name": "windows-reldebug",
"generator": "Ninja", "generator": "Ninja",
"binaryDir": "${sourceDir}/build/reldebug/", "binaryDir": "${sourceDir}/build/reldebug/",
"cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_EXPORT_COMPILE_COMMANDS": true,
"CMAKE_MAKE_PROGRAM": "ninja",
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/vcpkg/scripts/buildsystems/vcpkg.cmake",
"DXC_SHADER_FLAGS": "-Zi",
"GLSLC_SHADER_FLAGS": "-g"
},
"condition": {
"type": "equals",
"lhs": "${hostSystemName}",
"rhs": "Windows"
}
},
{
"name": "windows-reldebug-nobreak",
"generator": "Ninja",
"binaryDir": "${sourceDir}/build/reldebug/",
"cacheVariables": { "cacheVariables": {
"CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_BUILD_TYPE": "RelWithDebInfo",
"CMAKE_EXPORT_COMPILE_COMMANDS": true, "CMAKE_EXPORT_COMPILE_COMMANDS": true,

View File

@ -53,7 +53,7 @@ Failed(const vk::Result result)
using NameString = eastl::fixed_string<char, 32, false>; using NameString = eastl::fixed_string<char, 32, false>;
template <typename TFlagBits> template <typename TFlagBits>
struct std::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp) struct eastl::hash<vk::Flags<TFlagBits>> // NOLINT(*-dcl58-cpp)
{ {
[[nodiscard]] usize [[nodiscard]] usize
operator()(const vk::Flags<TFlagBits> &val) operator()(const vk::Flags<TFlagBits> &val)
@ -66,7 +66,7 @@ template <typename T>
[[nodiscard]] usize [[nodiscard]] usize
HashAny(const T &val) HashAny(const T &val)
{ {
return std::hash<std::remove_cvref_t<T>>()(val); return eastl::hash<std::remove_cvref_t<T>>()(val);
} }
[[nodiscard]] inline usize [[nodiscard]] inline usize

View File

@ -193,6 +193,79 @@ StorageTextureManager::Release(const Device *device, const StorageTextureHandle
TextureManager::Release(device, {handle.m_Index}); TextureManager::Release(device, {handle.m_Index});
} }
usize
HashSamplerCreateInfo(const vk::SamplerCreateInfo *createInfo)
{
usize hash = HashAny(createInfo->flags);
hash = HashCombine(hash, HashAny(createInfo->magFilter));
hash = HashCombine(hash, HashAny(createInfo->minFilter));
hash = HashCombine(hash, HashAny(createInfo->mipmapMode));
hash = HashCombine(hash, HashAny(createInfo->addressModeU));
hash = HashCombine(hash, HashAny(createInfo->addressModeV));
hash = HashCombine(hash, HashAny(createInfo->addressModeW));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo->mipLodBias * 1000))); // Resolution of 10^-3
hash = HashCombine(hash, HashAny(createInfo->anisotropyEnable));
hash = HashCombine(hash,
HashAny(Cast<usize>(createInfo->maxAnisotropy * 0x10))); // 16:1 Anisotropy is enough resolution
hash = HashCombine(hash, HashAny(createInfo->compareEnable));
hash = HashCombine(hash, HashAny(createInfo->compareOp));
hash = HashCombine(hash, HashAny(Cast<usize>(createInfo->minLod * 1000))); // 0.001 resolution is enough.
hash = HashCombine(hash,
HashAny(Cast<usize>(createInfo->maxLod * 1000))); // 0.001 resolution is enough. (1 == NO Clamp)
hash = HashCombine(hash, HashAny(createInfo->borderColor));
hash = HashCombine(hash, HashAny(createInfo->unnormalizedCoordinates));
return hash;
}
void
SamplerManager::Init(usize size)
{
m_Samplers.reserve(size);
m_SamplerHashes.reserve(size);
}
SamplerHandle
SamplerManager::Create(const Device *device, const vk::SamplerCreateInfo *createInfo)
{
const usize hash = HashSamplerCreateInfo(createInfo);
for (u32 index = 0; usize samplerHash : m_SamplerHashes)
{
if (samplerHash == hash)
{
return {index};
}
++index;
}
vk::Sampler sampler;
AbortIfFailed(device->m_Device.createSampler(createInfo, nullptr, &sampler));
const u32 index = Cast<u32>(m_SamplerHashes.size());
m_SamplerHashes.push_back(hash);
m_Samplers.push_back(sampler);
return {index};
}
vk::Sampler
SamplerManager::Fetch(const SamplerHandle handle)
{
assert(!handle.IsInvalid());
return m_Samplers[handle.m_Index];
}
void
SamplerManager::Destroy(const Device *device)
{
for (const auto &sampler : m_Samplers)
{
device->m_Device.destroy(sampler, nullptr);
}
m_Samplers.clear();
m_SamplerHashes.clear();
}
GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info) GpuResourceManager::WriteInfo::WriteInfo(vk::DescriptorBufferInfo info)
: uBufferInfo(info) : uBufferInfo(info)
{ {
@ -323,12 +396,14 @@ GpuResourceManager::Release(Texture *texture, TextureHandle handle)
} }
TextureHandle TextureHandle
GpuResourceManager::CommitTexture(Texture *texture) GpuResourceManager::CommitTexture(Texture *texture, const SamplerHandle sampler)
{ {
TextureHandle handle = m_TextureManager.Commit(texture); TextureHandle handle = m_TextureManager.Commit(texture);
const vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler);
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = nullptr, .sampler = samplerImpl,
.imageView = texture->m_View, .imageView = texture->m_View,
.imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
}); });
@ -352,12 +427,14 @@ GpuResourceManager::CommitTexture(Texture *texture)
} }
StorageTextureHandle StorageTextureHandle
GpuResourceManager::CommitStorageTexture(StorageTexture *storageTexture) GpuResourceManager::CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler)
{ {
StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture); StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture);
vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler);
m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ m_WriteInfos.emplace_back(vk::DescriptorImageInfo{
.sampler = nullptr, .sampler = samplerImpl,
.imageView = storageTexture->m_View, .imageView = storageTexture->m_View,
.imageLayout = vk::ImageLayout::eGeneral, .imageLayout = vk::ImageLayout::eGeneral,
}); });
@ -432,8 +509,16 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
u32 storageTexturesCount = u32 storageTexturesCount =
eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize)); eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast<u32>(maxSize));
// TODO: Switch to bindless samplers / multiple sampler configurations INFO("Max Buffer Count: {}", buffersCount);
vk::SamplerCreateInfo samplerCreateInfo = { INFO("Max Texture Count: {}", texturesCount);
INFO("Max Storage Texture Count: {}", storageTexturesCount);
m_BufferManager.Init(buffersCount);
m_TextureManager.Init(texturesCount);
m_StorageTextureManager.Init(storageTexturesCount);
m_SamplerManager.Init(storageTexturesCount);
m_DefaultSamplerCreateInfo = {
.magFilter = vk::Filter::eLinear, .magFilter = vk::Filter::eLinear,
.minFilter = vk::Filter::eLinear, .minFilter = vk::Filter::eLinear,
.mipmapMode = vk::SamplerMipmapMode::eLinear, .mipmapMode = vk::SamplerMipmapMode::eLinear,
@ -449,15 +534,8 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
.borderColor = vk::BorderColor::eFloatOpaqueBlack, .borderColor = vk::BorderColor::eFloatOpaqueBlack,
.unnormalizedCoordinates = false, .unnormalizedCoordinates = false,
}; };
AbortIfFailed(device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_ImmutableSampler));
INFO("Max Buffer Count: {}", buffersCount); m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo));
INFO("Max Texture Count: {}", texturesCount);
INFO("Max Storage Texture Count: {}", storageTexturesCount);
m_BufferManager.Init(buffersCount);
m_TextureManager.Init(texturesCount);
m_StorageTextureManager.Init(storageTexturesCount);
eastl::array poolSizes = { eastl::array poolSizes = {
vk::DescriptorPoolSize{ vk::DescriptorPoolSize{
@ -495,7 +573,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
.pBindingFlags = layoutBindingFlags.data(), .pBindingFlags = layoutBindingFlags.data(),
}; };
eastl::vector immutableSamplers(texturesCount, m_ImmutableSampler);
eastl::array descriptorLayoutBindings = { eastl::array descriptorLayoutBindings = {
vk::DescriptorSetLayoutBinding{ vk::DescriptorSetLayoutBinding{
.binding = BUFFER_BINDING_INDEX, .binding = BUFFER_BINDING_INDEX,
@ -508,7 +585,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize)
.descriptorType = vk::DescriptorType::eCombinedImageSampler, .descriptorType = vk::DescriptorType::eCombinedImageSampler,
.descriptorCount = Cast<u32>(texturesCount), .descriptorCount = Cast<u32>(texturesCount),
.stageFlags = vk::ShaderStageFlagBits::eAll, .stageFlags = vk::ShaderStageFlagBits::eAll,
.pImmutableSamplers = immutableSamplers.data(),
}, },
vk::DescriptorSetLayoutBinding{ vk::DescriptorSetLayoutBinding{
.binding = STORAGE_TEXTURE_BINDING_INDEX, .binding = STORAGE_TEXTURE_BINDING_INDEX,
@ -552,7 +628,7 @@ GpuResourceManager::~GpuResourceManager()
m_BufferManager.Destroy(m_Device); m_BufferManager.Destroy(m_Device);
m_TextureManager.Destroy(m_Device); m_TextureManager.Destroy(m_Device);
m_StorageTextureManager.Destroy(m_Device); m_StorageTextureManager.Destroy(m_Device);
m_Device->m_Device.destroy(m_ImmutableSampler, nullptr); m_SamplerManager.Destroy(m_Device);
m_Device->m_Device.destroy(m_DescriptorPool, nullptr); m_Device->m_Device.destroy(m_DescriptorPool, nullptr);
m_Device->m_Device.destroy(m_SetLayout, nullptr); m_Device->m_Device.destroy(m_SetLayout, nullptr);
} }
@ -561,10 +637,10 @@ GpuResourceManager::GpuResourceManager(GpuResourceManager &&other) noexcept
: m_WriteInfos(std::move(other.m_WriteInfos)) : m_WriteInfos(std::move(other.m_WriteInfos))
, m_Writes(std::move(other.m_Writes)) , m_Writes(std::move(other.m_Writes))
, m_WriteOwner(std::move(other.m_WriteOwner)) , m_WriteOwner(std::move(other.m_WriteOwner))
, m_ImmutableSampler(other.m_ImmutableSampler)
, m_BufferManager(std::move(other.m_BufferManager)) , m_BufferManager(std::move(other.m_BufferManager))
, m_TextureManager(std::move(other.m_TextureManager)) , m_TextureManager(std::move(other.m_TextureManager))
, m_StorageTextureManager(std::move(other.m_StorageTextureManager)) , m_StorageTextureManager(std::move(other.m_StorageTextureManager))
, m_SamplerManager(std::move(other.m_SamplerManager))
, m_Device(Take(other.m_Device)) , m_Device(Take(other.m_Device))
, m_DescriptorPool(other.m_DescriptorPool) , m_DescriptorPool(other.m_DescriptorPool)
, m_SetLayout(other.m_SetLayout) , m_SetLayout(other.m_SetLayout)
@ -586,10 +662,10 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept
m_WriteInfos = std::move(other.m_WriteInfos); m_WriteInfos = std::move(other.m_WriteInfos);
m_Writes = std::move(other.m_Writes); m_Writes = std::move(other.m_Writes);
m_WriteOwner = std::move(other.m_WriteOwner); m_WriteOwner = std::move(other.m_WriteOwner);
m_ImmutableSampler = other.m_ImmutableSampler;
m_BufferManager = std::move(other.m_BufferManager); m_BufferManager = std::move(other.m_BufferManager);
m_TextureManager = std::move(other.m_TextureManager); m_TextureManager = std::move(other.m_TextureManager);
m_StorageTextureManager = std::move(other.m_StorageTextureManager); m_StorageTextureManager = std::move(other.m_StorageTextureManager);
m_SamplerManager = std::move(other.m_SamplerManager);
m_Device = Take(other.m_Device); // Ensure taken. m_Device = Take(other.m_Device); // Ensure taken.
m_DescriptorPool = other.m_DescriptorPool; m_DescriptorPool = other.m_DescriptorPool;
m_SetLayout = other.m_SetLayout; m_SetLayout = other.m_SetLayout;
@ -602,4 +678,10 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept
assert(!other.m_Device); assert(!other.m_Device);
return *this; return *this;
}
SamplerHandle
GpuResourceManager::CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo)
{
return m_SamplerManager.Create(m_Device, samplerCreateInfo);
} }

View File

@ -39,6 +39,10 @@ struct StorageTextureHandle : GpuResourceHandle
{ {
}; };
struct SamplerHandle : GpuResourceHandle
{
};
struct TextureManager struct TextureManager
{ {
eastl::vector<Texture> m_Textures; eastl::vector<Texture> m_Textures;
@ -72,6 +76,18 @@ struct StorageTextureManager : TextureManager
void Release(const Device *device, StorageTextureHandle handle); void Release(const Device *device, StorageTextureHandle handle);
}; };
struct SamplerManager
{
// There can only be so many samplers.
eastl::vector<vk::Sampler> m_Samplers;
eastl::vector<usize> m_SamplerHashes;
void Init(usize size);
SamplerHandle Create(const Device *device, const vk::SamplerCreateInfo *createInfo);
vk::Sampler Fetch(SamplerHandle handle);
void Destroy(const Device *device);
};
struct GpuResourceManager struct GpuResourceManager
{ {
private: private:
@ -102,11 +118,12 @@ struct GpuResourceManager
eastl::vector<vk::WriteDescriptorSet> m_Writes; eastl::vector<vk::WriteDescriptorSet> m_Writes;
eastl::vector<WriteOwner> m_WriteOwner; eastl::vector<WriteOwner> m_WriteOwner;
vk::Sampler m_ImmutableSampler; vk::Sampler m_DefaultSampler;
BufferManager m_BufferManager; BufferManager m_BufferManager;
TextureManager m_TextureManager; TextureManager m_TextureManager;
StorageTextureManager m_StorageTextureManager; StorageTextureManager m_StorageTextureManager;
SamplerManager m_SamplerManager;
void EraseWrites(u32 handleIndex, HandleType handleType); void EraseWrites(u32 handleIndex, HandleType handleType);
@ -117,6 +134,8 @@ struct GpuResourceManager
constexpr static u32 TEXTURE_BINDING_INDEX = 1; constexpr static u32 TEXTURE_BINDING_INDEX = 1;
constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2; constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2;
vk::SamplerCreateInfo m_DefaultSamplerCreateInfo;
vk::DescriptorPool m_DescriptorPool; vk::DescriptorPool m_DescriptorPool;
vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSetLayout m_SetLayout;
vk::DescriptorSet m_DescriptorSet; vk::DescriptorSet m_DescriptorSet;
@ -126,13 +145,16 @@ struct GpuResourceManager
void Release(BufferHandle handle); // Release and Destroy void Release(BufferHandle handle); // Release and Destroy
void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return
TextureHandle CommitTexture(Texture *texture); // Commit to GPU and take Ownership TextureHandle CommitTexture(Texture *texture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
void Release(TextureHandle handle); // Release and Destroy void Release(TextureHandle handle); // Release and Destroy
void Release(Texture *texture, TextureHandle handle); // Release and Return void Release(Texture *texture, TextureHandle handle); // Release and Return
StorageTextureHandle CommitStorageTexture(StorageTexture *storageTexture); // Commit to GPU and take Ownership StorageTextureHandle
void Release(StorageTextureHandle handle); // Release and Destroy CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership
void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return void Release(StorageTextureHandle handle); // Release and Destroy
void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return
SamplerHandle CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo);
void Update(); // Update all the descriptors required. void Update(); // Update all the descriptors required.

View File

@ -24,6 +24,7 @@ add_shader(model_render shader/background.vs.hlsl)
add_shader(model_render shader/background.ps.hlsl) add_shader(model_render shader/background.ps.hlsl)
add_shader(model_render shader/diffuse_irradiance.cs.hlsl) add_shader(model_render shader/diffuse_irradiance.cs.hlsl)
add_shader(model_render shader/prefilter.cs.hlsl) add_shader(model_render shader/prefilter.cs.hlsl)
add_shader(model_render shader/brdf_lut.cs.hlsl)
target_link_libraries(model_render PRIVATE aster_core) target_link_libraries(model_render PRIVATE aster_core)
target_link_libraries(model_render PRIVATE util_helper) target_link_libraries(model_render PRIVATE util_helper)

View File

@ -272,7 +272,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
vk::ImageMemoryBarrier2 imageReadyBarrier = { vk::ImageMemoryBarrier2 imageReadyBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcStageMask = vk::PipelineStageFlagBits2::eTransfer,
.srcAccessMask = vk::AccessFlagBits2::eTransferWrite, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, .dstStageMask = finalStage,
.dstAccessMask = vk::AccessFlagBits2::eShaderRead, .dstAccessMask = vk::AccessFlagBits2::eShaderRead,
.oldLayout = vk::ImageLayout::eTransferSrcOptimal, .oldLayout = vk::ImageLayout::eTransferSrcOptimal,
.newLayout = finalLayout, .newLayout = finalLayout,

View File

@ -17,25 +17,43 @@
constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv"; constexpr cstr EQUIRECT_TO_CUBE_SHADER_FILE = "shader/eqrect_to_cube.cs.hlsl.spv";
constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv"; constexpr cstr DIFFUSE_IRRADIANCE_SHADER_FILE = "shader/diffuse_irradiance.cs.hlsl.spv";
constexpr cstr PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv"; constexpr cstr PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv";
constexpr cstr BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv";
eastl::tuple<TextureHandle, TextureHandle, TextureHandle> void
Environment::Destroy(GpuResourceManager *resourceManager)
{
resourceManager->Release(Take(m_Skybox));
resourceManager->Release(Take(m_Diffuse));
resourceManager->Release(Take(m_Prefilter));
resourceManager->Release(Take(m_BrdfLut));
}
Environment
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv, CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv,
const cstr name) const cstr name)
{ {
GpuResourceManager *resMan = assetLoader->m_ResourceManager; GpuResourceManager *resMan = assetLoader->m_ResourceManager;
const Device *pDevice = resMan->m_Device; const Device *pDevice = resMan->m_Device;
vk::SamplerCreateInfo brdfLutSamplerCreateInfo = resMan->m_DefaultSamplerCreateInfo;
brdfLutSamplerCreateInfo.addressModeU = vk::SamplerAddressMode::eClampToEdge;
brdfLutSamplerCreateInfo.addressModeV = vk::SamplerAddressMode::eClampToEdge;
brdfLutSamplerCreateInfo.addressModeW = vk::SamplerAddressMode::eClampToEdge;
StorageTextureCube skybox; StorageTextureCube skybox;
StorageTextureCube diffuseIrradiance;
StorageTextureCube prefilterCube;
StorageTexture brdfLut;
SamplerHandle brdfLutSampler;
skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox"); skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox");
TextureHandle skyboxHandle = resMan->CommitTexture(&skybox); TextureHandle skyboxHandle = resMan->CommitTexture(&skybox);
StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox); StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox);
StorageTextureCube diffuseIrradiance;
diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance"); diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance");
TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance); TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance);
StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance); StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance);
StorageTextureCube prefilterCube;
prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter"); prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter");
TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us. TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us.
constexpr u32 prefilterMipCountMax = 6; constexpr u32 prefilterMipCountMax = 6;
@ -61,6 +79,11 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
tex = resMan->CommitStorageTexture(&prefilterCube); tex = resMan->CommitStorageTexture(&prefilterCube);
} }
brdfLut.Init(pDevice, {512, 512}, vk::Format::eR16G16Sfloat, true, "BRDF LUT");
brdfLutSampler = resMan->CreateSampler(&brdfLutSamplerCreateInfo);
TextureHandle brdfLutHandle = resMan->CommitTexture(&brdfLut, brdfLutSampler);
StorageTextureHandle brdfLutStorageHandle = resMan->CommitStorageTexture(&brdfLut);
#pragma region Dependencies and Copies #pragma region Dependencies and Copies
vk::ImageSubresourceRange cubeSubresRange = { vk::ImageSubresourceRange cubeSubresRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor, .aspectMask = vk::ImageAspectFlagBits::eColor,
@ -69,6 +92,13 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
.baseArrayLayer = 0, .baseArrayLayer = 0,
.layerCount = 6, .layerCount = 6,
}; };
vk::ImageSubresourceRange lutSubresRange = {
.aspectMask = vk::ImageAspectFlagBits::eColor,
.baseMipLevel = 0,
.levelCount = 1,
.baseArrayLayer = 0,
.layerCount = 1,
};
vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = { vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = {
.srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe,
@ -81,48 +111,57 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.subresourceRange = cubeSubresRange, .subresourceRange = cubeSubresRange,
}; };
eastl::fixed_vector<vk::ImageMemoryBarrier2, 3> readyToWriteBarriers(2, readyToWriteBarrierTemplate); eastl::fixed_vector<vk::ImageMemoryBarrier2, 4> readyToWriteBarriers(4, readyToWriteBarrierTemplate);
readyToWriteBarriers[0].image = skybox.m_Image; readyToWriteBarriers[0].image = skybox.m_Image;
readyToWriteBarriers[1].image = diffuseIrradiance.m_Image; readyToWriteBarriers[1].image = diffuseIrradiance.m_Image;
readyToWriteBarriers[2].image = prefilterCube.m_Image;
readyToWriteBarriers[3].image = brdfLut.m_Image;
readyToWriteBarriers[3].subresourceRange = lutSubresRange;
vk::DependencyInfo readyToWriteDependency = { vk::DependencyInfo readyToWriteDependency = {
.imageMemoryBarrierCount = Cast<u32>(readyToWriteBarriers.size()), .imageMemoryBarrierCount = Cast<u32>(readyToWriteBarriers.size()),
.pImageMemoryBarriers = readyToWriteBarriers.data(), .pImageMemoryBarriers = readyToWriteBarriers.data(),
}; };
vk::ImageMemoryBarrier2 skyboxWriteToReadBarrier = { vk::ImageMemoryBarrier2 readyToSampleBarrierTemplate = {
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
.dstAccessMask = vk::AccessFlagBits2::eShaderStorageRead,
.oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eGeneral,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = skybox.m_Image,
.subresourceRange = cubeSubresRange,
};
vk::DependencyInfo skyboxWriteToReadDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &skyboxWriteToReadBarrier,
};
vk::ImageMemoryBarrier2 skyboxToSampleBarrier = {
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead, .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead,
.dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
.dstAccessMask = vk::AccessFlagBits2::eShaderSampledRead,
.oldLayout = vk::ImageLayout::eGeneral, .oldLayout = vk::ImageLayout::eGeneral,
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = skybox.m_Image,
.subresourceRange = cubeSubresRange, .subresourceRange = cubeSubresRange,
}; };
auto skyboxToSampleBarrier = readyToSampleBarrierTemplate;
skyboxToSampleBarrier.image = skybox.m_Image;
auto diffIrrToSampleBarrier = readyToSampleBarrierTemplate;
diffIrrToSampleBarrier.image = diffuseIrradiance.m_Image;
auto prefilterToSampleBarrier = readyToSampleBarrierTemplate;
prefilterToSampleBarrier.image = prefilterCube.m_Image;
auto brdfToSampleBarrier = readyToSampleBarrierTemplate;
prefilterToSampleBarrier.image = brdfLut.m_Image;
prefilterToSampleBarrier.subresourceRange = lutSubresRange;
vk::DependencyInfo skyboxToSampleDependency = { vk::DependencyInfo skyboxToSampleDependency = {
.imageMemoryBarrierCount = 1, .imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &skyboxToSampleBarrier, .pImageMemoryBarriers = &skyboxToSampleBarrier,
}; };
vk::DependencyInfo diffIrrToSampleDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &diffIrrToSampleBarrier,
};
vk::DependencyInfo prefilterToSampleDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &prefilterToSampleBarrier,
};
vk::DependencyInfo brdfToSampleDependency = {
.imageMemoryBarrierCount = 1,
.pImageMemoryBarriers = &brdfToSampleBarrier,
};
#pragma endregion #pragma endregion
@ -146,13 +185,17 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
f32 m_Roughness; f32 m_Roughness;
u32 m_EnvSide; u32 m_EnvSide;
}; };
struct BrdfLutPushConstants
{
StorageTextureHandle m_OutputTexture;
};
#pragma region Pipeline Creation etc #pragma region Pipeline Creation etc
vk::PushConstantRange pcr = { vk::PushConstantRange pcr = {
.stageFlags = vk::ShaderStageFlagBits::eCompute, .stageFlags = vk::ShaderStageFlagBits::eCompute,
.offset = 0, .offset = 0,
.size = eastl::max(sizeof(SkyboxPushConstants), .size = eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))), eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))),
}; };
@ -168,6 +211,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE); const auto eqRectToCubeShader = CreateShader(pDevice, EQUIRECT_TO_CUBE_SHADER_FILE);
const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); const auto diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE);
const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE); const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE);
const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE);
eastl::array computePipelineCreateInfo = { eastl::array computePipelineCreateInfo = {
vk::ComputePipelineCreateInfo{ vk::ComputePipelineCreateInfo{
.stage = .stage =
@ -196,6 +240,15 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
}, },
.layout = pipelineLayout, .layout = pipelineLayout,
}, },
vk::ComputePipelineCreateInfo{
.stage =
{
.stage = vk::ShaderStageFlagBits::eCompute,
.module = brdfLutShader,
.pName = "main",
},
.layout = pipelineLayout,
},
}; };
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines; eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
@ -206,6 +259,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
vk::Pipeline eqRectToCubePipeline = pipelines[0]; vk::Pipeline eqRectToCubePipeline = pipelines[0];
vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; vk::Pipeline diffuseIrradiancePipeline = pipelines[1];
vk::Pipeline prefilterPipeline = pipelines[2]; vk::Pipeline prefilterPipeline = pipelines[2];
vk::Pipeline brdfLutPipeline = pipelines[3];
for (auto &createInfos : computePipelineCreateInfo) for (auto &createInfos : computePipelineCreateInfo)
{ {
@ -228,6 +282,9 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
.m_SkyboxHandle = skyboxHandle, .m_SkyboxHandle = skyboxHandle,
.m_EnvSide = cubeSide, .m_EnvSide = cubeSide,
}; };
BrdfLutPushConstants brdfLutPushConstants = {
.m_OutputTexture = brdfLutStorageHandle,
};
resMan->Update(); resMan->Update();
@ -251,6 +308,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline);
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant,
&skyboxPushConstant); &skyboxPushConstant);
assert(skybox.m_Extent.width % 16 == 0 && skybox.m_Extent.height % 16 == 0);
cmd.dispatch(skybox.m_Extent.width / 16, skybox.m_Extent.height / 16, 6); cmd.dispatch(skybox.m_Extent.width / 16, skybox.m_Extent.height / 16, 6);
GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral, GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral,
@ -259,10 +317,14 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline);
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant,
&diffuseIrradiancePushConstants); &diffuseIrradiancePushConstants);
assert(diffuseIrradiance.m_Extent.width % 16 == 0 && diffuseIrradiance.m_Extent.height % 16 == 0);
cmd.dispatch(diffuseIrradiance.m_Extent.width / 16, diffuseIrradiance.m_Extent.width / 16, 6); cmd.dispatch(diffuseIrradiance.m_Extent.width / 16, diffuseIrradiance.m_Extent.width / 16, 6);
cmd.pipelineBarrier2(&diffIrrToSampleDependency);
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline);
u32 mipSize = prefilterCube.m_Extent.width; u32 mipSize = prefilterCube.m_Extent.width;
assert(mipSize % 16 == 0);
for (u32 mipCount = 0; auto &tex : prefilterStorageHandles) for (u32 mipCount = 0; auto &tex : prefilterStorageHandles)
{ {
prefilterPushConstants.m_OutputTexture = tex; prefilterPushConstants.m_OutputTexture = tex;
@ -278,6 +340,13 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
} }
cmd.pipelineBarrier2(&skyboxToSampleDependency); cmd.pipelineBarrier2(&skyboxToSampleDependency);
cmd.pipelineBarrier2(&prefilterToSampleDependency);
cmd.bindPipeline(vk::PipelineBindPoint::eCompute, brdfLutPipeline);
cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof brdfLutPushConstants,
&brdfLutPushConstants);
assert(brdfLut.m_Extent.width % 16 == 0 && brdfLut.m_Extent.height % 16 == 0);
cmd.dispatch(brdfLut.m_Extent.width / 16, brdfLut.m_Extent.height / 16, 1);
#if !defined(ASTER_NDEBUG) #if !defined(ASTER_NDEBUG)
cmd.endDebugUtilsLabelEXT(); cmd.endDebugUtilsLabelEXT();
@ -304,6 +373,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
skybox = {}; skybox = {};
resMan->Release(skyboxStorageHandle); resMan->Release(skyboxStorageHandle);
resMan->Release(diffuseIrradianceStorageHandle); resMan->Release(diffuseIrradianceStorageHandle);
resMan->Release(brdfLutStorageHandle);
for (auto &texHandles : prefilterStorageHandles) for (auto &texHandles : prefilterStorageHandles)
{ {
StorageTextureCube st; StorageTextureCube st;
@ -316,5 +386,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
} }
pDevice->m_Device.destroy(pipelineLayout, nullptr); pDevice->m_Device.destroy(pipelineLayout, nullptr);
return {skyboxHandle, diffuseIrradianceHandle, prefilterHandle}; return {
.m_Skybox = skyboxHandle,
.m_Diffuse = diffuseIrradianceHandle,
.m_Prefilter = prefilterHandle,
.m_BrdfLut = brdfLutHandle,
};
} }

View File

@ -15,6 +15,16 @@ struct Texture;
struct TextureCube; struct TextureCube;
struct AssetLoader; struct AssetLoader;
eastl::tuple<TextureHandle, TextureHandle, TextureHandle> struct Environment
{
TextureHandle m_Skybox;
TextureHandle m_Diffuse;
TextureHandle m_Prefilter;
TextureHandle m_BrdfLut;
void Destroy(GpuResourceManager *resourceManager);
};
Environment
CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv, CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv,
cstr name = nullptr); cstr name = nullptr);

View File

@ -176,13 +176,13 @@ main(int, char **)
LightManager lightManager = LightManager{&resourceManager}; LightManager lightManager = LightManager{&resourceManager};
Model model = assetLoader.LoadModelToGpu(MODEL_FILE); Model model = assetLoader.LoadModelToGpu(MODEL_FILE);
Texture environment; Texture environmentHdri;
assetLoader.LoadHdrImage(&environment, BACKDROP_FILE); assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE);
auto envHandle = resourceManager.CommitTexture(&environment); auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri);
auto [texCube, diffuseIrr, prefilter] = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHandle, "Cube Env"); auto environment = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env");
resourceManager.Release(envHandle); resourceManager.Release(envHdriHandle);
vk::Format attachmentFormat = vk::Format::eR8G8B8A8Srgb; vk::Format attachmentFormat = vk::Format::eR8G8B8A8Srgb;
@ -374,6 +374,8 @@ main(int, char **)
bool showDiffuse = false; bool showDiffuse = false;
bool useDiffuse = true; bool useDiffuse = true;
bool showPrefilter = false; bool showPrefilter = false;
bool useSpecular = true;
i32 height = Cast<i32>(internalResolution.height); i32 height = Cast<i32>(internalResolution.height);
f32 camPitch = glm::degrees(cameraController.m_Pitch); f32 camPitch = glm::degrees(cameraController.m_Pitch);
f32 camYaw = glm::degrees(cameraController.m_Yaw); f32 camYaw = glm::degrees(cameraController.m_Yaw);
@ -453,6 +455,7 @@ main(int, char **)
gui::Checkbox("Show DiffIrr", &showDiffuse); gui::Checkbox("Show DiffIrr", &showDiffuse);
gui::Checkbox("Use DiffIrr", &useDiffuse); gui::Checkbox("Use DiffIrr", &useDiffuse);
gui::Checkbox("Show Prefilter", &showPrefilter); gui::Checkbox("Show Prefilter", &showPrefilter);
gui::Checkbox("Use Specular", &useSpecular);
gui::Separator(); gui::Separator();
gui::Checkbox("Rotate", &rotating); gui::Checkbox("Rotate", &rotating);
gui::PopItemWidth(); gui::PopItemWidth();
@ -556,37 +559,31 @@ main(int, char **)
&model.m_Handles); &model.m_Handles);
pcbOffset += sizeof model.m_Handles; pcbOffset += sizeof model.m_Handles;
Environment thisFrameEnvBuffers = environment;
if (showDiffuse) if (showDiffuse)
{ {
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, thisFrameEnvBuffers.m_Skybox = environment.m_Diffuse;
&diffuseIrr);
pcbOffset += sizeof diffuseIrr;
} }
else if (showPrefilter) else if (showPrefilter)
{ {
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof prefilter, thisFrameEnvBuffers.m_Skybox = environment.m_Prefilter;
&prefilter);
pcbOffset += sizeof prefilter;
}
else
{
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube);
pcbOffset += sizeof texCube;
} }
if (useDiffuse) if (!useDiffuse)
{ {
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, thisFrameEnvBuffers.m_Diffuse = {};
&diffuseIrr);
pcbOffset += sizeof diffuseIrr;
} }
else if (!useSpecular)
{ {
TextureHandle invalid; thisFrameEnvBuffers.m_BrdfLut = {};
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof invalid, &invalid); thisFrameEnvBuffers.m_Prefilter = {};
pcbOffset += sizeof invalid;
} }
static_assert(sizeof texCube == sizeof diffuseIrr);
assert(pcbOffset == 16);
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof thisFrameEnvBuffers,
&thisFrameEnvBuffers);
pcbOffset += sizeof thisFrameEnvBuffers;
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof lightManager.m_MetaInfo, cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof lightManager.m_MetaInfo,
&lightManager.m_MetaInfo); &lightManager.m_MetaInfo);
pcbOffset += sizeof lightManager.m_MetaInfo; pcbOffset += sizeof lightManager.m_MetaInfo;
@ -664,9 +661,7 @@ main(int, char **)
AbortIfFailed(device.m_Device.waitIdle()); AbortIfFailed(device.m_Device.waitIdle());
resourceManager.Release(texCube); environment.Destroy(&resourceManager);
resourceManager.Release(diffuseIrr);
resourceManager.Release(prefilter);
pipelineCacheData = device.DumpPipelineCache(); pipelineCacheData = device.DumpPipelineCache();
ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written");

View File

@ -57,7 +57,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou
vk::PushConstantRange pushConstantRange = { vk::PushConstantRange pushConstantRange = {
.stageFlags = vk::ShaderStageFlagBits::eAll, .stageFlags = vk::ShaderStageFlagBits::eAll,
.offset = 0, .offset = 0,
.size = 44, .size = 52,
}; };
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
@ -206,7 +206,7 @@ CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, cons
vk::PushConstantRange pushConstantRange = { vk::PushConstantRange pushConstantRange = {
.stageFlags = vk::ShaderStageFlagBits::eAll, .stageFlags = vk::ShaderStageFlagBits::eAll,
.offset = 0, .offset = 0,
.size = 44, .size = 52,
}; };
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {

View File

@ -60,8 +60,10 @@ static const float TAU = 6.28318530f;
[[vk::binding(0, 0)]] StructuredBuffer<DirectionalLight> DirectionalLightBuffer[]; [[vk::binding(0, 0)]] StructuredBuffer<DirectionalLight> DirectionalLightBuffer[];
[[vk::binding(1, 0)]] Texture2D<float4> Textures[]; [[vk::binding(1, 0)]] Texture2D<float4> Textures[];
[[vk::binding(1, 0)]] Texture2D<float2> TexturesRG[];
[[vk::binding(1, 0)]] TextureCube<float4> TextureCubes[]; [[vk::binding(1, 0)]] TextureCube<float4> TextureCubes[];
[[vk::binding(1, 0)]] SamplerState ImmutableSamplers[]; [[vk::binding(1, 0)]] SamplerState ImmutableSamplers[];
[[vk::binding(2, 0)]] RWTexture2D<float4> StorageTextures[]; [[vk::binding(2, 0)]] RWTexture2D<float4> StorageTextures[];
[[vk::binding(2, 0)]] RWTexture2D<float2> StorageTexturesRG[];
[[vk::binding(2, 0)]] RWTexture2DArray<float4> StorageTextureArrays[]; [[vk::binding(2, 0)]] RWTexture2DArray<float4> StorageTextureArrays[];

View File

@ -0,0 +1,82 @@
#include "ibl_common.hlsli"
struct Block
{
uint OutputTextureHandle;
};
[[vk::push_constant]]
Block Pcb;
float GeometrySchlickGGX(float NdotV, float Roughness)
{
float R = Roughness;
// (Rough + 1)^2 / 8 for Punctual Lights
// Rough^2 / 2 for IBL
float K = (R * R) / 2.0;
float Numerator = NdotV;
float Denominator = NdotV * (1.0f - K) + K;
return Numerator / Denominator;
}
float GeometrySmith(float NdotV, float NdotL, float Roughness)
{
float GGX1 = GeometrySchlickGGX(NdotV, Roughness);
float GGX2 = GeometrySchlickGGX(NdotL, Roughness);
return GGX1 * GGX2;
}
float2 IntegrateBRDF(float NdotV, float Roughness)
{
float3 ViewDir;
ViewDir.x = sqrt(1.0f - NdotV * NdotV);
ViewDir.y = 0.0f;
ViewDir.z = NdotV;
float A = 0.0f;
float B = 0.0f;
float3 Normal = float3(0.0f, 0.0f, 1.0f);
const uint SAMPLE_COUNT = 1024u;
for (uint i = 0u; i < SAMPLE_COUNT; ++i)
{
float2 Xi = Hammersley(i, SAMPLE_COUNT);
float3 Halfway = ImportanceSampleGGX(Xi, Normal, Roughness);
float3 LightDir = normalize(2.0f * dot(ViewDir, Halfway) * Halfway - ViewDir);
float NdotL = max(LightDir.z, 0.0f);
float NdotH = max(Halfway.z, 0.0f);
float VdotH = max(dot(ViewDir, Halfway), 0.0f);
if (NdotL > 0.0f)
{
float G = GeometrySmith(NdotV, NdotL, Roughness);
float G_Vis = (G * VdotH) / max((NdotH * NdotV), 0.0001f);
float Fc = pow(1.0f - VdotH, 5.0f);
A += (1.0f - Fc) * G_Vis;
B += Fc * G_Vis;
}
}
A /= float(SAMPLE_COUNT);
B /= float(SAMPLE_COUNT);
return float2(A, B);
}
[numthreads(16, 16, 1)]
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
{
float Width, Height;
StorageTexturesRG[Pcb.OutputTextureHandle].GetDimensions(Width, Height);
float2 UV = GlobalInvocationID.xy / float2(Width, Height);
float2 IntegratedBRDF = IntegrateBRDF(UV.x, UV.y);
StorageTexturesRG[Pcb.OutputTextureHandle][GlobalInvocationID.xy] = IntegratedBRDF;
}

View File

@ -27,7 +27,7 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
float3 Forward, Up, Right; float3 Forward, Up, Right;
Forward = GetCubeDir(GlobalInvocationID, pcb.CubeSide); Forward = GetCubeDir(GlobalInvocationID, pcb.CubeSide);
Up = float3(0.0f, 1.0f, 0.0f); // 0.01f offset to Up = abs(Forward.y) < 1.0f ? float3(0.0f, 1.0f, 0.0f) : float3(1.0f, 0.0f, 0.0f); // 0.01f offset to
Right = normalize(cross(Up, Forward)); Right = normalize(cross(Up, Forward));
Up = normalize(cross(Forward, Right)); Up = normalize(cross(Forward, Right));

View File

@ -15,11 +15,16 @@ struct Block
uint VertexDataHandle; uint VertexDataHandle;
uint MaterialBufferHandle; uint MaterialBufferHandle;
uint NodeBufferHandle; uint NodeBufferHandle;
uint EnvCubeHandle; uint EnvCubeHandle;
uint DiffuseIrradianceHandle; uint DiffuseIrradianceHandle;
uint PrefilterHandle;
uint BrdfLutHandle;
uint LightHandle; uint LightHandle;
uint PointLightIndexer; uint PointLightIndexer;
uint DirectionalLightIndexer; uint DirectionalLightIndexer;
int MaterialIdx; int MaterialIdx;
uint NodeIdx; uint NodeIdx;
}; };

View File

@ -36,4 +36,42 @@ float3 GetCubeDir(uint3 GlobalInvocationID, float SideLength)
// Never reach here. // Never reach here.
return 0.0f.xxx; return 0.0f.xxx;
} }
}
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
}
float2 Hammersley(uint SampleIndex, uint SampleCount)
{
return float2(float(SampleIndex) / float(SampleCount), RadicalInverse_VdC(SampleIndex));
}
float3 ImportanceSampleGGX(float2 Xi, float3 Normal, float Roughness)
{
float A = Roughness * Roughness;
float Phi = 2.0f * PI * Xi.x;
float CosTheta = sqrt((1.0f - Xi.y) / (1.0f + (A * A - 1.0f) * Xi.y));
float SinTheta = sqrt(1.0f - CosTheta * CosTheta);
// from spherical coordinates to cartesian coordinates
float3 H;
H.x = cos(Phi) * SinTheta;
H.y = sin(Phi) * SinTheta;
H.z = CosTheta;
// from tangent-space vector to world-space sample vector
float3 Up = abs(Normal.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f);
float3 Tangent = normalize(cross(Up, Normal));
float3 Bitangent = cross(Normal, Tangent);
float3 SampleVec = Tangent * H.x + Bitangent * H.y + Normal * H.z;
return normalize(SampleVec);
} }

View File

@ -77,6 +77,26 @@ float3 SampleIrradiance(float3 Direction)
return 0.04f.xxx; return 0.04f.xxx;
} }
float3 SamplePrefiltered(float3 Direction, float Roughness)
{
const float MAX_MIP_LEVEL = 5.0f;
float Mip = MAX_MIP_LEVEL * Roughness;
if (PushConstant.PrefilterHandle != INVALID_HANDLE)
{
return TextureCubes[PushConstant.PrefilterHandle].SampleLevel(ImmutableSamplers[PushConstant.PrefilterHandle], Direction, Mip).rgb;
}
return 0.0f.xxx;
}
float2 SampleBrdfLut(float NdotV, float Roughness)
{
if (PushConstant.BrdfLutHandle != INVALID_HANDLE)
{
return TexturesRG[PushConstant.BrdfLutHandle].Sample(ImmutableSamplers[PushConstant.BrdfLutHandle], float2(NdotV, Roughness));
}
return 0.0f.xx;
}
float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness) float TrowbridgeReitzGGX(float3 Normal, float3 Halfway, float Roughness)
{ {
float Coeff = Roughness * Roughness; float Coeff = Roughness * Roughness;
@ -196,7 +216,6 @@ float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position,
return Contrib; return Contrib;
} }
float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal)
{ {
if (PushConstant.LightHandle == INVALID_HANDLE) if (PushConstant.LightHandle == INVALID_HANDLE)
@ -240,18 +259,28 @@ float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Pos
float3 GetAmbientInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal, float Occlusion) float3 GetAmbientInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal, float Occlusion)
{ {
float3 ViewDir = normalize(Camera.Position.xyz - Position); float3 ViewDir = normalize(Camera.Position.xyz - Position);
float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // TODO: Is Normal or is it Halfway? float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient.
float Metal = MetalRough.r;
float Roughness = MetalRough.g;
float3 F_0 = 0.04f.xxx; float3 F_0 = 0.04f.xxx;
F_0 = lerp(F_0, Albedo, MetalRough.r); F_0 = lerp(F_0, Albedo, MetalRough.r);
float3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, MetalRough.g); float3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, Roughness);
float3 K_Diffuse = 1.0f.xxx - K_Specular; float3 K_Diffuse = 1.0f.xxx - K_Specular;
K_Diffuse *= 1.0f - MetalRough.r; // Metals don't have diffuse/refractions. K_Diffuse *= 1.0f - Metal; // Metals don't have diffuse/refractions.
float3 DiffuseIrradiance = K_Diffuse * Albedo * SampleIrradiance(Normal) * Occlusion; float3 ReflectionDir = reflect(-ViewDir, Normal);
return DiffuseIrradiance; float NdotV = max(dot(Normal, ViewDir), 0.0f);
float3 PrefilteredColor = SamplePrefiltered(ReflectionDir, Roughness).rgb;
float2 EnvBRDF = SampleBrdfLut(NdotV, Roughness);
float3 Specular = PrefilteredColor * (K_Specular * EnvBRDF.x + EnvBRDF.y);
float3 DiffuseIrradiance = Albedo * SampleIrradiance(Normal);
return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion;
} }
FS_Output main(FS_Input StageInput) FS_Output main(FS_Input StageInput)

View File

@ -12,44 +12,6 @@ struct Block
[[vk::push_constant]] [[vk::push_constant]]
Block Pcb; Block Pcb;
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
}
float2 Hammersley(uint SampleIndex, uint SampleCount)
{
return float2(float(SampleIndex) / float(SampleCount), RadicalInverse_VdC(SampleIndex));
}
float3 ImportanceSampleGGX(float2 Xi, float3 Normal, float Roughness)
{
float A = Roughness * Roughness;
float Phi = 2.0f * PI * Xi.x;
float CosTheta = sqrt((1.0f - Xi.y) / (1.0f + (A * A - 1.0f) * Xi.y));
float SinTheta = sqrt(1.0f - CosTheta * CosTheta);
// from spherical coordinates to cartesian coordinates
float3 H;
H.x = cos(Phi) * SinTheta;
H.y = sin(Phi) * SinTheta;
H.z = CosTheta;
// from tangent-space vector to world-space sample vector
float3 Up = abs(Normal.z) < 0.999f ? float3(0.0f, 0.0f, 1.0f) : float3(1.0f, 0.0f, 0.0f);
float3 Tangent = normalize(cross(Up, Normal));
float3 Bitangent = cross(Normal, Tangent);
float3 SampleVec = Tangent * H.x + Bitangent * H.y + Normal * H.z;
return normalize(SampleVec);
}
float TrowbridgeReitzGGX(float NdotH, float Roughness) float TrowbridgeReitzGGX(float NdotH, float Roughness)
{ {
float Coeff = Roughness * Roughness; float Coeff = Roughness * Roughness;