Compute Prefilter.
This commit is contained in:
parent
23cd7eae8b
commit
b9d5ba56d4
|
|
@ -130,9 +130,9 @@ struct GpuResourceManager
|
||||||
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); // What they said ^
|
StorageTextureHandle CommitStorageTexture(StorageTexture *storageTexture); // Commit to GPU and take Ownership
|
||||||
void Release(StorageTextureHandle handle); //
|
void Release(StorageTextureHandle handle); // Release and Destroy
|
||||||
void Release(StorageTexture *texture, StorageTextureHandle handle); //
|
void Release(StorageTexture *texture, StorageTextureHandle handle); // Release and Return
|
||||||
|
|
||||||
void Update(); // Update all the descriptors required.
|
void Update(); // Update all the descriptors required.
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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.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)
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
||||||
|
|
@ -12,12 +12,12 @@
|
||||||
|
|
||||||
#include "asset_loader.h"
|
#include "asset_loader.h"
|
||||||
|
|
||||||
|
#include "EASTL/fixed_vector.h"
|
||||||
#include "buffer.h"
|
#include "buffer.h"
|
||||||
#include "device.h"
|
#include "device.h"
|
||||||
#include "gpu_resource_manager.h"
|
#include "gpu_resource_manager.h"
|
||||||
#include "helpers.h"
|
#include "helpers.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
#include "EASTL/fixed_vector.h"
|
|
||||||
|
|
||||||
#include <EASTL/hash_map.h>
|
#include <EASTL/hash_map.h>
|
||||||
#include <glm/gtc/type_ptr.hpp>
|
#include <glm/gtc/type_ptr.hpp>
|
||||||
|
|
@ -191,10 +191,19 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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 = {
|
vk::ImageMemoryBarrier2 imageStartBarrier = {
|
||||||
.srcStageMask = vk::PipelineStageFlagBits2::eAllCommands,
|
.srcStageMask = prevStage,
|
||||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||||
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
.dstStageMask = vk::PipelineStageFlagBits2::eTransfer,
|
||||||
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
.dstAccessMask = vk::AccessFlagBits2::eTransferRead,
|
||||||
|
|
@ -346,6 +355,9 @@ GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayo
|
||||||
}
|
}
|
||||||
|
|
||||||
commandBuffer.pipelineBarrier2(&imageReadyDependency);
|
commandBuffer.pipelineBarrier2(&imageReadyDependency);
|
||||||
|
#if !defined(ASTER_NDEBUG)
|
||||||
|
commandBuffer.endDebugUtilsLabelEXT();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureHandle
|
TextureHandle
|
||||||
|
|
|
||||||
|
|
@ -120,4 +120,6 @@ struct AssetLoader
|
||||||
};
|
};
|
||||||
|
|
||||||
void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
|
void GenerateMipMaps(vk::CommandBuffer commandBuffer, Texture *texture, vk::ImageLayout initialLayout,
|
||||||
vk::ImageLayout finalLayout);
|
vk::ImageLayout finalLayout,
|
||||||
|
vk::PipelineStageFlags2 prevStage = vk::PipelineStageFlagBits2::eAllCommands,
|
||||||
|
vk::PipelineStageFlags2 finalStage = vk::PipelineStageFlagBits2::eAllCommands);
|
||||||
|
|
@ -16,29 +16,51 @@
|
||||||
|
|
||||||
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";
|
||||||
|
|
||||||
eastl::tuple<TextureHandle, TextureHandle>
|
eastl::tuple<TextureHandle, TextureHandle, TextureHandle>
|
||||||
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;
|
||||||
|
|
||||||
StackString<64> skyboxName = "Skybox: ";
|
|
||||||
StackString<64> diffuseIrradianceName = "DiffIrr: ";
|
|
||||||
skyboxName += name ? name : "";
|
|
||||||
diffuseIrradianceName += name ? name : "";
|
|
||||||
|
|
||||||
StorageTextureCube skybox;
|
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);
|
TextureHandle skyboxHandle = resMan->CommitTexture(&skybox);
|
||||||
StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox);
|
StorageTextureHandle skyboxStorageHandle = resMan->CommitStorageTexture(&skybox);
|
||||||
|
|
||||||
StorageTextureCube diffuseIrradiance;
|
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);
|
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");
|
||||||
|
TextureHandle prefilterHandle = resMan->CommitTexture(&prefilterCube); // This stores the original view for us.
|
||||||
|
constexpr u32 prefilterMipCountMax = 6;
|
||||||
|
eastl::array<StorageTextureHandle, prefilterMipCountMax> 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
|
#pragma region Dependencies and Copies
|
||||||
vk::ImageSubresourceRange cubeSubresRange = {
|
vk::ImageSubresourceRange cubeSubresRange = {
|
||||||
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
.aspectMask = vk::ImageAspectFlagBits::eColor,
|
||||||
|
|
@ -80,7 +102,7 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
.image = skybox.m_Image,
|
.image = skybox.m_Image,
|
||||||
.subresourceRange = cubeSubresRange,
|
.subresourceRange = cubeSubresRange,
|
||||||
};
|
};
|
||||||
vk::DependencyInfo skyboxReadToWriteDependency = {
|
vk::DependencyInfo skyboxWriteToReadDependency = {
|
||||||
.imageMemoryBarrierCount = 1,
|
.imageMemoryBarrierCount = 1,
|
||||||
.pImageMemoryBarriers = &skyboxWriteToReadBarrier,
|
.pImageMemoryBarriers = &skyboxWriteToReadBarrier,
|
||||||
};
|
};
|
||||||
|
|
@ -116,13 +138,22 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
StorageTextureHandle m_OutputTexture;
|
StorageTextureHandle m_OutputTexture;
|
||||||
u32 m_CubeSide;
|
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
|
#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), sizeof(DiffuseIrradiancePushConstants)),
|
.size = eastl::max(sizeof(SkyboxPushConstants),
|
||||||
|
eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))),
|
||||||
};
|
};
|
||||||
|
|
||||||
vk::PipelineLayout pipelineLayout;
|
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 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);
|
||||||
eastl::array computePipelineCreateInfo = {
|
eastl::array computePipelineCreateInfo = {
|
||||||
vk::ComputePipelineCreateInfo{
|
vk::ComputePipelineCreateInfo{
|
||||||
.stage =
|
.stage =
|
||||||
|
|
@ -155,16 +187,25 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
},
|
},
|
||||||
.layout = pipelineLayout,
|
.layout = pipelineLayout,
|
||||||
},
|
},
|
||||||
|
vk::ComputePipelineCreateInfo{
|
||||||
|
.stage =
|
||||||
|
{
|
||||||
|
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||||
|
.module = prefilterShader,
|
||||||
|
.pName = "main",
|
||||||
|
},
|
||||||
|
.layout = pipelineLayout,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
eastl::array<vk::Pipeline, 2> pipelines;
|
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> pipelines;
|
||||||
static_assert(pipelines.size() == computePipelineCreateInfo.size());
|
|
||||||
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(),
|
AbortIfFailed(pDevice->m_Device.createComputePipelines(pDevice->m_PipelineCache, computePipelineCreateInfo.size(),
|
||||||
computePipelineCreateInfo.data(), nullptr,
|
computePipelineCreateInfo.data(), nullptr,
|
||||||
pipelines.data()));
|
pipelines.data()));
|
||||||
|
|
||||||
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];
|
||||||
|
|
||||||
for (auto &createInfos : computePipelineCreateInfo)
|
for (auto &createInfos : computePipelineCreateInfo)
|
||||||
{
|
{
|
||||||
|
|
@ -183,6 +224,10 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
.m_OutputTexture = diffuseIrradianceStorageHandle,
|
.m_OutputTexture = diffuseIrradianceStorageHandle,
|
||||||
.m_CubeSide = diffuseIrradiance.m_Extent.width,
|
.m_CubeSide = diffuseIrradiance.m_Extent.width,
|
||||||
};
|
};
|
||||||
|
PrefilterPushConstants prefilterPushConstants = {
|
||||||
|
.m_SkyboxHandle = skyboxHandle,
|
||||||
|
.m_EnvSide = cubeSide,
|
||||||
|
};
|
||||||
|
|
||||||
resMan->Update();
|
resMan->Update();
|
||||||
|
|
||||||
|
|
@ -206,14 +251,31 @@ 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);
|
||||||
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.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);
|
||||||
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<f32>(mipCount) / Cast<f32>(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);
|
cmd.pipelineBarrier2(&skyboxToSampleDependency);
|
||||||
|
|
||||||
|
|
@ -242,11 +304,17 @@ CreateCubeFromHdrEnv(AssetLoader *assetLoader, vk::Queue computeQueue, const u32
|
||||||
skybox = {};
|
skybox = {};
|
||||||
resMan->Release(skyboxStorageHandle);
|
resMan->Release(skyboxStorageHandle);
|
||||||
resMan->Release(diffuseIrradianceStorageHandle);
|
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)
|
for (auto &pipeline : pipelines)
|
||||||
{
|
{
|
||||||
pDevice->m_Device.destroy(pipeline, nullptr);
|
pDevice->m_Device.destroy(pipeline, nullptr);
|
||||||
}
|
}
|
||||||
pDevice->m_Device.destroy(pipelineLayout, nullptr);
|
pDevice->m_Device.destroy(pipelineLayout, nullptr);
|
||||||
|
|
||||||
return {skyboxHandle, diffuseIrradianceHandle};
|
return {skyboxHandle, diffuseIrradianceHandle, prefilterHandle};
|
||||||
}
|
}
|
||||||
|
|
@ -15,6 +15,6 @@ struct Texture;
|
||||||
struct TextureCube;
|
struct TextureCube;
|
||||||
struct AssetLoader;
|
struct AssetLoader;
|
||||||
|
|
||||||
eastl::tuple<TextureHandle, TextureHandle>
|
eastl::tuple<TextureHandle, TextureHandle, TextureHandle>
|
||||||
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);
|
||||||
|
|
@ -180,7 +180,7 @@ main(int, char **)
|
||||||
assetLoader.LoadHdrImage(&environment, BACKDROP_FILE);
|
assetLoader.LoadHdrImage(&environment, BACKDROP_FILE);
|
||||||
auto envHandle = resourceManager.CommitTexture(&environment);
|
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);
|
resourceManager.Release(envHandle);
|
||||||
|
|
||||||
|
|
@ -373,6 +373,7 @@ main(int, char **)
|
||||||
bool lockToScreen = true;
|
bool lockToScreen = true;
|
||||||
bool showDiffuse = false;
|
bool showDiffuse = false;
|
||||||
bool useDiffuse = true;
|
bool useDiffuse = true;
|
||||||
|
bool showPrefilter = false;
|
||||||
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);
|
||||||
|
|
@ -451,6 +452,7 @@ main(int, char **)
|
||||||
gui::Text("IBL");
|
gui::Text("IBL");
|
||||||
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::Separator();
|
gui::Separator();
|
||||||
gui::Checkbox("Rotate", &rotating);
|
gui::Checkbox("Rotate", &rotating);
|
||||||
gui::PopItemWidth();
|
gui::PopItemWidth();
|
||||||
|
|
@ -553,17 +555,25 @@ main(int, char **)
|
||||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles,
|
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof model.m_Handles,
|
||||||
&model.m_Handles);
|
&model.m_Handles);
|
||||||
pcbOffset += sizeof model.m_Handles;
|
pcbOffset += sizeof model.m_Handles;
|
||||||
|
|
||||||
if (showDiffuse)
|
if (showDiffuse)
|
||||||
{
|
{
|
||||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr,
|
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr,
|
||||||
&diffuseIrr);
|
&diffuseIrr);
|
||||||
pcbOffset += sizeof diffuseIrr;
|
pcbOffset += sizeof diffuseIrr;
|
||||||
}
|
}
|
||||||
|
else if (showPrefilter)
|
||||||
|
{
|
||||||
|
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof prefilter,
|
||||||
|
&prefilter);
|
||||||
|
pcbOffset += sizeof prefilter;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube);
|
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof texCube, &texCube);
|
||||||
pcbOffset += sizeof texCube;
|
pcbOffset += sizeof texCube;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (useDiffuse)
|
if (useDiffuse)
|
||||||
{
|
{
|
||||||
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr,
|
cmd.pushConstants(pipeline.m_Layout, vk::ShaderStageFlagBits::eAll, pcbOffset, sizeof diffuseIrr,
|
||||||
|
|
@ -656,6 +666,7 @@ main(int, char **)
|
||||||
|
|
||||||
resourceManager.Release(texCube);
|
resourceManager.Release(texCube);
|
||||||
resourceManager.Release(diffuseIrr);
|
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");
|
||||||
|
|
|
||||||
|
|
@ -26,14 +26,14 @@ void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
float3 Forward, Up, Right;
|
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
|
Up = float3(0.0f, 1.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));
|
||||||
|
|
||||||
float3 Irradiance = 0.0f.xxx;
|
float3 Irradiance = 0.0f.xxx;
|
||||||
float3 IrrDirr = 0.0f.xxx;
|
float3 IrrDirr = 0.0f.xxx;
|
||||||
float SampleStep = 0.025f;
|
float SampleStep = 0.005f;
|
||||||
float SampleCount = 0.0f;
|
float SampleCount = 0.0f;
|
||||||
|
|
||||||
for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep)
|
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 DirectionTanSpace = float3(sin(Zenith) * cos(Azimuth), sin(Zenith) * sin(Azimuth), cos(Zenith));
|
||||||
float3 DirectionWorld = DirectionTanSpace.x * Right + DirectionTanSpace.y * Up + DirectionTanSpace.z * Forward;
|
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));
|
Irradiance += TextureCubes[pcb.SkyboxHandle].SampleLevel(ImmutableSamplers[pcb.SkyboxHandle], DirectionWorld, 0).xyz * (cos(Zenith) * sin(Zenith));
|
||||||
SampleCount++;
|
SampleCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f);
|
StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = PI * float4(Irradiance * (1.0f / SampleCount), 1.0f);
|
||||||
}
|
}
|
||||||
|
|
@ -19,21 +19,10 @@ float2 SampleSphericalMap(float3 v)
|
||||||
return UV;
|
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)]
|
[numthreads(16, 16, 1)]
|
||||||
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
|
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
|
||||||
{
|
{
|
||||||
float3 LocalDir = GetLocalDir(pcb.CubeSide, GlobalInvocationID);
|
float3 LocalDir = GetCubeDir(GlobalInvocationID, pcb.CubeSide);
|
||||||
|
|
||||||
float2 UV = SampleSphericalMap(LocalDir);
|
float2 UV = SampleSphericalMap(LocalDir);
|
||||||
StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0);
|
StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0);
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,39 @@
|
||||||
#include "bindless_structs.hlsli"
|
#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;
|
float2 FaceUV = float2(GlobalInvocationID.xy) / SideLength; // (0, SideLength) -> (0, 1)
|
||||||
float AxisSign = 2.0f * (0.5f - (GlobalInvocationID.z % 2));
|
FaceUV = 2.0f * FaceUV - 1.0f; // (0, 1) -> (-1, 1)
|
||||||
|
|
||||||
float3 LocalDir;
|
switch (GlobalInvocationID.z)
|
||||||
if (GlobalInvocationID.z < 2)
|
|
||||||
{
|
{
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue