From e82b37b2d9a0a2d9e9eff8971a1e534626f5ffe5 Mon Sep 17 00:00:00 2001 From: Anish Bhobe Date: Thu, 1 Aug 2024 01:37:25 +0200 Subject: [PATCH] IBL Specular complete. --- CMakePresets.json | 18 +++ aster/global.h | 4 +- samples/00_util/gpu_resource_manager.cpp | 120 +++++++++++++--- samples/00_util/gpu_resource_manager.h | 36 ++++- samples/03_model_render/CMakeLists.txt | 1 + samples/03_model_render/asset_loader.cpp | 2 +- samples/03_model_render/ibl_helpers.cpp | 129 ++++++++++++++---- samples/03_model_render/ibl_helpers.h | 12 +- samples/03_model_render/model_render.cpp | 51 ++++--- samples/03_model_render/pipeline_utils.cpp | 4 +- .../shader/bindless_structs.hlsli | 2 + .../03_model_render/shader/brdf_lut.cs.hlsl | 82 +++++++++++ .../shader/diffuse_irradiance.cs.hlsl | 2 +- .../shader/graphics_structs.hlsli | 5 + .../03_model_render/shader/ibl_common.hlsli | 38 ++++++ samples/03_model_render/shader/model.ps.hlsl | 43 +++++- .../03_model_render/shader/prefilter.cs.hlsl | 38 ------ 17 files changed, 454 insertions(+), 133 deletions(-) create mode 100644 samples/03_model_render/shader/brdf_lut.cs.hlsl diff --git a/CMakePresets.json b/CMakePresets.json index 9b3f0b0..d45a509 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -40,6 +40,24 @@ "name": "windows-reldebug", "generator": "Ninja", "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": { "CMAKE_BUILD_TYPE": "RelWithDebInfo", "CMAKE_EXPORT_COMPILE_COMMANDS": true, diff --git a/aster/global.h b/aster/global.h index 8725037..a020475 100644 --- a/aster/global.h +++ b/aster/global.h @@ -53,7 +53,7 @@ Failed(const vk::Result result) using NameString = eastl::fixed_string; template -struct std::hash> // NOLINT(*-dcl58-cpp) +struct eastl::hash> // NOLINT(*-dcl58-cpp) { [[nodiscard]] usize operator()(const vk::Flags &val) @@ -66,7 +66,7 @@ template [[nodiscard]] usize HashAny(const T &val) { - return std::hash>()(val); + return eastl::hash>()(val); } [[nodiscard]] inline usize diff --git a/samples/00_util/gpu_resource_manager.cpp b/samples/00_util/gpu_resource_manager.cpp index 9463af7..b8be54f 100644 --- a/samples/00_util/gpu_resource_manager.cpp +++ b/samples/00_util/gpu_resource_manager.cpp @@ -193,6 +193,79 @@ StorageTextureManager::Release(const Device *device, const StorageTextureHandle 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(createInfo->mipLodBias * 1000))); // Resolution of 10^-3 + hash = HashCombine(hash, HashAny(createInfo->anisotropyEnable)); + hash = HashCombine(hash, + HashAny(Cast(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(createInfo->minLod * 1000))); // 0.001 resolution is enough. + hash = HashCombine(hash, + HashAny(Cast(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(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) : uBufferInfo(info) { @@ -323,12 +396,14 @@ GpuResourceManager::Release(Texture *texture, TextureHandle handle) } TextureHandle -GpuResourceManager::CommitTexture(Texture *texture) +GpuResourceManager::CommitTexture(Texture *texture, const SamplerHandle sampler) { TextureHandle handle = m_TextureManager.Commit(texture); + const vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler); + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ - .sampler = nullptr, + .sampler = samplerImpl, .imageView = texture->m_View, .imageLayout = vk::ImageLayout::eShaderReadOnlyOptimal, }); @@ -352,12 +427,14 @@ GpuResourceManager::CommitTexture(Texture *texture) } StorageTextureHandle -GpuResourceManager::CommitStorageTexture(StorageTexture *storageTexture) +GpuResourceManager::CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler) { StorageTextureHandle handle = m_StorageTextureManager.Commit(storageTexture); + vk::Sampler samplerImpl = sampler.IsInvalid() ? m_DefaultSampler : m_SamplerManager.Fetch(sampler); + m_WriteInfos.emplace_back(vk::DescriptorImageInfo{ - .sampler = nullptr, + .sampler = samplerImpl, .imageView = storageTexture->m_View, .imageLayout = vk::ImageLayout::eGeneral, }); @@ -432,8 +509,16 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize) u32 storageTexturesCount = eastl::min(properties.limits.maxPerStageDescriptorStorageImages - 1024, Cast(maxSize)); - // TODO: Switch to bindless samplers / multiple sampler configurations - vk::SamplerCreateInfo samplerCreateInfo = { + INFO("Max Buffer Count: {}", buffersCount); + 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, .minFilter = vk::Filter::eLinear, .mipmapMode = vk::SamplerMipmapMode::eLinear, @@ -449,15 +534,8 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize) .borderColor = vk::BorderColor::eFloatOpaqueBlack, .unnormalizedCoordinates = false, }; - AbortIfFailed(device->m_Device.createSampler(&samplerCreateInfo, nullptr, &m_ImmutableSampler)); - INFO("Max Buffer Count: {}", buffersCount); - INFO("Max Texture Count: {}", texturesCount); - INFO("Max Storage Texture Count: {}", storageTexturesCount); - - m_BufferManager.Init(buffersCount); - m_TextureManager.Init(texturesCount); - m_StorageTextureManager.Init(storageTexturesCount); + m_DefaultSampler = m_SamplerManager.Fetch(m_SamplerManager.Create(device, &m_DefaultSamplerCreateInfo)); eastl::array poolSizes = { vk::DescriptorPoolSize{ @@ -495,7 +573,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize) .pBindingFlags = layoutBindingFlags.data(), }; - eastl::vector immutableSamplers(texturesCount, m_ImmutableSampler); eastl::array descriptorLayoutBindings = { vk::DescriptorSetLayoutBinding{ .binding = BUFFER_BINDING_INDEX, @@ -508,7 +585,6 @@ GpuResourceManager::GpuResourceManager(Device *device, u16 maxSize) .descriptorType = vk::DescriptorType::eCombinedImageSampler, .descriptorCount = Cast(texturesCount), .stageFlags = vk::ShaderStageFlagBits::eAll, - .pImmutableSamplers = immutableSamplers.data(), }, vk::DescriptorSetLayoutBinding{ .binding = STORAGE_TEXTURE_BINDING_INDEX, @@ -552,7 +628,7 @@ GpuResourceManager::~GpuResourceManager() m_BufferManager.Destroy(m_Device); m_TextureManager.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_SetLayout, nullptr); } @@ -561,10 +637,10 @@ GpuResourceManager::GpuResourceManager(GpuResourceManager &&other) noexcept : m_WriteInfos(std::move(other.m_WriteInfos)) , m_Writes(std::move(other.m_Writes)) , m_WriteOwner(std::move(other.m_WriteOwner)) - , m_ImmutableSampler(other.m_ImmutableSampler) , m_BufferManager(std::move(other.m_BufferManager)) , m_TextureManager(std::move(other.m_TextureManager)) , m_StorageTextureManager(std::move(other.m_StorageTextureManager)) + , m_SamplerManager(std::move(other.m_SamplerManager)) , m_Device(Take(other.m_Device)) , m_DescriptorPool(other.m_DescriptorPool) , m_SetLayout(other.m_SetLayout) @@ -586,10 +662,10 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept m_WriteInfos = std::move(other.m_WriteInfos); m_Writes = std::move(other.m_Writes); m_WriteOwner = std::move(other.m_WriteOwner); - m_ImmutableSampler = other.m_ImmutableSampler; m_BufferManager = std::move(other.m_BufferManager); m_TextureManager = std::move(other.m_TextureManager); m_StorageTextureManager = std::move(other.m_StorageTextureManager); + m_SamplerManager = std::move(other.m_SamplerManager); m_Device = Take(other.m_Device); // Ensure taken. m_DescriptorPool = other.m_DescriptorPool; m_SetLayout = other.m_SetLayout; @@ -602,4 +678,10 @@ GpuResourceManager::operator=(GpuResourceManager &&other) noexcept assert(!other.m_Device); return *this; +} + +SamplerHandle +GpuResourceManager::CreateSampler(const vk::SamplerCreateInfo *samplerCreateInfo) +{ + return m_SamplerManager.Create(m_Device, samplerCreateInfo); } \ No newline at end of file diff --git a/samples/00_util/gpu_resource_manager.h b/samples/00_util/gpu_resource_manager.h index 59958d4..7dcc11a 100644 --- a/samples/00_util/gpu_resource_manager.h +++ b/samples/00_util/gpu_resource_manager.h @@ -39,6 +39,10 @@ struct StorageTextureHandle : GpuResourceHandle { }; +struct SamplerHandle : GpuResourceHandle +{ +}; + struct TextureManager { eastl::vector m_Textures; @@ -72,6 +76,18 @@ struct StorageTextureManager : TextureManager void Release(const Device *device, StorageTextureHandle handle); }; +struct SamplerManager +{ + // There can only be so many samplers. + eastl::vector m_Samplers; + eastl::vector 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 { private: @@ -102,11 +118,12 @@ struct GpuResourceManager eastl::vector m_Writes; eastl::vector m_WriteOwner; - vk::Sampler m_ImmutableSampler; + vk::Sampler m_DefaultSampler; BufferManager m_BufferManager; TextureManager m_TextureManager; StorageTextureManager m_StorageTextureManager; + SamplerManager m_SamplerManager; void EraseWrites(u32 handleIndex, HandleType handleType); @@ -117,6 +134,8 @@ struct GpuResourceManager constexpr static u32 TEXTURE_BINDING_INDEX = 1; constexpr static u32 STORAGE_TEXTURE_BINDING_INDEX = 2; + vk::SamplerCreateInfo m_DefaultSamplerCreateInfo; + vk::DescriptorPool m_DescriptorPool; vk::DescriptorSetLayout m_SetLayout; vk::DescriptorSet m_DescriptorSet; @@ -126,13 +145,16 @@ struct GpuResourceManager void Release(BufferHandle handle); // Release and Destroy void Release(StorageBuffer *storageBuffer, BufferHandle handle); // Release and Return - TextureHandle CommitTexture(Texture *texture); // Commit to GPU and take Ownership - void Release(TextureHandle handle); // Release and Destroy - void Release(Texture *texture, TextureHandle handle); // Release and Return + TextureHandle CommitTexture(Texture *texture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership + void Release(TextureHandle handle); // Release and Destroy + void Release(Texture *texture, TextureHandle handle); // Release and Return - StorageTextureHandle CommitStorageTexture(StorageTexture *storageTexture); // Commit to GPU and take Ownership - void Release(StorageTextureHandle handle); // Release and Destroy - void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return + StorageTextureHandle + CommitStorageTexture(StorageTexture *storageTexture, SamplerHandle sampler = {}); // Commit to GPU and take Ownership + 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. diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index 79b3ec9..9a0faa9 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -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/diffuse_irradiance.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 util_helper) diff --git a/samples/03_model_render/asset_loader.cpp b/samples/03_model_render/asset_loader.cpp index d9871ee..97d1547 100644 --- a/samples/03_model_render/asset_loader.cpp +++ b/samples/03_model_render/asset_loader.cpp @@ -272,7 +272,7 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo vk::ImageMemoryBarrier2 imageReadyBarrier = { .srcStageMask = vk::PipelineStageFlagBits2::eTransfer, .srcAccessMask = vk::AccessFlagBits2::eTransferWrite, - .dstStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .dstStageMask = finalStage, .dstAccessMask = vk::AccessFlagBits2::eShaderRead, .oldLayout = vk::ImageLayout::eTransferSrcOptimal, .newLayout = finalLayout, diff --git a/samples/03_model_render/ibl_helpers.cpp b/samples/03_model_render/ibl_helpers.cpp index 5f12405..3e11e01 100644 --- a/samples/03_model_render/ibl_helpers.cpp +++ b/samples/03_model_render/ibl_helpers.cpp @@ -17,25 +17,43 @@ 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 PREFILTER_SHADER_FILE = "shader/prefilter.cs.hlsl.spv"; +constexpr cstr BRDF_LUT_SHADER_FILE = "shader/brdf_lut.cs.hlsl.spv"; -eastl::tuple +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, const cstr name) { GpuResourceManager *resMan = assetLoader->m_ResourceManager; 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 diffuseIrradiance; + StorageTextureCube prefilterCube; + StorageTexture brdfLut; + SamplerHandle brdfLutSampler; + skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Skybox"); TextureHandle skyboxHandle = resMan->CommitTexture(&skybox); StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox); - StorageTextureCube diffuseIrradiance; diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance"); TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance); StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance); - StorageTextureCube prefilterCube; prefilterCube.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, true, "Prefilter"); TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us. constexpr u32 prefilterMipCountMax = 6; @@ -61,6 +79,11 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 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 vk::ImageSubresourceRange cubeSubresRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -69,6 +92,13 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .baseArrayLayer = 0, .layerCount = 6, }; + vk::ImageSubresourceRange lutSubresRange = { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = 0, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 1, + }; vk::ImageMemoryBarrier2 readyToWriteBarrierTemplate = { .srcStageMask = vk::PipelineStageFlagBits2::eTopOfPipe, @@ -81,48 +111,57 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .subresourceRange = cubeSubresRange, }; - eastl::fixed_vector readyToWriteBarriers(2, readyToWriteBarrierTemplate); + eastl::fixed_vector readyToWriteBarriers(4, readyToWriteBarrierTemplate); readyToWriteBarriers[0].image = skybox.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 = { .imageMemoryBarrierCount = Cast(readyToWriteBarriers.size()), .pImageMemoryBarriers = readyToWriteBarriers.data(), }; - vk::ImageMemoryBarrier2 skyboxWriteToReadBarrier = { - .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 = { + vk::ImageMemoryBarrier2 readyToSampleBarrierTemplate = { .srcStageMask = vk::PipelineStageFlagBits2::eComputeShader, .srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead, - .dstStageMask = vk::PipelineStageFlagBits2::eFragmentShader, - .dstAccessMask = vk::AccessFlagBits2::eShaderSampledRead, + .dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe, .oldLayout = vk::ImageLayout::eGeneral, .newLayout = vk::ImageLayout::eShaderReadOnlyOptimal, .srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, .dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED, - .image = skybox.m_Image, .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 = { .imageMemoryBarrierCount = 1, .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 @@ -146,13 +185,17 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 f32 m_Roughness; u32 m_EnvSide; }; + struct BrdfLutPushConstants + { + StorageTextureHandle m_OutputTexture; + }; #pragma region Pipeline Creation etc vk::PushConstantRange pcr = { .stageFlags = vk::ShaderStageFlagBits::eCompute, .offset = 0, - .size = eastl::max(sizeof(SkyboxPushConstants), + .size = eastl::max(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)), 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 diffuseRadianceShader = CreateShader(pDevice, DIFFUSE_IRRADIANCE_SHADER_FILE); const auto prefilterShader = CreateShader(pDevice, PREFILTER_SHADER_FILE); + const auto brdfLutShader = CreateShader(pDevice, BRDF_LUT_SHADER_FILE); eastl::array computePipelineCreateInfo = { vk::ComputePipelineCreateInfo{ .stage = @@ -196,6 +240,15 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 }, .layout = pipelineLayout, }, + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = brdfLutShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, }; eastl::array pipelines; @@ -206,6 +259,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 vk::Pipeline eqRectToCubePipeline = pipelines[0]; vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; vk::Pipeline prefilterPipeline = pipelines[2]; + vk::Pipeline brdfLutPipeline = pipelines[3]; for (auto &createInfos : computePipelineCreateInfo) { @@ -228,6 +282,9 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .m_SkyboxHandle = skyboxHandle, .m_EnvSide = cubeSide, }; + BrdfLutPushConstants brdfLutPushConstants = { + .m_OutputTexture = brdfLutStorageHandle, + }; resMan->Update(); @@ -251,6 +308,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof 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); 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.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, &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.pipelineBarrier2(&diffIrrToSampleDependency); + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline); u32 mipSize = prefilterCube.m_Extent.width; + assert(mipSize % 16 == 0); for (u32 mipCount = 0; auto &tex : prefilterStorageHandles) { prefilterPushConstants.m_OutputTexture = tex; @@ -278,6 +340,13 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 } 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) cmd.endDebugUtilsLabelEXT(); @@ -304,6 +373,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 skybox = {}; resMan->Release(skyboxStorageHandle); resMan->Release(diffuseIrradianceStorageHandle); + resMan->Release(brdfLutStorageHandle); for (auto &texHandles : prefilterStorageHandles) { StorageTextureCube st; @@ -316,5 +386,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 } pDevice->m_Device.destroy(pipelineLayout, nullptr); - return {skyboxHandle, diffuseIrradianceHandle, prefilterHandle}; + return { + .m_Skybox = skyboxHandle, + .m_Diffuse = diffuseIrradianceHandle, + .m_Prefilter = prefilterHandle, + .m_BrdfLut = brdfLutHandle, + }; } \ No newline at end of file diff --git a/samples/03_model_render/ibl_helpers.h b/samples/03_model_render/ibl_helpers.h index ca84b95..f9b0f5e 100644 --- a/samples/03_model_render/ibl_helpers.h +++ b/samples/03_model_render/ibl_helpers.h @@ -15,6 +15,16 @@ struct Texture; struct TextureCube; struct AssetLoader; -eastl::tuple +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, cstr name = nullptr); \ No newline at end of file diff --git a/samples/03_model_render/model_render.cpp b/samples/03_model_render/model_render.cpp index 031fd69..056192a 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -176,13 +176,13 @@ main(int, char **) LightManager lightManager = LightManager{&resourceManager}; Model model = assetLoader.LoadModelToGpu(MODEL_FILE); - Texture environment; - assetLoader.LoadHdrImage(&environment, BACKDROP_FILE); - auto envHandle = resourceManager.CommitTexture(&environment); + Texture environmentHdri; + assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE); + 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; @@ -374,6 +374,8 @@ main(int, char **) bool showDiffuse = false; bool useDiffuse = true; bool showPrefilter = false; + bool useSpecular = true; + i32 height = Cast(internalResolution.height); f32 camPitch = glm::degrees(cameraController.m_Pitch); f32 camYaw = glm::degrees(cameraController.m_Yaw); @@ -453,6 +455,7 @@ main(int, char **) gui::Checkbox("Show DiffIrr", &showDiffuse); gui::Checkbox("Use DiffIrr", &useDiffuse); gui::Checkbox("Show Prefilter", &showPrefilter); + gui::Checkbox("Use Specular", &useSpecular); gui::Separator(); gui::Checkbox("Rotate", &rotating); gui::PopItemWidth(); @@ -556,37 +559,31 @@ main(int, char **) &model.m_Handles); pcbOffset += sizeof model.m_Handles; + Environment thisFrameEnvBuffers = environment; if (showDiffuse) { - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, - &diffuseIrr); - pcbOffset += sizeof diffuseIrr; + thisFrameEnvBuffers.m_Skybox = environment.m_Diffuse; } else if (showPrefilter) { - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof prefilter, - &prefilter); - pcbOffset += sizeof prefilter; - } - else - { - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube); - pcbOffset += sizeof texCube; + thisFrameEnvBuffers.m_Skybox = environment.m_Prefilter; } - if (useDiffuse) + if (!useDiffuse) { - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, - &diffuseIrr); - pcbOffset += sizeof diffuseIrr; + thisFrameEnvBuffers.m_Diffuse = {}; } - else + if (!useSpecular) { - TextureHandle invalid; - cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof invalid, &invalid); - pcbOffset += sizeof invalid; + thisFrameEnvBuffers.m_BrdfLut = {}; + thisFrameEnvBuffers.m_Prefilter = {}; } - 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, &lightManager.m_MetaInfo); pcbOffset += sizeof lightManager.m_MetaInfo; @@ -664,9 +661,7 @@ main(int, char **) AbortIfFailed(device.m_Device.waitIdle()); - resourceManager.Release(texCube); - resourceManager.Release(diffuseIrr); - resourceManager.Release(prefilter); + environment.Destroy(&resourceManager); pipelineCacheData = device.DumpPipelineCache(); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); diff --git a/samples/03_model_render/pipeline_utils.cpp b/samples/03_model_render/pipeline_utils.cpp index d4a409c..df8d019 100644 --- a/samples/03_model_render/pipeline_utils.cpp +++ b/samples/03_model_render/pipeline_utils.cpp @@ -57,7 +57,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const GpuResou vk::PushConstantRange pushConstantRange = { .stageFlags = vk::ShaderStageFlagBits::eAll, .offset = 0, - .size = 44, + .size = 52, }; vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { @@ -206,7 +206,7 @@ CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, cons vk::PushConstantRange pushConstantRange = { .stageFlags = vk::ShaderStageFlagBits::eAll, .offset = 0, - .size = 44, + .size = 52, }; vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = { diff --git a/samples/03_model_render/shader/bindless_structs.hlsli b/samples/03_model_render/shader/bindless_structs.hlsli index 4dab07e..3e9bf13 100644 --- a/samples/03_model_render/shader/bindless_structs.hlsli +++ b/samples/03_model_render/shader/bindless_structs.hlsli @@ -60,8 +60,10 @@ static const float TAU = 6.28318530f; [[vk::binding(0, 0)]] StructuredBuffer DirectionalLightBuffer[]; [[vk::binding(1, 0)]] Texture2D Textures[]; +[[vk::binding(1, 0)]] Texture2D TexturesRG[]; [[vk::binding(1, 0)]] TextureCube TextureCubes[]; [[vk::binding(1, 0)]] SamplerState ImmutableSamplers[]; [[vk::binding(2, 0)]] RWTexture2D StorageTextures[]; +[[vk::binding(2, 0)]] RWTexture2D StorageTexturesRG[]; [[vk::binding(2, 0)]] RWTexture2DArray StorageTextureArrays[]; diff --git a/samples/03_model_render/shader/brdf_lut.cs.hlsl b/samples/03_model_render/shader/brdf_lut.cs.hlsl new file mode 100644 index 0000000..b515ed7 --- /dev/null +++ b/samples/03_model_render/shader/brdf_lut.cs.hlsl @@ -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; +} \ No newline at end of file diff --git a/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl index cff05bd..21f3313 100644 --- a/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl +++ b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl @@ -27,7 +27,7 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID) float3 Forward, Up, Right; 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)); Up = normalize(cross(Forward, Right)); diff --git a/samples/03_model_render/shader/graphics_structs.hlsli b/samples/03_model_render/shader/graphics_structs.hlsli index c305415..b19a3e3 100644 --- a/samples/03_model_render/shader/graphics_structs.hlsli +++ b/samples/03_model_render/shader/graphics_structs.hlsli @@ -15,11 +15,16 @@ struct Block uint VertexDataHandle; uint MaterialBufferHandle; uint NodeBufferHandle; + uint EnvCubeHandle; uint DiffuseIrradianceHandle; + uint PrefilterHandle; + uint BrdfLutHandle; + uint LightHandle; uint PointLightIndexer; uint DirectionalLightIndexer; + int MaterialIdx; uint NodeIdx; }; diff --git a/samples/03_model_render/shader/ibl_common.hlsli b/samples/03_model_render/shader/ibl_common.hlsli index 18e1d56..bec0adc 100644 --- a/samples/03_model_render/shader/ibl_common.hlsli +++ b/samples/03_model_render/shader/ibl_common.hlsli @@ -36,4 +36,42 @@ float3 GetCubeDir(uint3 GlobalInvocationID, float SideLength) // Never reach here. 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); } \ No newline at end of file diff --git a/samples/03_model_render/shader/model.ps.hlsl b/samples/03_model_render/shader/model.ps.hlsl index 17bc179..98c850c 100644 --- a/samples/03_model_render/shader/model.ps.hlsl +++ b/samples/03_model_render/shader/model.ps.hlsl @@ -77,6 +77,26 @@ float3 SampleIrradiance(float3 Direction) 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 Coeff = Roughness * Roughness; @@ -196,7 +216,6 @@ float3 GetPointLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, return Contrib; } - float3 GetDirectionalLightInfluence(float3 Albedo, float2 MetalRough, float3 Position, float3 Normal) { 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 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; 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; - 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; - - return DiffuseIrradiance; + float3 ReflectionDir = reflect(-ViewDir, Normal); + + 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) diff --git a/samples/03_model_render/shader/prefilter.cs.hlsl b/samples/03_model_render/shader/prefilter.cs.hlsl index 7de6d81..738d019 100644 --- a/samples/03_model_render/shader/prefilter.cs.hlsl +++ b/samples/03_model_render/shader/prefilter.cs.hlsl @@ -12,44 +12,6 @@ struct Block [[vk::push_constant]] 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 Coeff = Roughness * Roughness;