diff --git a/samples/00_util/gpu_resource_manager.h b/samples/00_util/gpu_resource_manager.h index 01a2e96..59958d4 100644 --- a/samples/00_util/gpu_resource_manager.h +++ b/samples/00_util/gpu_resource_manager.h @@ -130,9 +130,9 @@ struct GpuResourceManager void Release(TextureHandle handle); // Release and Destroy void Release(Texture *texture, TextureHandle handle); // Release and Return - StorageTextureHandle CommitStorageTexture(StorageTexture *storageTexture); // What they said ^ - void Release(StorageTextureHandle handle); // - void Release(StorageTexture *texture, StorageTextureHandle handle); // + 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 void Update(); // Update all the descriptors required. diff --git a/samples/03_model_render/CMakeLists.txt b/samples/03_model_render/CMakeLists.txt index d7a93b4..79b3ec9 100644 --- a/samples/03_model_render/CMakeLists.txt +++ b/samples/03_model_render/CMakeLists.txt @@ -23,6 +23,7 @@ add_shader(model_render shader/eqrect_to_cube.cs.hlsl) 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) 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 a494fb6..d9871ee 100644 --- a/samples/03_model_render/asset_loader.cpp +++ b/samples/03_model_render/asset_loader.cpp @@ -12,12 +12,12 @@ #include "asset_loader.h" +#include "EASTL/fixed_vector.h" #include "buffer.h" #include "device.h" #include "gpu_resource_manager.h" #include "helpers.h" #include "image.h" -#include "EASTL/fixed_vector.h" #include #include @@ -191,10 +191,19 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const } void -GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout, vk::ImageLayout finalLayout) +GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout, + vk::ImageLayout finalLayout, vk::PipelineStageFlags2 prevStage, vk::PipelineStageFlags2 finalStage) { +#if !defined(ASTER_NDEBUG) + vk::DebugUtilsLabelEXT label = { + .pLabelName = "Generate Mipmap", + .color = std::array{0.9f, 0.9f, 0.9f, 1.0f}, + }; + commandBuffer.beginDebugUtilsLabelEXT(&label); +#endif + vk::ImageMemoryBarrier2 imageStartBarrier = { - .srcStageMask = vk::PipelineStageFlagBits2::eAllCommands, + .srcStageMask = prevStage, .srcAccessMask = vk::AccessFlagBits2::eNone, .dstStageMask = vk::PipelineStageFlagBits2::eTransfer, .dstAccessMask = vk::AccessFlagBits2::eTransferRead, @@ -346,6 +355,9 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo } commandBuffer.pipelineBarrier2(&imageReadyDependency); +#if !defined(ASTER_NDEBUG) + commandBuffer.endDebugUtilsLabelEXT(); +#endif } TextureHandle diff --git a/samples/03_model_render/asset_loader.h b/samples/03_model_render/asset_loader.h index 8e0f0ff..6c7c936 100644 --- a/samples/03_model_render/asset_loader.h +++ b/samples/03_model_render/asset_loader.h @@ -120,4 +120,6 @@ struct AssetLoader }; void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout, - vk::ImageLayout finalLayout); \ No newline at end of file + vk::ImageLayout finalLayout, + vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands, + vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands); \ No newline at end of file diff --git a/samples/03_model_render/ibl_helpers.cpp b/samples/03_model_render/ibl_helpers.cpp index db58cdc..5f12405 100644 --- a/samples/03_model_render/ibl_helpers.cpp +++ b/samples/03_model_render/ibl_helpers.cpp @@ -16,29 +16,51 @@ 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"; -eastl::tuple +eastl::tuple 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; - StackString<64> skyboxName = "Skybox: "; - StackString<64> diffuseIrradianceName = "DiffIrr: "; - skyboxName += name ? name : ""; - diffuseIrradianceName += name ? name : ""; - StorageTextureCube skybox; - skybox.Init(pDevice, cubeSide, vk::Format::eR16G16B16A16Sfloat, true, false, skyboxName.c_str()); + 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, diffuseIrradianceName.c_str()); + 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; + eastl::array prefilterStorageHandles; + // All non-owning copies. + for (u32 mipLevel = 0; auto &tex : prefilterStorageHandles) + { + vk::ImageViewCreateInfo imageViewCreateInfo = { + .image = prefilterCube.m_Image, + .viewType = vk::ImageViewType::eCube, + .format = vk::Format::eR16G16B16A16Sfloat, + .components = vk::ComponentMapping{}, + .subresourceRange = + { + .aspectMask = vk::ImageAspectFlagBits::eColor, + .baseMipLevel = mipLevel++, + .levelCount = 1, + .baseArrayLayer = 0, + .layerCount = 6, + }, + }; + AbortIfFailed(pDevice->m_Device.createImageView(&imageViewCreateInfo, nullptr, &prefilterCube.m_View)); + tex = resMan->CommitStorageTexture(&prefilterCube); + } + #pragma region Dependencies and Copies vk::ImageSubresourceRange cubeSubresRange = { .aspectMask = vk::ImageAspectFlagBits::eColor, @@ -80,7 +102,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .image = skybox.m_Image, .subresourceRange = cubeSubresRange, }; - vk::DependencyInfo skyboxReadToWriteDependency = { + vk::DependencyInfo skyboxWriteToReadDependency = { .imageMemoryBarrierCount = 1, .pImageMemoryBarriers = &skyboxWriteToReadBarrier, }; @@ -116,13 +138,22 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 StorageTextureHandle m_OutputTexture; u32 m_CubeSide; }; + struct PrefilterPushConstants + { + TextureHandle m_SkyboxHandle; + StorageTextureHandle m_OutputTexture; + u32 m_CubeSide; + f32 m_Roughness; + u32 m_EnvSide; + }; #pragma region Pipeline Creation etc vk::PushConstantRange pcr = { .stageFlags = vk::ShaderStageFlagBits::eCompute, .offset = 0, - .size = eastl::max(sizeof(SkyboxPushConstants), sizeof(DiffuseIrradiancePushConstants)), + .size = eastl::max(sizeof(SkyboxPushConstants), + eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))), }; vk::PipelineLayout pipelineLayout; @@ -136,6 +167,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); eastl::array computePipelineCreateInfo = { vk::ComputePipelineCreateInfo{ .stage = @@ -155,16 +187,25 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 }, .layout = pipelineLayout, }, + vk::ComputePipelineCreateInfo{ + .stage = + { + .stage = vk::ShaderStageFlagBits::eCompute, + .module = prefilterShader, + .pName = "main", + }, + .layout = pipelineLayout, + }, }; - eastl::array pipelines; - static_assert(pipelines.size() == computePipelineCreateInfo.size()); + eastl::array pipelines; AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(), computePipelineCreateInfo.data(), nullptr, pipelines.data())); vk::Pipeline eqRectToCubePipeline = pipelines[0]; vk::Pipeline diffuseIrradiancePipeline = pipelines[1]; + vk::Pipeline prefilterPipeline = pipelines[2]; for (auto &createInfos : computePipelineCreateInfo) { @@ -183,6 +224,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 .m_OutputTexture = diffuseIrradianceStorageHandle, .m_CubeSide = diffuseIrradiance.m_Extent.width, }; + PrefilterPushConstants prefilterPushConstants = { + .m_SkyboxHandle = skyboxHandle, + .m_EnvSide = cubeSide, + }; resMan->Update(); @@ -206,14 +251,31 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cmd.bindPipeline(vk::PipelineBindPoint::eCompute, eqRectToCubePipeline); cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, &skyboxPushConstant); - cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); + cmd.dispatch(skybox.m_Extent.width / 16, skybox.m_Extent.height / 16, 6); - cmd.pipelineBarrier2(&skyboxReadToWriteDependency); + GenerateMipMaps(cmd, &skybox, vk::ImageLayout::eGeneral, vk::ImageLayout::eGeneral, + vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader); cmd.bindPipeline(vk::PipelineBindPoint::eCompute, diffuseIrradiancePipeline); cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof skyboxPushConstant, &diffuseIrradiancePushConstants); - cmd.dispatch(cubeSide / 16, cubeSide / 16, 6); + cmd.dispatch(diffuseIrradiance.m_Extent.width / 16, diffuseIrradiance.m_Extent.width / 16, 6); + + cmd.bindPipeline(vk::PipelineBindPoint::eCompute, prefilterPipeline); + u32 mipSize = prefilterCube.m_Extent.width; + for (u32 mipCount = 0; auto &tex : prefilterStorageHandles) + { + prefilterPushConstants.m_OutputTexture = tex; + prefilterPushConstants.m_CubeSide = mipSize; + prefilterPushConstants.m_Roughness = Cast(mipCount) / Cast(prefilterMipCountMax); + cmd.pushConstants(pipelineLayout, vk::ShaderStageFlagBits::eCompute, 0, sizeof prefilterPushConstants, + &prefilterPushConstants); + u32 groupCount = eastl::max(mipSize / 16u, 1u); + cmd.dispatch(groupCount, groupCount, 6); + + ++mipCount; + mipSize = mipSize >> 1; + } cmd.pipelineBarrier2(&skyboxToSampleDependency); @@ -242,11 +304,17 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 skybox = {}; resMan->Release(skyboxStorageHandle); resMan->Release(diffuseIrradianceStorageHandle); + for (auto &texHandles : prefilterStorageHandles) + { + StorageTextureCube st; + resMan->Release(&st, texHandles); + pDevice->m_Device.destroy(st.m_View, nullptr); + } for (auto &pipeline : pipelines) { pDevice->m_Device.destroy(pipeline, nullptr); } pDevice->m_Device.destroy(pipelineLayout, nullptr); - return {skyboxHandle, diffuseIrradianceHandle}; + return {skyboxHandle, diffuseIrradianceHandle, prefilterHandle}; } \ 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 2c742b7..ca84b95 100644 --- a/samples/03_model_render/ibl_helpers.h +++ b/samples/03_model_render/ibl_helpers.h @@ -15,6 +15,6 @@ struct Texture; struct TextureCube; struct AssetLoader; -eastl::tuple +eastl::tuple 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 0bc7ba8..031fd69 100644 --- a/samples/03_model_render/model_render.cpp +++ b/samples/03_model_render/model_render.cpp @@ -180,7 +180,7 @@ main(int, char **) assetLoader.LoadHdrImage(&environment, BACKDROP_FILE); auto envHandle = resourceManager.CommitTexture(&environment); - auto [texCube, diffuseIrr] = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHandle, "Cube Env"); + auto [texCube, diffuseIrr, prefilter] = CreateCubeFromHdrEnv(&assetLoader, graphicsQueue, 512, envHandle, "Cube Env"); resourceManager.Release(envHandle); @@ -373,6 +373,7 @@ main(int, char **) bool lockToScreen = true; bool showDiffuse = false; bool useDiffuse = true; + bool showPrefilter = false; i32 height = Cast(internalResolution.height); f32 camPitch = glm::degrees(cameraController.m_Pitch); f32 camYaw = glm::degrees(cameraController.m_Yaw); @@ -451,6 +452,7 @@ main(int, char **) gui::Text("IBL"); gui::Checkbox("Show DiffIrr", &showDiffuse); gui::Checkbox("Use DiffIrr", &useDiffuse); + gui::Checkbox("Show Prefilter", &showPrefilter); gui::Separator(); gui::Checkbox("Rotate", &rotating); gui::PopItemWidth(); @@ -553,17 +555,25 @@ main(int, char **) cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles, &model.m_Handles); pcbOffset += sizeof model.m_Handles; + if (showDiffuse) { cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, &diffuseIrr); pcbOffset += sizeof diffuseIrr; } + 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; } + if (useDiffuse) { cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr, @@ -656,6 +666,7 @@ main(int, char **) resourceManager.Release(texCube); resourceManager.Release(diffuseIrr); + resourceManager.Release(prefilter); pipelineCacheData = device.DumpPipelineCache(); ERROR_IF(!WriteFileBytes(PIPELINE_CACHE_FILE, pipelineCacheData), "Pipeline Cache incorrectly written"); diff --git a/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl index 0023bec..cff05bd 100644 --- a/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl +++ b/samples/03_model_render/shader/diffuse_irradiance.cs.hlsl @@ -26,14 +26,14 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID) { float3 Forward, Up, Right; - Forward = GetLocalDir(pcb.CubeSide, GlobalInvocationID); + Forward = GetCubeDir(GlobalInvocationID, pcb.CubeSide); Up = float3(0.0f, 1.0f, 0.0f); // 0.01f offset to Right = normalize(cross(Up, Forward)); Up = normalize(cross(Forward, Right)); float3 Irradiance = 0.0f.xxx; float3 IrrDirr = 0.0f.xxx; - float SampleStep = 0.025f; + float SampleStep = 0.005f; float SampleCount = 0.0f; for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep) @@ -42,10 +42,11 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID) { float3 DirectionTanSpace = float3(sin(Zenith) * cos(Azimuth), sin(Zenith) * sin(Azimuth), cos(Zenith)); float3 DirectionWorld = DirectionTanSpace.x * Right + DirectionTanSpace.y * Up + DirectionTanSpace.z * Forward; + Irradiance += TextureCubes[pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[pcb.SkyboxHandle], DirectionWorld, 0).xyz * (cos(Zenith) * sin(Zenith)); SampleCount++; } } - + StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f); } \ No newline at end of file diff --git a/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl b/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl index 580e0f6..d80a0e3 100644 --- a/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl +++ b/samples/03_model_render/shader/eqrect_to_cube.cs.hlsl @@ -19,21 +19,10 @@ float2 SampleSphericalMap(float3 v) return UV; } -/* -| Axis | Layer | Up | -|:----:|:-----:|:--:| -| +x | 0 | -y | -| -x | 1 | -y | -| +y | 2 | +z | -| -y | 3 | -z | -| -z | 4 | -y | -| +z | 5 | -y | -*/ - [numthreads(16, 16, 1)] void main(uint3 GlobalInvocationID : SV_DispatchThreadID) { - float3 LocalDir = GetLocalDir(pcb.CubeSide, GlobalInvocationID); + float3 LocalDir = GetCubeDir(GlobalInvocationID, pcb.CubeSide); float2 UV = SampleSphericalMap(LocalDir); StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0); diff --git a/samples/03_model_render/shader/ibl_common.hlsli b/samples/03_model_render/shader/ibl_common.hlsli index 2ad3436..18e1d56 100644 --- a/samples/03_model_render/shader/ibl_common.hlsli +++ b/samples/03_model_render/shader/ibl_common.hlsli @@ -1,22 +1,39 @@ #include "bindless_structs.hlsli" -float3 GetLocalDir(float Side, float3 GlobalInvocationID) +/* + The Goal is simply to convert from a (0,0,0) to (CubeSide, CubeSide, FaceCount) Invocation ID space to a + (-1,-1,-1) to (1, 1, 1) space. + +| Axis | Layer | Up | +|:----:|:-----:|:--:| +| +x | 0 | -y | +| -x | 1 | -y | +| +y | 2 | +z | +| -y | 3 | -z | +| -z | 4 | -y | +| +z | 5 | -y | +*/ +float3 GetCubeDir(uint3 GlobalInvocationID, float SideLength) { - float HalfSide = Side / 2; - float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2)); + float2 FaceUV = float2(GlobalInvocationID.xy) / SideLength; // (0, SideLength) -> (0, 1) + FaceUV = 2.0f * FaceUV - 1.0f; // (0, 1) -> (-1, 1) - float3 LocalDir; - if (GlobalInvocationID.z < 2) + switch (GlobalInvocationID.z) { - LocalDir = float3(AxisSign * HalfSide, HalfSide - GlobalInvocationID.y, (HalfSide - GlobalInvocationID.x) * AxisSign); + case 0: + return normalize(float3(1.0f, -FaceUV.y, -FaceUV.x)); // Face +X; x = 1, y = -v, z = -u + case 1: + return normalize(float3(-1.0f, -FaceUV.y, FaceUV.x)); // Face -X; x = -1, y = -v, z = u + case 2: + return normalize(float3(FaceUV.x, 1.0f, FaceUV.y)); // Face +Y; x = u, y = 1, z = v + case 3: + return normalize(float3(FaceUV.x, -1.0f, -FaceUV.y)); // Face -Y; x=u, y=-1, z=-v + case 4: + return normalize(float3(FaceUV.x, -FaceUV.y, 1.0f)); // Face +Z; x=u,y=-v, z=1 + case 5: + return normalize(float3(-FaceUV.x, -FaceUV.y, -1.0f)); // Face -Z; x=u,y=-v, z=-1 + default: + // Never reach here. + return 0.0f.xxx; } - else if (GlobalInvocationID.z < 4) - { - LocalDir = float3(GlobalInvocationID.x - HalfSide, AxisSign * HalfSide, AxisSign * (GlobalInvocationID.y - HalfSide)); - } - else - { - LocalDir = float3((GlobalInvocationID.x - HalfSide) * AxisSign, HalfSide - GlobalInvocationID.y, HalfSide * AxisSign); - } - return normalize(LocalDir); } \ No newline at end of file diff --git a/samples/03_model_render/shader/prefilter.cs.hlsl b/samples/03_model_render/shader/prefilter.cs.hlsl new file mode 100644 index 0000000..7de6d81 --- /dev/null +++ b/samples/03_model_render/shader/prefilter.cs.hlsl @@ -0,0 +1,106 @@ +#include "ibl_common.hlsli" + +struct Block +{ + uint SkyboxHandle; + uint OutputTextureHandle; + int CubeSide; + float Roughness; + uint EnvRes; +}; + +[[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; + float Alpha2 = Coeff * Coeff; + float NdotH2 = NdotH * NdotH; + + float Numerator = Alpha2; + float Denominator = NdotH2 * (Alpha2 - 1.0f) + 1.0f; + Denominator = PI * Denominator * Denominator; + + return Numerator / Denominator; +} + +float GetSampleMipLevel(float NdotH, float HdotV, float SampleCount) +{ + float D = TrowbridgeReitzGGX(NdotH, Pcb.Roughness); + float Pdf = (D * NdotH / (4.0f * HdotV)) + 0.0001f; + + float SurfAreaTexel = 4.0f * PI / (6.0f * Pcb.EnvRes * Pcb.EnvRes); + float SurfAreaSample = 1.0f / (SampleCount * Pdf + 0.0001f); + + return Pcb.Roughness == 0.0f ? 0.0f : 0.5f * log2(SurfAreaSample / SurfAreaTexel); +} + +[numthreads(16, 16, 1)] +void main(uint3 GlobalInvocationID : SV_DispatchThreadID) +{ + float3 Normal = GetCubeDir(GlobalInvocationID, Pcb.CubeSide); + float3 ViewDir = Normal; + + const uint SAMPLE_COUNT = 2048u; + float TotalWeight = 0.0f; + float3 PrefilterColor = 0.0f.xxx; + for (uint i = 0u; i < SAMPLE_COUNT; ++i) + { + float2 Xi = Hammersley(i, SAMPLE_COUNT); + float3 Halfway = ImportanceSampleGGX(Xi, Normal, Pcb.Roughness); + float3 LightDir = normalize(2.0 * dot(ViewDir, Halfway) * Halfway - ViewDir); + + float NdotH = max(dot(Normal, Halfway), 0.0f); + + float MipLevel = GetSampleMipLevel(NdotH, NdotH /* N = V :: NdotH = HdotV */, SAMPLE_COUNT); + + float NdotL = max(dot(Normal, LightDir), 0.0); + if (NdotL > 0.0) + { + PrefilterColor += TextureCubes[Pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[Pcb.SkyboxHandle], LightDir, MipLevel).rgb * NdotL; + TotalWeight += NdotL; + } + } + PrefilterColor = PrefilterColor / TotalWeight; + + StorageTextureArrays[Pcb.OutputTextureHandle][GlobalInvocationID] = float4(PrefilterColor, 1.0f); +} \ No newline at end of file