[WIP] IBL etc integrated.
This commit is contained in:
parent
978ed648fd
commit
8f6dc4677d
|
|
@ -13,11 +13,18 @@ add_executable(scene_render main.cpp
|
|||
pipeline_utils.cpp pipeline_utils.h
|
||||
core_components.h
|
||||
ecs_adapter.h
|
||||
camera.h)
|
||||
camera.h
|
||||
ibl_helpers.cpp ibl_helpers.h
|
||||
light_manager.cpp light_manager.h)
|
||||
|
||||
add_shader(scene_render shader/model.frag.glsl)
|
||||
add_shader(scene_render shader/model.vert.glsl)
|
||||
# add_shader(scene_render shader/model.vs.hlsl)
|
||||
add_shader(scene_render shader/eqrect_to_cube.cs.hlsl)
|
||||
add_shader(scene_render shader/diffuse_irradiance.cs.hlsl)
|
||||
add_shader(scene_render shader/prefilter.cs.hlsl)
|
||||
add_shader(scene_render shader/brdf_lut.cs.hlsl)
|
||||
add_shader(scene_render shader/background.vert.glsl)
|
||||
add_shader(scene_render shader/background.frag.glsl)
|
||||
|
||||
target_link_libraries(scene_render PRIVATE aster_core)
|
||||
target_link_libraries(scene_render PRIVATE util_helper)
|
||||
|
|
|
|||
|
|
@ -99,9 +99,8 @@ AssetLoader::LoadHdrImage(Texture *texture, cstr path, cstr name) const
|
|||
|
||||
i32 x, y, nChannels;
|
||||
f32 *data = stbi_loadf(path, &x, &y, &nChannels, 4);
|
||||
assert(nChannels == 3);
|
||||
|
||||
ERROR_IF(!data, "Could not load {}", path) THEN_ABORT(-1);
|
||||
assert(nChannels == 3);
|
||||
|
||||
u32 width = Cast<u32>(x);
|
||||
u32 height = Cast<u32>(y);
|
||||
|
|
|
|||
|
|
@ -35,8 +35,6 @@ struct Material
|
|||
TextureHandle m_MetalRoughTex; // 04 48
|
||||
TextureHandle m_OcclusionTex; // 04 52
|
||||
TextureHandle m_EmissionTex; // 04 56
|
||||
|
||||
static constexpr usize ALIGNMENT = 4;
|
||||
};
|
||||
|
||||
static_assert(sizeof(Material) == 56);
|
||||
|
|
@ -47,8 +45,6 @@ struct VertexData
|
|||
vec2 m_TexCoord0 = vec2{0.0f, 0.0f};
|
||||
vec2 m_TexCoord1 = vec2{0.0f, 0.0f};
|
||||
vec4 m_Color0 = vec4{1.0f, 1.0f, 1.0f, 1.0f};
|
||||
|
||||
static constexpr usize ALIGNMENT = 16;
|
||||
};
|
||||
|
||||
struct Model
|
||||
|
|
|
|||
|
|
@ -0,0 +1,395 @@
|
|||
// =============================================
|
||||
// Aster: ibl_helpers.cpp
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#include "ibl_helpers.h"
|
||||
|
||||
#include "EASTL/fixed_vector.h"
|
||||
#include "EASTL/tuple.h"
|
||||
#include "asset_loader.h"
|
||||
#include "device.h"
|
||||
#include "render_resource_manager.h"
|
||||
#include "helpers.h"
|
||||
#include "image.h"
|
||||
#include "pipeline_utils.h"
|
||||
|
||||
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";
|
||||
|
||||
void
|
||||
Environment::Destroy(RenderResourceManager *resourceManager)
|
||||
{
|
||||
resourceManager->Release(Take(m_Skybox));
|
||||
resourceManager->Release(Take(m_Diffuse));
|
||||
resourceManager->Release(Take(m_Prefilter));
|
||||
resourceManager->Release(Take(m_BrdfLut));
|
||||
}
|
||||
|
||||
Environment
|
||||
CreateEnvironment(AssetLoader *assetLoader, vk::Queue computeQueue, const u32 cubeSide, TextureHandle hdrEnv,
|
||||
const cstr name)
|
||||
{
|
||||
RenderResourceManager *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);
|
||||
|
||||
diffuseIrradiance.Init(pDevice, 64, vk::Format::eR16G16B16A16Sfloat, true, false, "Diffuse Irradiance");
|
||||
TextureHandle diffuseIrradianceHandle = resMan->CommitTexture(&diffuseIrradiance);
|
||||
StorageTextureHandle diffuseIrradianceStorageHandle = resMan->CommitStorageTexture(&diffuseIrradiance);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.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,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eNone,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.dstAccessMask = vk::AccessFlagBits2::eShaderStorageWrite,
|
||||
.oldLayout = vk::ImageLayout::eUndefined,
|
||||
.newLayout = vk::ImageLayout::eGeneral,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.subresourceRange = cubeSubresRange,
|
||||
};
|
||||
eastl::fixed_vector<vk::ImageMemoryBarrier2, 4> 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<u32>(readyToWriteBarriers.size()),
|
||||
.pImageMemoryBarriers = readyToWriteBarriers.data(),
|
||||
};
|
||||
|
||||
vk::ImageMemoryBarrier2 readyToSampleBarrierTemplate = {
|
||||
.srcStageMask = vk::PipelineStageFlagBits2::eComputeShader,
|
||||
.srcAccessMask = vk::AccessFlagBits2::eShaderStorageWrite | vk::AccessFlagBits2::eShaderStorageRead,
|
||||
.dstStageMask = vk::PipelineStageFlagBits2::eBottomOfPipe,
|
||||
.oldLayout = vk::ImageLayout::eGeneral,
|
||||
.newLayout = vk::ImageLayout::eShaderReadOnlyOptimal,
|
||||
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
|
||||
.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
|
||||
|
||||
struct SkyboxPushConstants
|
||||
{
|
||||
TextureHandle m_HdrEnvHandle;
|
||||
StorageTextureHandle m_OutputTexture;
|
||||
u32 m_CubeSide;
|
||||
};
|
||||
struct DiffuseIrradiancePushConstants
|
||||
{
|
||||
TextureHandle m_SkyboxHandle;
|
||||
StorageTextureHandle m_OutputTexture;
|
||||
u32 m_CubeSide;
|
||||
};
|
||||
struct PrefilterPushConstants
|
||||
{
|
||||
TextureHandle m_SkyboxHandle;
|
||||
StorageTextureHandle m_OutputTexture;
|
||||
u32 m_CubeSide;
|
||||
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(eastl::max(sizeof(SkyboxPushConstants), sizeof(BrdfLutPushConstants)),
|
||||
eastl::max(sizeof(DiffuseIrradiancePushConstants), sizeof(PrefilterPushConstants))),
|
||||
};
|
||||
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
const vk::PipelineLayoutCreateInfo layoutCreateInfo = {
|
||||
.setLayoutCount = 1,
|
||||
.pSetLayouts = &resMan->m_SetLayout,
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &pcr,
|
||||
};
|
||||
AbortIfFailed(pDevice->m_Device.createPipelineLayout(&layoutCreateInfo, nullptr, &pipelineLayout));
|
||||
|
||||
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 =
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = eqRectToCubeShader,
|
||||
.pName = "main",
|
||||
},
|
||||
.layout = pipelineLayout,
|
||||
},
|
||||
vk::ComputePipelineCreateInfo{
|
||||
.stage =
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = diffuseRadianceShader,
|
||||
.pName = "main",
|
||||
},
|
||||
.layout = pipelineLayout,
|
||||
},
|
||||
vk::ComputePipelineCreateInfo{
|
||||
.stage =
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = prefilterShader,
|
||||
.pName = "main",
|
||||
},
|
||||
.layout = pipelineLayout,
|
||||
},
|
||||
vk::ComputePipelineCreateInfo{
|
||||
.stage =
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eCompute,
|
||||
.module = brdfLutShader,
|
||||
.pName = "main",
|
||||
},
|
||||
.layout = pipelineLayout,
|
||||
},
|
||||
};
|
||||
|
||||
eastl::array<vk::Pipeline, computePipelineCreateInfo.size()> 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];
|
||||
vk::Pipeline brdfLutPipeline = pipelines[3];
|
||||
|
||||
for (auto &createInfos : computePipelineCreateInfo)
|
||||
{
|
||||
pDevice->m_Device.destroy(createInfos.stage.module, nullptr);
|
||||
}
|
||||
|
||||
#pragma endregion
|
||||
|
||||
SkyboxPushConstants skyboxPushConstant = {
|
||||
.m_HdrEnvHandle = hdrEnv,
|
||||
.m_OutputTexture = skyboxStorageHandle,
|
||||
.m_CubeSide = cubeSide,
|
||||
};
|
||||
DiffuseIrradiancePushConstants diffuseIrradiancePushConstants = {
|
||||
.m_SkyboxHandle = skyboxHandle,
|
||||
.m_OutputTexture = diffuseIrradianceStorageHandle,
|
||||
.m_CubeSide = diffuseIrradiance.m_Extent.width,
|
||||
};
|
||||
PrefilterPushConstants prefilterPushConstants = {
|
||||
.m_SkyboxHandle = skyboxHandle,
|
||||
.m_EnvSide = cubeSide,
|
||||
};
|
||||
BrdfLutPushConstants brdfLutPushConstants = {
|
||||
.m_OutputTexture = brdfLutStorageHandle,
|
||||
};
|
||||
|
||||
resMan->Update();
|
||||
|
||||
auto cmd = assetLoader->m_CommandBuffer;
|
||||
constexpr vk::CommandBufferBeginInfo beginInfo = {.flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit};
|
||||
AbortIfFailed(cmd.begin(&beginInfo));
|
||||
|
||||
#if !defined(ASTER_NDEBUG)
|
||||
StackString<128> labelName = "Eqrect -> Cubemap: ";
|
||||
labelName += name ? name : "<unknown env>";
|
||||
vk::DebugUtilsLabelEXT label = {
|
||||
.pLabelName = labelName.c_str(),
|
||||
.color = std::array{1.0f, 1.0f, 1.0f, 1.0f},
|
||||
};
|
||||
cmd.beginDebugUtilsLabelEXT(&label);
|
||||
#endif
|
||||
|
||||
cmd.pipelineBarrier2(&readyToWriteDependency);
|
||||
|
||||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eCompute, pipelineLayout, 0, 1, &resMan->m_DescriptorSet, 0, nullptr);
|
||||
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,
|
||||
vk::PipelineStageFlagBits2::eComputeShader, vk::PipelineStageFlagBits2::eComputeShader);
|
||||
|
||||
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;
|
||||
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(&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();
|
||||
#endif
|
||||
|
||||
AbortIfFailed(cmd.end());
|
||||
|
||||
vk::SubmitInfo submitInfo = {
|
||||
.waitSemaphoreCount = 0,
|
||||
.pWaitDstStageMask = nullptr,
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &cmd,
|
||||
};
|
||||
|
||||
vk::Fence fence;
|
||||
vk::FenceCreateInfo fenceCreateInfo = {};
|
||||
AbortIfFailed(pDevice->m_Device.createFence(&fenceCreateInfo, nullptr, &fence));
|
||||
AbortIfFailed(computeQueue.submit(1, &submitInfo, fence));
|
||||
AbortIfFailed(pDevice->m_Device.waitForFences(1, &fence, true, MaxValue<u32>));
|
||||
pDevice->m_Device.destroy(fence, nullptr);
|
||||
|
||||
AbortIfFailed(pDevice->m_Device.resetCommandPool(assetLoader->m_CommandPool, {}));
|
||||
|
||||
skybox = {};
|
||||
resMan->Release(skyboxStorageHandle);
|
||||
resMan->Release(diffuseIrradianceStorageHandle);
|
||||
resMan->Release(brdfLutStorageHandle);
|
||||
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 {
|
||||
.m_Skybox = skyboxHandle,
|
||||
.m_Diffuse = diffuseIrradianceHandle,
|
||||
.m_Prefilter = prefilterHandle,
|
||||
.m_BrdfLut = brdfLutHandle,
|
||||
};
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
// =============================================
|
||||
// Aster: ibl_helpers.h
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "global.h"
|
||||
#include "render_resource_manager.h"
|
||||
|
||||
struct Pipeline;
|
||||
struct Texture;
|
||||
struct TextureCube;
|
||||
struct AssetLoader;
|
||||
|
||||
struct Environment
|
||||
{
|
||||
TextureHandle m_Skybox;
|
||||
TextureHandle m_Diffuse;
|
||||
TextureHandle m_Prefilter;
|
||||
TextureHandle m_BrdfLut;
|
||||
|
||||
void Destroy(RenderResourceManager *resourceManager);
|
||||
};
|
||||
|
||||
Environment
|
||||
CreateEnvironment(AssetLoader *assetLoader, vk::Queue computeQueue, u32 cubeSide, TextureHandle hdrEnv,
|
||||
cstr name = nullptr);
|
||||
Binary file not shown.
|
|
@ -0,0 +1,303 @@
|
|||
// =============================================
|
||||
// Aster: light_manager.cpp
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#include "light_manager.h"
|
||||
|
||||
#include "buffer.h"
|
||||
#include "ibl_helpers.h"
|
||||
#include "glm/ext/matrix_transform.hpp"
|
||||
|
||||
struct Light
|
||||
{
|
||||
union {
|
||||
vec3 um_Position;
|
||||
vec3 um_Direction;
|
||||
};
|
||||
f32 m_Range; // < 0.0 for invalid
|
||||
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
||||
f32 m_Intensity;
|
||||
|
||||
constexpr static u32 MAX_GEN = 0x40;
|
||||
constexpr static u32 GEN_MASK = MAX_GEN - 1;
|
||||
|
||||
constexpr static u32 TYPE_MASK = 0xC0;
|
||||
constexpr static u32 TYPE_INVALID = 0x0;
|
||||
constexpr static u32 TYPE_DIRECTIONAL = 1 << 6;
|
||||
constexpr static u32 TYPE_POINT = 2 << 6;
|
||||
constexpr static u32 TYPE_SPOT = 3 << 6; // Currently Unused
|
||||
|
||||
constexpr static u32 COLOR_MASK = ~(GEN_MASK | TYPE_MASK);
|
||||
};
|
||||
|
||||
// Static Checks
|
||||
|
||||
// Ensure layouts are exact.
|
||||
static_assert(offsetof(DirectionalLight, m_Direction) == offsetof(Light, um_Direction));
|
||||
static_assert(offsetof(DirectionalLight, m_Color_) == offsetof(Light, m_Color_));
|
||||
static_assert(offsetof(DirectionalLight, m_Intensity) == offsetof(Light, m_Intensity));
|
||||
static_assert(sizeof(DirectionalLight) <= sizeof(Light));
|
||||
|
||||
// Ensure layouts are exact.
|
||||
static_assert(offsetof(PointLight, m_Position) == offsetof(Light, um_Position));
|
||||
static_assert(offsetof(PointLight, m_Range) == offsetof(Light, m_Range));
|
||||
static_assert(offsetof(PointLight, m_Color_) == offsetof(Light, m_Color_));
|
||||
static_assert(offsetof(PointLight, m_Intensity) == offsetof(Light, m_Intensity));
|
||||
static_assert(sizeof(PointLight) <= sizeof(Light));
|
||||
|
||||
// Ensure bitmask are in the right place.
|
||||
static_assert((Light::TYPE_MASK & Light::TYPE_INVALID) == Light::TYPE_INVALID);
|
||||
static_assert((Light::TYPE_MASK & Light::TYPE_DIRECTIONAL) == Light::TYPE_DIRECTIONAL);
|
||||
static_assert((Light::TYPE_MASK & Light::TYPE_POINT) == Light::TYPE_POINT);
|
||||
static_assert((Light::TYPE_MASK & Light::TYPE_SPOT) == Light::TYPE_SPOT);
|
||||
static_assert(Light::COLOR_MASK == 0xFFFFFF00);
|
||||
|
||||
inline u32
|
||||
ToColor32(const vec4 &col)
|
||||
{
|
||||
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
||||
const u32 a = Cast<u32>(eastl::min(col.a, 1.0f) * 255.99f);
|
||||
|
||||
return r << 24 | g << 16 | b << 8 | a;
|
||||
}
|
||||
|
||||
inline u32
|
||||
ToColor32(const vec3 &col)
|
||||
{
|
||||
const u32 r = Cast<u32>(eastl::min(col.r, 1.0f) * 255.99f);
|
||||
const u32 g = Cast<u32>(eastl::min(col.g, 1.0f) * 255.99f);
|
||||
const u32 b = Cast<u32>(eastl::min(col.b, 1.0f) * 255.99f);
|
||||
constexpr u32 a = 255;
|
||||
|
||||
return r << 24 | g << 16 | b << 8 | a;
|
||||
}
|
||||
|
||||
LightManager::LightManager(RenderResourceManager *resourceManager)
|
||||
: m_ResourceManager{resourceManager}
|
||||
, m_DirectionalLightCount{}
|
||||
, m_PointLightCount{}
|
||||
, m_MetaInfo{}
|
||||
, m_GpuBufferCapacity_{0}
|
||||
{
|
||||
}
|
||||
|
||||
LightManager::~LightManager()
|
||||
{
|
||||
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Skybox);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Diffuse);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Prefilter);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_BrdfLut);
|
||||
}
|
||||
|
||||
LightManager::LightManager(LightManager &&other) noexcept
|
||||
: m_ResourceManager(other.m_ResourceManager)
|
||||
, m_Lights(std::move(other.m_Lights))
|
||||
, m_DirectionalLightCount(other.m_DirectionalLightCount)
|
||||
, m_PointLightCount(other.m_PointLightCount)
|
||||
, m_MetaInfo(other.m_MetaInfo)
|
||||
, m_GpuBufferCapacity_(other.m_GpuBufferCapacity_)
|
||||
{
|
||||
other.m_MetaInfo.m_LightBuffer = {};
|
||||
}
|
||||
|
||||
LightManager &
|
||||
LightManager::operator=(LightManager &&other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
m_ResourceManager = other.m_ResourceManager;
|
||||
m_Lights = std::move(other.m_Lights);
|
||||
m_DirectionalLightCount = other.m_DirectionalLightCount;
|
||||
m_PointLightCount = other.m_PointLightCount;
|
||||
m_MetaInfo = other.m_MetaInfo;
|
||||
other.m_MetaInfo.m_LightBuffer = {};
|
||||
m_GpuBufferCapacity_ = other.m_GpuBufferCapacity_;
|
||||
return *this;
|
||||
}
|
||||
|
||||
LightHandle
|
||||
LightManager::AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity)
|
||||
{
|
||||
const vec3 normDirection = normalize(direction);
|
||||
if (m_DirectionalLightCount < m_MetaInfo.m_DirectionalLightMaxCount)
|
||||
{
|
||||
u16 index = 0;
|
||||
for (auto &light : m_Lights)
|
||||
{
|
||||
if (light.m_Range < 0)
|
||||
{
|
||||
const u8 gen = light.m_Color_ & Light::GEN_MASK;
|
||||
|
||||
light.m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
||||
light.m_Range = 1.0f;
|
||||
light.um_Direction = normDirection;
|
||||
light.m_Intensity = intensity;
|
||||
|
||||
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
||||
|
||||
return {Light::TYPE_DIRECTIONAL, gen, index};
|
||||
}
|
||||
++index;
|
||||
assert(index < m_MetaInfo.m_DirectionalLightMaxCount); // Gap not found. But must exist
|
||||
}
|
||||
}
|
||||
|
||||
// In case we will end up intersecting, we move point lights away. (2 at a time)
|
||||
if (m_DirectionalLightCount == m_MetaInfo.m_DirectionalLightMaxCount &&
|
||||
m_MetaInfo.m_DirectionalLightMaxCount == m_MetaInfo.m_PointLightOffset)
|
||||
{
|
||||
const u16 oldPointLightOffset = m_MetaInfo.m_PointLightOffset;
|
||||
const u32 pointLightMaxCount = m_MetaInfo.m_PointLightMaxCount;
|
||||
// Might cause a capacity increase, but I want to use that for my gpu buffer resize.
|
||||
m_Lights.push_back();
|
||||
m_Lights.push_back();
|
||||
|
||||
if (m_MetaInfo.m_PointLightMaxCount > 0) // Edge Case: nullptr at size 0
|
||||
{
|
||||
Light *oldPointStart = m_Lights.data() + oldPointLightOffset;
|
||||
Light *oldPointEnd = oldPointStart + pointLightMaxCount;
|
||||
Light *newPointEnd = oldPointEnd + 2;
|
||||
|
||||
static_assert(std::is_trivially_copyable_v<Light>);
|
||||
|
||||
// Overlaps since 0 -> 2, 1 -> 3, 2 -> 4 (old 2 is lost)
|
||||
// Backward copy fixes this.
|
||||
std::copy_backward(oldPointStart, oldPointEnd, newPointEnd);
|
||||
}
|
||||
|
||||
m_MetaInfo.m_PointLightOffset += 2;
|
||||
}
|
||||
|
||||
constexpr u8 gen = 0; // New light
|
||||
m_Lights[m_DirectionalLightCount].m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_DIRECTIONAL | gen;
|
||||
m_Lights[m_DirectionalLightCount].m_Range = 1.0f;
|
||||
m_Lights[m_DirectionalLightCount].um_Direction = normDirection;
|
||||
m_Lights[m_DirectionalLightCount].m_Intensity = intensity;
|
||||
const u16 index = m_DirectionalLightCount;
|
||||
|
||||
++m_DirectionalLightCount;
|
||||
++m_MetaInfo.m_DirectionalLightMaxCount;
|
||||
|
||||
return {Light::TYPE_DIRECTIONAL, gen, index};
|
||||
}
|
||||
|
||||
LightHandle
|
||||
LightManager::AddPoint(const vec3 &position, const vec3 &color, const f32 radius, f32 intensity)
|
||||
{
|
||||
assert(m_PointLightCount <= m_MetaInfo.m_PointLightMaxCount);
|
||||
assert(radius >= 0.0f);
|
||||
if (m_PointLightCount < m_MetaInfo.m_PointLightMaxCount)
|
||||
{
|
||||
Light *light = m_Lights.data() + m_MetaInfo.m_PointLightOffset;
|
||||
for (u32 index = 0; index < m_MetaInfo.m_PointLightMaxCount; ++index)
|
||||
{
|
||||
if (light->m_Range < 0)
|
||||
{
|
||||
const u8 gen = light->m_Color_ & Light::GEN_MASK;
|
||||
|
||||
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
||||
light->m_Range = radius;
|
||||
light->um_Position = position;
|
||||
light->m_Intensity = intensity;
|
||||
|
||||
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
||||
|
||||
return {Light::TYPE_POINT, gen, Cast<u16>(index)};
|
||||
}
|
||||
++light;
|
||||
}
|
||||
assert(false); // gap must exists.
|
||||
return {};
|
||||
}
|
||||
|
||||
m_Lights.push_back();
|
||||
const u16 index = m_PointLightCount;
|
||||
|
||||
Light *light = &m_Lights[index + m_MetaInfo.m_PointLightOffset];
|
||||
constexpr u8 gen = 0; // New light
|
||||
|
||||
light->m_Color_ = (ToColor32(color) & Light::COLOR_MASK) | Light::TYPE_POINT | gen;
|
||||
light->m_Range = radius;
|
||||
light->um_Position = position;
|
||||
light->m_Intensity = intensity;
|
||||
|
||||
++m_PointLightCount;
|
||||
++m_MetaInfo.m_PointLightMaxCount;
|
||||
|
||||
m_GpuBufferCapacity_ |= UPDATE_REQUIRED_BIT;
|
||||
|
||||
return {Light::TYPE_POINT, gen, index};
|
||||
}
|
||||
|
||||
void
|
||||
LightManager::Update()
|
||||
{
|
||||
const u16 requiredBufferCapacity = eastl::min(Cast<u16>(m_Lights.capacity()), MAX_LIGHTS);
|
||||
if ((m_GpuBufferCapacity_ & CAPACITY_MASK) < requiredBufferCapacity)
|
||||
{
|
||||
StorageBuffer newBuffer;
|
||||
newBuffer.Init(m_ResourceManager->m_Device, requiredBufferCapacity * sizeof m_Lights[0], true, "Light Buffer");
|
||||
m_GpuBufferCapacity_ = requiredBufferCapacity | UPDATE_REQUIRED_BIT;
|
||||
|
||||
m_ResourceManager->Release(m_MetaInfo.m_LightBuffer);
|
||||
m_MetaInfo.m_LightBuffer = m_ResourceManager->Commit(&newBuffer);
|
||||
}
|
||||
if (m_GpuBufferCapacity_ & UPDATE_REQUIRED_BIT)
|
||||
{
|
||||
m_ResourceManager->Write(m_MetaInfo.m_LightBuffer, 0, m_Lights.size() * sizeof m_Lights[0], m_Lights.data());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LightManager::RemoveLight(const LightHandle handle)
|
||||
{
|
||||
const u8 handleGen = handle.m_Generation;
|
||||
|
||||
if (handle.m_Type == Light::TYPE_DIRECTIONAL)
|
||||
{
|
||||
Light *lightSlot = &m_Lights[handle.m_Index];
|
||||
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
||||
if (slotGen > handleGen)
|
||||
{
|
||||
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
||||
return;
|
||||
}
|
||||
|
||||
lightSlot->m_Range = -1.0f;
|
||||
lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN;
|
||||
--m_DirectionalLightCount;
|
||||
}
|
||||
|
||||
if (handle.m_Type == Light::TYPE_POINT)
|
||||
{
|
||||
Light *lightSlot = &m_Lights[handle.m_Index + m_MetaInfo.m_PointLightOffset];
|
||||
const u8 slotGen = lightSlot->m_Color_ & Light::GEN_MASK;
|
||||
if (slotGen > handleGen)
|
||||
{
|
||||
WARN("Invalid handle gen: {} being freed. (slot gen: {})", handleGen, slotGen);
|
||||
return;
|
||||
}
|
||||
|
||||
lightSlot->m_Range = -1.0f;
|
||||
lightSlot->m_Color_ = Light::TYPE_INVALID | (slotGen + 1) % Light::MAX_GEN;
|
||||
--m_PointLightCount;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
LightManager::SetEnvironment(Environment &&environment)
|
||||
{
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Skybox);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Diffuse);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_Prefilter);
|
||||
m_ResourceManager->Release(m_MetaInfo.m_BrdfLut);
|
||||
|
||||
m_MetaInfo.m_Skybox = environment.m_Skybox;
|
||||
m_MetaInfo.m_Diffuse = environment.m_Diffuse;
|
||||
m_MetaInfo.m_Prefilter = environment.m_Prefilter;
|
||||
m_MetaInfo.m_BrdfLut = environment.m_BrdfLut;
|
||||
}
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// =============================================
|
||||
// Aster: light_manager.h
|
||||
// Copyright (c) 2020-2024 Anish Bhobe
|
||||
// =============================================
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "global.h"
|
||||
|
||||
// TODO: Separate files so you only import handles.
|
||||
#include "render_resource_manager.h"
|
||||
|
||||
struct Environment;
|
||||
|
||||
struct DirectionalLight
|
||||
{
|
||||
vec3 m_Direction;
|
||||
u32 m_UnusedPadding0_;
|
||||
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
||||
f32 m_Intensity;
|
||||
};
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
vec3 m_Position;
|
||||
f32 m_Range;
|
||||
u32 m_Color_; // LSB is used for flags. (R G B Flags)
|
||||
f32 m_Intensity;
|
||||
};
|
||||
|
||||
struct LightHandle
|
||||
{
|
||||
u8 m_Type;
|
||||
u8 m_Generation;
|
||||
u16 m_Index;
|
||||
};
|
||||
|
||||
struct Light;
|
||||
|
||||
struct LightManager
|
||||
{
|
||||
constexpr static u16 MAX_LIGHTS = MaxValue<u16>;
|
||||
struct LightMetaInfo
|
||||
{
|
||||
TextureHandle m_Skybox; // 04 04
|
||||
TextureHandle m_Diffuse; // 04 08
|
||||
TextureHandle m_Prefilter; // 04 12
|
||||
TextureHandle m_BrdfLut; // 04 16
|
||||
|
||||
// The number of directional lights is relatively low (1 - 2) and will almost never change in a scene.
|
||||
// We can use that with Offset = 0, and point light at further offsets.
|
||||
// This way we don't need to move point lights often.
|
||||
BufferHandle m_LightBuffer; // 04 20
|
||||
u16 m_PointLightMaxCount; // 02 22
|
||||
u16 m_PointLightOffset; // 02 24
|
||||
u16 m_DirectionalLightMaxCount; // 02 26
|
||||
u16 m_UnusedPadding0 = 0; // 02 28
|
||||
};
|
||||
|
||||
RenderResourceManager *m_ResourceManager;
|
||||
eastl::vector<Light> m_Lights;
|
||||
|
||||
// We don't need a Directional Light free list. We will just brute force iterate.
|
||||
u16 m_DirectionalLightCount;
|
||||
// TODO: A point light free list. We will brute force until we have a lot (100+) of point lights.
|
||||
u16 m_PointLightCount;
|
||||
|
||||
LightMetaInfo m_MetaInfo;
|
||||
// Using lower bit for flags. Use CAPACITY_MASK for value.
|
||||
u16 m_GpuBufferCapacity_;
|
||||
|
||||
// Using lower bit. Capacity can be directly a multiple of 2
|
||||
// Thus, range is up to MaxValue<u16>
|
||||
constexpr static u16 UPDATE_REQUIRED_BIT = 1;
|
||||
constexpr static u16 CAPACITY_MASK = ~(UPDATE_REQUIRED_BIT);
|
||||
|
||||
LightHandle AddDirectional(const vec3 &direction, const vec3 &color, f32 intensity);
|
||||
LightHandle AddPoint(const vec3 &position, const vec3 &color, f32 radius, f32 intensity);
|
||||
void Update();
|
||||
void RemoveLight(LightHandle handle);
|
||||
|
||||
void SetEnvironment(Environment &&environment);
|
||||
|
||||
explicit LightManager(RenderResourceManager *resourceManager);
|
||||
~LightManager();
|
||||
|
||||
LightManager(LightManager &&other) noexcept;
|
||||
LightManager &operator=(LightManager &&other) noexcept;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(LightManager);
|
||||
};
|
||||
|
|
@ -11,12 +11,14 @@
|
|||
#include "swapchain.h"
|
||||
#include "window.h"
|
||||
|
||||
#include "light_manager.h"
|
||||
#include "asset_loader.h"
|
||||
#include "camera.h"
|
||||
#include "core_components.h"
|
||||
|
||||
#include "ecs_adapter.h"
|
||||
#include "frame.h"
|
||||
#include "ibl_helpers.h"
|
||||
#include "image.h"
|
||||
#include "pipeline.h"
|
||||
|
||||
|
|
@ -93,16 +95,30 @@ main(int, char *[])
|
|||
|
||||
AssetLoader assetLoader = {&resourceManager, ®istry, graphicsQueue, queueAllocation.m_Family,
|
||||
queueAllocation.m_Family};
|
||||
LightManager lightManager = LightManager{&resourceManager};
|
||||
|
||||
Texture environmentHdri;
|
||||
assetLoader.LoadHdrImage(&environmentHdri, BACKDROP_FILE);
|
||||
auto envHdriHandle = resourceManager.CommitTexture(&environmentHdri);
|
||||
|
||||
auto environment = CreateEnvironment(&assetLoader, graphicsQueue, 512, envHdriHandle, "Cube Env");
|
||||
|
||||
resourceManager.Release(envHdriHandle);
|
||||
|
||||
lightManager.SetEnvironment(std::move(environment));
|
||||
|
||||
Model model = assetLoader.LoadModelToGpu(MODEL_FILE, "Main Model");
|
||||
Model model2 = assetLoader.LoadModelToGpu(MODEL_FILE2, "Main Model 2");
|
||||
registry.get<CDynamicTransform>(model2.m_RootEntity).m_Position.x += 1.0f;
|
||||
|
||||
UniformBuffer ubo;
|
||||
ubo.Init(&device, sizeof cameraController.m_Camera, "Desc1 UBO");
|
||||
constexpr usize uboTotalSize = sizeof cameraController.m_Camera + sizeof lightManager.m_MetaInfo;
|
||||
ubo.Init(&device, uboTotalSize, "Desc1 UBO");
|
||||
ubo.Write(&device, 0, sizeof cameraController.m_Camera, &cameraController.m_Camera);
|
||||
ubo.Write(&device, sizeof cameraController.m_Camera, sizeof lightManager.m_MetaInfo, &lightManager.m_MetaInfo);
|
||||
|
||||
Pipeline pipeline = CreatePipeline(&device, attachmentFormat, &resourceManager);
|
||||
Pipeline backgroundPipeline = CreateBackgroundPipeline(&device, attachmentFormat, &resourceManager);
|
||||
|
||||
vk::DescriptorPool descriptorPool;
|
||||
vk::DescriptorSet perFrameDescriptor;
|
||||
|
|
@ -127,10 +143,10 @@ main(int, char *[])
|
|||
AbortIfFailed(device.m_Device.allocateDescriptorSets(&descriptorSetAllocateInfo, &perFrameDescriptor));
|
||||
}
|
||||
|
||||
vk::DescriptorBufferInfo cameraBufferInfo = {
|
||||
vk::DescriptorBufferInfo camLightBufferInfo = {
|
||||
.buffer = ubo.m_Buffer,
|
||||
.offset = 0,
|
||||
.range = sizeof(Camera),
|
||||
.range = uboTotalSize,
|
||||
};
|
||||
eastl::array writeDescriptors = {
|
||||
vk::WriteDescriptorSet{
|
||||
|
|
@ -139,7 +155,7 @@ main(int, char *[])
|
|||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.pBufferInfo = &cameraBufferInfo,
|
||||
.pBufferInfo = &camLightBufferInfo,
|
||||
},
|
||||
};
|
||||
device.m_Device.updateDescriptorSets(Cast<u32>(writeDescriptors.size()), writeDescriptors.data(), 0, nullptr);
|
||||
|
|
@ -438,7 +454,6 @@ main(int, char *[])
|
|||
cmd.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, pipeline.m_Layout, 1, 1, &perFrameDescriptor, 0,
|
||||
nullptr);
|
||||
|
||||
//TODO("Unify index buffers");
|
||||
cmd.bindIndexBuffer(resourceManager.GetIndexBuffer(), 0, vk::IndexType::eUint32);
|
||||
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, pipeline.m_Pipeline);
|
||||
|
|
@ -452,6 +467,10 @@ main(int, char *[])
|
|||
cmd.drawIndexed(node.m_IndexCount, 1, node.m_FirstIndex, 0, 0);
|
||||
}
|
||||
|
||||
cmd.bindPipeline(vk::PipelineBindPoint::eGraphics, backgroundPipeline.m_Pipeline);
|
||||
|
||||
cmd.draw(3, 1, 0, 0);
|
||||
|
||||
cmd.endRendering();
|
||||
|
||||
cmd.pipelineBarrier2(&postRenderDependencies);
|
||||
|
|
|
|||
|
|
@ -43,18 +43,6 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe
|
|||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 1,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
},
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 2,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
},
|
||||
};
|
||||
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
||||
.bindingCount = Cast<u32>(descriptorSetLayoutBindings.size()),
|
||||
|
|
@ -67,7 +55,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe
|
|||
}
|
||||
|
||||
vk::PushConstantRange pushConstantRange = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.offset = 0,
|
||||
.size = 96,
|
||||
};
|
||||
|
|
@ -80,7 +68,7 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe
|
|||
};
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||
device->SetName(pipelineLayout, "Box Layout");
|
||||
device->SetName(pipelineLayout, "Scene Layout");
|
||||
|
||||
descriptorSetLayouts[0] = nullptr; // Not owned.
|
||||
|
||||
|
|
@ -172,6 +160,157 @@ CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderRe
|
|||
return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)};
|
||||
}
|
||||
|
||||
|
||||
|
||||
Pipeline
|
||||
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager)
|
||||
{
|
||||
// Pipeline Setup
|
||||
auto vertexShaderModule = CreateShader(device, BACKGROUND_VERTEX_SHADER_FILE);
|
||||
auto fragmentShaderModule = CreateShader(device, BACKGROUND_FRAGMENT_SHADER_FILE);
|
||||
|
||||
eastl::array<vk::PipelineShaderStageCreateInfo, 2> shaderStages = {{
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eVertex,
|
||||
.module = vertexShaderModule,
|
||||
.pName = "main",
|
||||
},
|
||||
{
|
||||
.stage = vk::ShaderStageFlagBits::eFragment,
|
||||
.module = fragmentShaderModule,
|
||||
.pName = "main",
|
||||
},
|
||||
}};
|
||||
|
||||
eastl::vector<vk::DescriptorSetLayout> descriptorSetLayouts;
|
||||
|
||||
descriptorSetLayouts.push_back(resourceManager->m_SetLayout);
|
||||
|
||||
{
|
||||
eastl::array descriptorSetLayoutBindings = {
|
||||
vk::DescriptorSetLayoutBinding{
|
||||
.binding = 0,
|
||||
.descriptorType = vk::DescriptorType::eUniformBuffer,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
},
|
||||
};
|
||||
vk::DescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo = {
|
||||
.bindingCount = Cast<u32>(descriptorSetLayoutBindings.size()),
|
||||
.pBindings = descriptorSetLayoutBindings.data(),
|
||||
};
|
||||
vk::DescriptorSetLayout descriptorSetLayout;
|
||||
AbortIfFailed(
|
||||
device->m_Device.createDescriptorSetLayout(&descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayout));
|
||||
descriptorSetLayouts.push_back(descriptorSetLayout);
|
||||
}
|
||||
|
||||
vk::PushConstantRange pushConstantRange = {
|
||||
.stageFlags = vk::ShaderStageFlagBits::eAll,
|
||||
.offset = 0,
|
||||
.size = 96,
|
||||
};
|
||||
|
||||
vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo = {
|
||||
.setLayoutCount = Cast<u32>(descriptorSetLayouts.size()),
|
||||
.pSetLayouts = descriptorSetLayouts.data(),
|
||||
.pushConstantRangeCount = 1,
|
||||
.pPushConstantRanges = &pushConstantRange,
|
||||
};
|
||||
vk::PipelineLayout pipelineLayout;
|
||||
AbortIfFailed(device->m_Device.createPipelineLayout(&pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
|
||||
device->SetName(pipelineLayout, "Scene BG Layout");
|
||||
|
||||
descriptorSetLayouts[0] = nullptr; // Not owned.
|
||||
|
||||
vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo = {};
|
||||
vk::PipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = {
|
||||
.topology = vk::PrimitiveTopology::eTriangleList,
|
||||
.primitiveRestartEnable = false,
|
||||
};
|
||||
|
||||
vk::PipelineViewportStateCreateInfo viewportStateCreateInfo = {
|
||||
.viewportCount = 1,
|
||||
.scissorCount = 1,
|
||||
};
|
||||
|
||||
vk::PipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = {
|
||||
.depthClampEnable = false,
|
||||
.rasterizerDiscardEnable = false,
|
||||
.polygonMode = vk::PolygonMode::eFill,
|
||||
.cullMode = vk::CullModeFlagBits::eBack,
|
||||
.frontFace = vk::FrontFace::eCounterClockwise,
|
||||
.depthBiasEnable = false,
|
||||
.lineWidth = 1.0,
|
||||
};
|
||||
vk::PipelineMultisampleStateCreateInfo multisampleStateCreateInfo = {
|
||||
.rasterizationSamples = vk::SampleCountFlagBits::e1,
|
||||
.sampleShadingEnable = false,
|
||||
};
|
||||
vk::PipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = {
|
||||
.depthTestEnable = true,
|
||||
.depthWriteEnable = true,
|
||||
.depthCompareOp = vk::CompareOp::eLessOrEqual,
|
||||
};
|
||||
vk::PipelineColorBlendAttachmentState colorBlendAttachmentState = {
|
||||
.blendEnable = false,
|
||||
.srcColorBlendFactor = vk::BlendFactor::eSrcColor,
|
||||
.dstColorBlendFactor = vk::BlendFactor::eOneMinusSrcColor,
|
||||
.colorBlendOp = vk::BlendOp::eAdd,
|
||||
.srcAlphaBlendFactor = vk::BlendFactor::eSrcAlpha,
|
||||
.dstAlphaBlendFactor = vk::BlendFactor::eOneMinusSrcAlpha,
|
||||
.alphaBlendOp = vk::BlendOp::eAdd,
|
||||
.colorWriteMask = vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG |
|
||||
vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA,
|
||||
};
|
||||
vk::PipelineColorBlendStateCreateInfo colorBlendStateCreateInfo = {
|
||||
.logicOpEnable = false,
|
||||
.attachmentCount = 1,
|
||||
.pAttachments = &colorBlendAttachmentState,
|
||||
};
|
||||
|
||||
eastl::array dynamicStates = {
|
||||
vk::DynamicState::eScissor,
|
||||
vk::DynamicState::eViewport,
|
||||
};
|
||||
|
||||
vk::PipelineDynamicStateCreateInfo dynamicStateCreateInfo = {
|
||||
.dynamicStateCount = Cast<u32>(dynamicStates.size()),
|
||||
.pDynamicStates = dynamicStates.data(),
|
||||
};
|
||||
|
||||
vk::PipelineRenderingCreateInfo renderingCreateInfo = {
|
||||
.viewMask = 0,
|
||||
.colorAttachmentCount = 1,
|
||||
.pColorAttachmentFormats = &attachmentFormat,
|
||||
.depthAttachmentFormat = vk::Format::eD24UnormS8Uint,
|
||||
};
|
||||
|
||||
vk::GraphicsPipelineCreateInfo pipelineCreateInfo = {
|
||||
.pNext = &renderingCreateInfo,
|
||||
.stageCount = Cast<u32>(shaderStages.size()),
|
||||
.pStages = shaderStages.data(),
|
||||
.pVertexInputState = &vertexInputStateCreateInfo,
|
||||
.pInputAssemblyState = &inputAssemblyStateCreateInfo,
|
||||
.pViewportState = &viewportStateCreateInfo,
|
||||
.pRasterizationState = &rasterizationStateCreateInfo,
|
||||
.pMultisampleState = &multisampleStateCreateInfo,
|
||||
.pDepthStencilState = &depthStencilStateCreateInfo,
|
||||
.pColorBlendState = &colorBlendStateCreateInfo,
|
||||
.pDynamicState = &dynamicStateCreateInfo,
|
||||
.layout = pipelineLayout,
|
||||
};
|
||||
vk::Pipeline pipeline;
|
||||
AbortIfFailed(
|
||||
device->m_Device.createGraphicsPipelines(device->m_PipelineCache, 1, &pipelineCreateInfo, nullptr, &pipeline));
|
||||
device->SetName(pipeline, "BG Pipeline");
|
||||
|
||||
device->m_Device.destroy(vertexShaderModule, nullptr);
|
||||
device->m_Device.destroy(fragmentShaderModule, nullptr);
|
||||
|
||||
return {device, pipelineLayout, pipeline, std::move(descriptorSetLayouts)};
|
||||
}
|
||||
|
||||
vk::ShaderModule
|
||||
CreateShader(const Device *device, cstr shaderFile)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -14,7 +14,12 @@ struct Device;
|
|||
|
||||
constexpr auto VERTEX_SHADER_FILE = "shader/model.vert.glsl.spv";
|
||||
constexpr auto FRAGMENT_SHADER_FILE = "shader/model.frag.glsl.spv";
|
||||
constexpr auto BACKGROUND_VERTEX_SHADER_FILE = "shader/background.vert.glsl.spv";
|
||||
constexpr auto BACKGROUND_FRAGMENT_SHADER_FILE = "shader/background.frag.glsl.spv";
|
||||
|
||||
vk::ShaderModule CreateShader(const Device *device, cstr shaderFile);
|
||||
Pipeline
|
||||
CreatePipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager);
|
||||
|
||||
Pipeline
|
||||
CreateBackgroundPipeline(const Device *device, vk::Format attachmentFormat, const RenderResourceManager *resourceManager);
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
#version 450
|
||||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#include "bindless_structs.glsl"
|
||||
#include "graphics_bindings.glsl"
|
||||
|
||||
layout (location=0) in vec3 in_WorldPosition;
|
||||
|
||||
layout (location=0) out vec4 out_Color;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 direction = normalize(in_WorldPosition - camera.m_Position.xyz);
|
||||
#ifndef _DEBUG
|
||||
vec4 color = texture(textureCubes[lights.m_EnvCubeHandle], direction);
|
||||
#else
|
||||
vec4 color;
|
||||
// if ((PushConstant.DebugFlags & SHOW_DIFFUSE_BIT) > 0)
|
||||
// {
|
||||
// Color = TextureCubes[Lights.DiffuseIrradianceHandle].Sample(ImmutableSamplers[Lights.DiffuseIrradianceHandle], Direction);
|
||||
// }
|
||||
// else if ((PushConstant.DebugFlags & SHOW_PREFILTER_BIT) > 0)
|
||||
// {
|
||||
// Color = TextureCubes[Lights.PrefilterHandle].Sample(ImmutableSamplers[Lights.PrefilterHandle], Direction);
|
||||
// }
|
||||
// else
|
||||
{
|
||||
color = texture(textureCubes[lights.m_EnvCubeHandle], direction);
|
||||
}
|
||||
#endif
|
||||
out_Color = vec4(Uncharted2Tonemap(color.rgb), color.a);
|
||||
}
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
#version 450
|
||||
#pragma shader_stage(vertex)
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
#include "graphics_bindings.glsl"
|
||||
|
||||
layout (location = 0) out vec3 out_WorldPosition;
|
||||
|
||||
void main()
|
||||
{
|
||||
vec3 points[] =
|
||||
{
|
||||
vec3(-1.0f, -1.0f, 1.0f),
|
||||
vec3(3.0f, -1.0f, 1.0f),
|
||||
vec3(-1.0f, 3.0f, 1.0f),
|
||||
};
|
||||
|
||||
gl_Position = vec4(points[gl_VertexIndex], 1.0f);
|
||||
|
||||
vec4 clipSpace = camera.m_InvProjection * vec4(points[gl_VertexIndex], 1.0f);
|
||||
vec4 worldSpace = camera.m_InvView * (clipSpace / clipSpace.wwww);
|
||||
out_WorldPosition = worldSpace.xyz;
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
|
||||
struct VertexData {
|
||||
vec4 Normal;
|
||||
vec2 TexCoord0;
|
||||
vec2 TexCoord1;
|
||||
vec4 Color;
|
||||
};
|
||||
|
||||
struct Material
|
||||
{
|
||||
vec4 m_AlbedoFactor; // 16 16
|
||||
vec3 m_EmissionFactor; // 12 28
|
||||
float m_MetalFactor; // 04 32
|
||||
float m_RoughFactor; // 04 36
|
||||
uint m_AlbedoTex; // 04 40
|
||||
uint m_NormalTex; // 04 44
|
||||
uint m_MetalRoughTex; // 04 48
|
||||
uint m_OcclusionTex; // 04 52
|
||||
uint m_EmissionTex; // 04 56
|
||||
// We might be able to go upto 64 without pains.
|
||||
};
|
||||
|
||||
// ---------- Bindless Bindings ----------
|
||||
#define INVALID_HANDLE 0xFFFFFFFF
|
||||
|
||||
layout (set = 0, binding = 1) uniform sampler2D textures[];
|
||||
layout (set = 0, binding = 1) uniform samplerCube textureCubes[];
|
||||
|
||||
|
||||
// ---------- Buffer References ----------
|
||||
layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef {
|
||||
vec4 Positions[];
|
||||
};
|
||||
|
||||
layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VDataRef {
|
||||
VertexData Data[];
|
||||
};
|
||||
|
||||
layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer MaterialsRef {
|
||||
Material materials[];
|
||||
};
|
||||
|
||||
layout(std430, buffer_reference, buffer_reference_align=8) readonly buffer MaterialRef {
|
||||
vec4 m_AlbedoFactor; // 16 16
|
||||
vec3 m_EmissionFactor; // 12 28
|
||||
float m_MetalFactor; // 04 32
|
||||
float m_RoughFactor; // 04 36
|
||||
uint m_AlbedoTex; // 04 40
|
||||
uint m_NormalTex; // 04 44
|
||||
uint m_MetalRoughTex; // 04 48
|
||||
uint m_OcclusionTex; // 04 52
|
||||
uint m_EmissionTex; // 04 56
|
||||
};
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
|
||||
struct VertexData
|
||||
{
|
||||
float4 Normal;
|
||||
float2 TexCoord0;
|
||||
float2 TexCoord1;
|
||||
float4 Color0;
|
||||
};
|
||||
|
||||
struct TransformData
|
||||
{
|
||||
float4x4 Transform;
|
||||
float4x4 NormalTransform;
|
||||
};
|
||||
|
||||
struct MaterialData
|
||||
{
|
||||
float AlbedoFactor[4];
|
||||
float EmissionFactor[3];
|
||||
float MetalFactor;
|
||||
float RoughFactor;
|
||||
uint AlbedoTex;
|
||||
uint NormalTex;
|
||||
uint MetalRoughTex;
|
||||
uint OcclusionTex;
|
||||
uint EmissionTex;
|
||||
};
|
||||
|
||||
struct PointLight
|
||||
{
|
||||
float Position[3];
|
||||
float Range;
|
||||
uint Color;
|
||||
float Intensity;
|
||||
};
|
||||
|
||||
struct DirectionalLight
|
||||
{
|
||||
float Direction[3];
|
||||
float Validity_;
|
||||
uint Color;
|
||||
float Intensity;
|
||||
};
|
||||
|
||||
// Little Endian storage. First short is least significant.
|
||||
#define IndexerCount(Indexer) (Indexer & 0xFFFF)
|
||||
#define IndexerOffset(Indexer) ((Indexer & 0xFFFF0000) >> 16);
|
||||
|
||||
#define INVALID_HANDLE 0xFFFFFFFF
|
||||
|
||||
static const float HALF_PI = 1.57079633f;
|
||||
static const float PI = 3.14159265f;
|
||||
static const float TAU = 6.28318530f;
|
||||
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<float4> VertexBuffer[];
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<VertexData> VertexDataBuffer[];
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<MaterialData> MaterialsBuffer[];
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<TransformData> NodeBuffer[];
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<PointLight> PointLightBuffer[];
|
||||
[[vk::binding(0, 0)]] StructuredBuffer<DirectionalLight> DirectionalLightBuffer[];
|
||||
|
||||
[[vk::binding(1, 0)]] Texture2D<float4> Textures[];
|
||||
[[vk::binding(1, 0)]] Texture2D<float2> TexturesRG[];
|
||||
[[vk::binding(1, 0)]] TextureCube<float4> TextureCubes[];
|
||||
[[vk::binding(1, 0)]] SamplerState ImmutableSamplers[];
|
||||
|
||||
[[vk::binding(2, 0)]] RWTexture2D<float4> StorageTextures[];
|
||||
[[vk::binding(2, 0)]] RWTexture2D<float2> StorageTexturesRG[];
|
||||
[[vk::binding(2, 0)]] RWTexture2DArray<float4> StorageTextureArrays[];
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
#include "ibl_common.hlsli"
|
||||
|
||||
struct Block
|
||||
{
|
||||
uint SkyboxHandle;
|
||||
uint OutputTextureHandle;
|
||||
int CubeSide;
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
Block pcb;
|
||||
|
||||
/*
|
||||
| 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 Forward, Up, Right;
|
||||
|
||||
Forward = GetCubeDir(GlobalInvocationID, pcb.CubeSide);
|
||||
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));
|
||||
|
||||
float3 Irradiance = 0.0f.xxx;
|
||||
float3 IrrDirr = 0.0f.xxx;
|
||||
float SampleStep = 0.005f;
|
||||
float SampleCount = 0.0f;
|
||||
|
||||
for (float Azimuth = 0.0f; Azimuth < TAU; Azimuth += SampleStep)
|
||||
{
|
||||
for (float Zenith = 0.0f; Zenith < HALF_PI; Zenith += SampleStep)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
#include "ibl_common.hlsli"
|
||||
|
||||
struct Block
|
||||
{
|
||||
uint HdrEnvHandle;
|
||||
uint OutputTextureHandle;
|
||||
int CubeSide;
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
Block pcb;
|
||||
|
||||
float2 SampleSphericalMap(float3 v)
|
||||
{
|
||||
const float2 InvTan = float2(0.1591f, 0.3183f); // (1/2PI, 1/PI)
|
||||
float2 UV = float2(atan2(-v.x, v.z), asin(-v.y)); // (-PI, -PI/2) to (PI, PI/2)
|
||||
UV *= InvTan; // (-1/2, -1/2) to (1/2, 1/2)
|
||||
UV += 0.5f.xx; // (0, 0) to (1, 1)
|
||||
return UV;
|
||||
}
|
||||
|
||||
[numthreads(16, 16, 1)]
|
||||
void main(uint3 GlobalInvocationID : SV_DispatchThreadID)
|
||||
{
|
||||
float3 LocalDir = GetCubeDir(GlobalInvocationID, pcb.CubeSide);
|
||||
|
||||
float2 UV = SampleSphericalMap(LocalDir);
|
||||
StorageTextureArrays[pcb.OutputTextureHandle][GlobalInvocationID.xyz] = Textures[pcb.HdrEnvHandle].SampleLevel(ImmutableSamplers[pcb.HdrEnvHandle], UV, 0);
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
|
||||
struct Camera {
|
||||
mat4 m_View; // 64
|
||||
mat4 m_Projection; // 128
|
||||
mat4 m_InvView; // 192
|
||||
mat4 m_InvProjection; // 256
|
||||
vec4 m_Position; // 272
|
||||
};
|
||||
|
||||
struct Lighting
|
||||
{
|
||||
uint m_EnvCubeHandle; // 4
|
||||
uint m_DiffuseIrradianceHandle; // 8
|
||||
uint m_PrefilterHandle; // 12
|
||||
uint m_BrdfLutHandle; // 16
|
||||
uint m_LightHandle; // 20
|
||||
uint m_PointLightIndexer; // 24
|
||||
uint m_DirectionalLightIndexer; // 28
|
||||
};
|
||||
|
||||
layout(set=1, binding=0) uniform Ubo {
|
||||
Camera camera;
|
||||
Lighting lights;
|
||||
};
|
||||
|
||||
// Little Endian storage. First short is least significant.
|
||||
#define IndexerCount(Indexer) (Indexer & 0xFFFF)
|
||||
#define IndexerOffset(Indexer) ((Indexer & 0xFFFF0000) >> 16)
|
||||
|
||||
#define PI 3.1415926535
|
||||
|
||||
vec3 Uncharted2Tonemap(vec3 color)
|
||||
{
|
||||
float A = 0.15f;
|
||||
float B = 0.50f;
|
||||
float C = 0.10f;
|
||||
float D = 0.20f;
|
||||
float E = 0.02f;
|
||||
float F = 0.30f;
|
||||
float W = 11.2f;
|
||||
return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F;
|
||||
}
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
#include "bindless_structs.hlsli"
|
||||
|
||||
/*
|
||||
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)
|
||||
{
|
||||
float2 FaceUV = float2(GlobalInvocationID.xy) / SideLength; // (0, SideLength) -> (0, 1)
|
||||
FaceUV = 2.0f * FaceUV - 1.0f; // (0, 1) -> (-1, 1)
|
||||
|
||||
switch (GlobalInvocationID.z)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
@ -2,17 +2,299 @@
|
|||
#pragma shader_stage(fragment)
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
#include "bindless_structs.glsl"
|
||||
#include "graphics_bindings.glsl"
|
||||
|
||||
layout (location = 0) in vec4 in_Position;
|
||||
layout (location = 1) in vec4 in_Normal;
|
||||
layout (location = 2) in vec4 in_Color0;
|
||||
layout (location = 3) in vec2 in_TexCoord0;
|
||||
|
||||
layout (location = 2) in vec4 inColor;
|
||||
layout (location = 0) out vec4 outColor;
|
||||
|
||||
layout(push_constant) uniform Constants {
|
||||
layout(push_constant) uniform Constants
|
||||
{
|
||||
mat4 globalTransform;
|
||||
uint64_t vertexPos;
|
||||
uint64_t vertexDat;
|
||||
uint64_t materialIdx;
|
||||
} pcb;
|
||||
MaterialRef g_Material;
|
||||
};
|
||||
|
||||
vec4 GetAlbedo(vec4 albedoFactor, uint texHandle, vec4 in_Color0, vec2 uv)
|
||||
{
|
||||
return albedoFactor * in_Color0 * ((texHandle != INVALID_HANDLE) ? texture(textures[texHandle], uv) : vec4(1.0f));
|
||||
}
|
||||
|
||||
float GetOcclusion(uint texHandle, vec2 uv)
|
||||
{
|
||||
return texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).r : 1.0f;
|
||||
}
|
||||
|
||||
vec3 GetEmissive(vec3 emissionFactor, uint texHandle, vec2 uv)
|
||||
{
|
||||
return emissionFactor * (texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).rgb : vec3(1.0f));
|
||||
}
|
||||
|
||||
vec3 GetNormal(uint texHandle, vec3 position, vec3 normal, vec2 uv)
|
||||
{
|
||||
vec3 N = normalize(normal);
|
||||
|
||||
if (texHandle == INVALID_HANDLE)
|
||||
{
|
||||
return N;
|
||||
}
|
||||
|
||||
vec3 tanSpaceNormal = texture(textures[texHandle], uv).xyz * 2.0f - 1.0f;
|
||||
|
||||
vec3 q1 = dFdx(position);
|
||||
vec3 q2 = dFdy(position);
|
||||
vec2 st1 = dFdx(uv);
|
||||
vec2 st2 = dFdy(uv);
|
||||
|
||||
vec3 T = normalize(q1 * st2.y - q2 * st1.y).xyz;
|
||||
vec3 B = -normalize(cross(N, T));
|
||||
mat3 TBN = mat3(T, B, N); // Construction is Col by Col
|
||||
|
||||
return normalize(TBN * tanSpaceNormal);
|
||||
}
|
||||
|
||||
vec2 GetMetalRough(float metalFactor, float roughFactor, uint texHandle, vec2 uv)
|
||||
{
|
||||
return vec2(metalFactor, roughFactor) * (texHandle != INVALID_HANDLE ? texture(textures[texHandle], uv).bg : vec2(1.0f)); // Metal is B, Rough is G.
|
||||
}
|
||||
|
||||
vec3 SampleIrradiance(vec3 direction)
|
||||
{
|
||||
uint texHandle = lights.m_DiffuseIrradianceHandle;
|
||||
return ((texHandle != INVALID_HANDLE) ? texture(textureCubes[texHandle], direction).rgb : vec3(0.04f));
|
||||
}
|
||||
|
||||
vec3 SamplePrefiltered(vec3 direction, float roughness)
|
||||
{
|
||||
const float MAX_MIP_LEVEL = 5.0f;
|
||||
float mipLevel = MAX_MIP_LEVEL * roughness;
|
||||
|
||||
uint texHandle = lights.m_PrefilterHandle;
|
||||
|
||||
return (texHandle != INVALID_HANDLE) ? textureLod(textureCubes[texHandle], direction, mipLevel).rgb : vec3(0.0f);
|
||||
}
|
||||
|
||||
vec2 SampleBrdfLut(float ndotv, float roughness)
|
||||
{
|
||||
return lights.m_BrdfLutHandle != INVALID_HANDLE ? texture(textures[lights.m_BrdfLutHandle], vec2(ndotv, roughness)).rg : 0.0f.xx;
|
||||
}
|
||||
|
||||
|
||||
float TrowbridgeReitzGGX(vec3 normal, vec3 halfway, float roughness)
|
||||
{
|
||||
float coeff = roughness * roughness;
|
||||
float coeff2 = coeff * coeff;
|
||||
float ndoth = max(dot(normal, halfway), 0.0f);
|
||||
float ndoth2 = ndoth * ndoth;
|
||||
|
||||
float numerator = coeff2;
|
||||
float denominator = ndoth2 * (coeff2 - 1.0f) + 1.0f;
|
||||
denominator = PI * denominator * denominator;
|
||||
|
||||
return numerator / denominator;
|
||||
}
|
||||
|
||||
float GeometrySchlickGGX(float ndotv, float roughness)
|
||||
{
|
||||
float r = roughness + 1.0f;
|
||||
float k = (r * r) / 8.0f;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// https://en.wikipedia.org/wiki/Schlick%27s_approximation
|
||||
// TODO: Possibly needs fixing. unreal vs LearnOpenGL impl.
|
||||
vec3 FresnelSchlick(float cosine, vec3 f0)
|
||||
{
|
||||
return f0 + (1.0f - f0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
|
||||
}
|
||||
|
||||
// Sebastian Lagarde
|
||||
vec3 FresnelSchlickRoughness(float cosine, vec3 f0, float roughness)
|
||||
{
|
||||
return f0 + (max((1.0f - roughness).xxx, f0) - f0) * pow(clamp(1.0f - cosine, 0.0f, 1.0f), 5.0f); // Clamp to avoid artifacts.
|
||||
}
|
||||
|
||||
vec3 GetPBRContrib(vec3 albedo, vec3 lightColor, vec3 viewDir, vec3 normal, float metallic, float roughness, vec3 f0, vec3 lightDir, float lightDistance)
|
||||
{
|
||||
float attenuation = 1.0f / (lightDistance * lightDistance); // TODO: Controlled Attenuation
|
||||
vec3 halfway = normalize(viewDir + lightDir);
|
||||
|
||||
float cosineFactor = max(dot(halfway, viewDir), 0.0f);
|
||||
float ndotv = max(dot(normal, viewDir), 0.0f);
|
||||
float ndotl = max(dot(normal, lightDir), 0.0f);
|
||||
|
||||
vec3 radiance = lightColor * attenuation;
|
||||
|
||||
float normalDistribution = TrowbridgeReitzGGX(normal, halfway, roughness);
|
||||
float geometry = GeometrySmith(ndotv, ndotl, roughness);
|
||||
vec3 fresnel = FresnelSchlickRoughness(cosineFactor, f0, roughness);
|
||||
|
||||
vec3 numerator = (normalDistribution * geometry) * fresnel;
|
||||
float denominator = 4.0f * ndotv * ndotl;
|
||||
vec3 specular = numerator / (denominator + 0.00001f);
|
||||
|
||||
vec3 kSpecular = fresnel;
|
||||
vec3 kDiffuse = 1.0f.xxx - kSpecular;
|
||||
|
||||
kDiffuse *= 1.0f - metallic;
|
||||
|
||||
return ndotl * radiance * (kDiffuse * albedo / PI + specular);
|
||||
}
|
||||
//
|
||||
//vec3 GetPointLightInfluence(vec3 albedo, vec2 MetalRough, vec3 Position, vec3 Normal)
|
||||
//{
|
||||
// if (lights.LightHandle == INVALID_HANDLE)
|
||||
// return 0.0f.xxx;
|
||||
//
|
||||
// uint Offset = IndexerOffset(lights.PointLightIndexer);
|
||||
// uint Count = IndexerCount(lights.PointLightIndexer);
|
||||
//
|
||||
// vec3 ViewDir = normalize(Camera.Position.xyz - Position);
|
||||
//
|
||||
// float Metallic = MetalRough.r;
|
||||
// float Roughness = MetalRough.g;
|
||||
//
|
||||
// // Dielectric F_0 based on LearnOpenGL.
|
||||
// // TODO: Cite
|
||||
// vec3 F_0 = 0.04f.xxx;
|
||||
// F_0 = lerp(F_0, albedo, Metallic);
|
||||
//
|
||||
// vec3 Contrib = 0.0f;
|
||||
// for (uint i = 0; i < Count; ++i)
|
||||
// {
|
||||
// PointLight Light = PointLightBuffer[lights.LightHandle][i + Offset];
|
||||
//
|
||||
// if (Light.Range < 0.0f)
|
||||
// continue;
|
||||
//
|
||||
// vec3 LightDir = vec3(Light.Position) - Position;
|
||||
// float LightDistance = length(LightDir);
|
||||
//
|
||||
// if (LightDistance > Light.Range)
|
||||
// continue;
|
||||
//
|
||||
// LightDir /= LightDistance; // Normalization
|
||||
//
|
||||
// // Color Unpack
|
||||
// float R = (Light.Color & 0xFF000000) >> 24;
|
||||
// float G = (Light.Color & 0x00FF0000) >> 16;
|
||||
// float B = (Light.Color & 0x0000FF00) >> 8;
|
||||
//
|
||||
// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
//
|
||||
// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, LightDistance);
|
||||
// }
|
||||
//
|
||||
// return Contrib;
|
||||
//}
|
||||
//
|
||||
//vec3 GetDirectionalLightInfluence(vec3 albedo, float2 MetalRough, vec3 Position, vec3 Normal)
|
||||
//{
|
||||
// if (lights.LightHandle == INVALID_HANDLE)
|
||||
// return 0.0f.xxx;
|
||||
//
|
||||
// uint Count = IndexerCount(lights.DirectionalLightIndexer);
|
||||
//
|
||||
// vec3 ViewDir = normalize(Camera.Position.xyz - Position);
|
||||
//
|
||||
// float Metallic = MetalRough.r;
|
||||
// float Roughness = MetalRough.g;
|
||||
//
|
||||
// // Dielectric F_0 based on LearnOpenGL.
|
||||
// // TODO: Cite
|
||||
// vec3 F_0 = 0.04f.xxx;
|
||||
// F_0 = lerp(F_0, albedo, Metallic);
|
||||
//
|
||||
// vec3 Contrib = 0.0f;
|
||||
// for (uint i = 0; i < Count; ++i)
|
||||
// {
|
||||
// DirectionalLight Light = DirectionalLightBuffer[lights.LightHandle][i];
|
||||
//
|
||||
// if (Light.Validity_ < 0.0f)
|
||||
// continue;
|
||||
//
|
||||
// vec3 LightDir = -normalize(vec3(Light.Direction));
|
||||
//
|
||||
// // Color Unpack
|
||||
// float R = (Light.Color & 0xFF000000) >> 24;
|
||||
// float G = (Light.Color & 0x00FF0000) >> 16;
|
||||
// float B = (Light.Color & 0x0000FF00) >> 8;
|
||||
//
|
||||
// vec3 LightColor = Light.Intensity * vec3(R, G, B) * 0.00392156862f; // 0.00392156862 = 1/255
|
||||
//
|
||||
// Contrib += GetPBRContrib(albedo, LightColor, ViewDir, Normal, Metallic, Roughness, F_0, LightDir, 1.0f);
|
||||
// }
|
||||
//
|
||||
// return Contrib;
|
||||
//}
|
||||
//
|
||||
vec3 GetAmbientInfluence(vec3 albedo, float metal, float roughness, vec3 Position, vec3 Normal, float Occlusion)
|
||||
{
|
||||
vec3 ViewDir = normalize(camera.m_Position.xyz - Position);
|
||||
float CosineFactor = max(dot(Normal, ViewDir), 0.0f); // Normal instead of Halfway since there's no halfway in ambient.
|
||||
|
||||
vec3 F_0 = 0.04f.xxx;
|
||||
F_0 = mix(F_0, albedo, metal);
|
||||
vec3 K_Specular = FresnelSchlickRoughness(CosineFactor, F_0, roughness);
|
||||
vec3 K_Diffuse = 1.0f.xxx - K_Specular;
|
||||
|
||||
K_Diffuse *= 1.0f - metal; // Metals don't have diffuse/refractions.
|
||||
|
||||
vec3 ReflectionDir = reflect(-ViewDir, Normal);
|
||||
|
||||
float NdotV = max(dot(Normal, ViewDir), 0.0f);
|
||||
vec3 PrefilteredColor = SamplePrefiltered(ReflectionDir, roughness).rgb;
|
||||
vec2 EnvBRDF = SampleBrdfLut(NdotV, roughness);
|
||||
vec3 Specular = PrefilteredColor * (K_Specular * EnvBRDF.x + EnvBRDF.y);
|
||||
|
||||
vec3 DiffuseIrradiance = albedo * SampleIrradiance(Normal);
|
||||
//#ifdef _DEBUG
|
||||
// if ((PushConstant.DebugFlags & USE_DIFFUSE_BIT) == 0) {
|
||||
// DiffuseIrradiance = 0.0f.xxx;
|
||||
// }
|
||||
// if ((PushConstant.DebugFlags & USE_SPECULAR_BIT) == 0) {
|
||||
// Specular = 0.0f.xxx;
|
||||
// }
|
||||
//#endif
|
||||
|
||||
return (K_Diffuse * DiffuseIrradiance + Specular) * Occlusion;
|
||||
}
|
||||
|
||||
void main() {
|
||||
outColor = vec4(inColor.rgb, 1.0f);
|
||||
|
||||
vec4 albedoA = GetAlbedo(g_Material.m_AlbedoFactor, g_Material.m_AlbedoTex, in_Color0, in_TexCoord0);
|
||||
vec3 albedo = albedoA.rgb;
|
||||
float alpha = albedoA.a;
|
||||
|
||||
vec3 normal = GetNormal(g_Material.m_NormalTex, in_Position.xyz, normalize(in_Normal.xyz), in_TexCoord0);
|
||||
|
||||
vec3 emission = GetEmissive(g_Material.m_EmissionFactor, g_Material.m_EmissionTex, in_TexCoord0);
|
||||
|
||||
vec2 metalRough = GetMetalRough(g_Material.m_MetalFactor, g_Material.m_RoughFactor, g_Material.m_MetalRoughTex, in_TexCoord0);
|
||||
float occlusion = GetOcclusion(g_Material.m_OcclusionTex, in_TexCoord0);
|
||||
|
||||
vec3 ambientLumin = GetAmbientInfluence(albedo, metalRough.r, metalRough.g, in_Position.xyz, normal, occlusion);
|
||||
|
||||
outColor = vec4(Uncharted2Tonemap(ambientLumin + emission), alpha);
|
||||
|
||||
// outColor = vec4(0.5f + 0.5f * normal, 1.0f);
|
||||
}
|
||||
|
|
@ -3,40 +3,20 @@
|
|||
#extension GL_EXT_shader_explicit_arithmetic_types_int64 : enable
|
||||
#extension GL_EXT_buffer_reference : require
|
||||
|
||||
#include "bindless_structs.glsl"
|
||||
#include "graphics_bindings.glsl"
|
||||
|
||||
layout(location=0) out vec4 outWorldNormal;
|
||||
layout(location=1) out vec4 outWorldPosition;
|
||||
layout(location=2) out vec4 outColor;
|
||||
layout(location=3) out vec2 outUV0;
|
||||
|
||||
struct VertexData {
|
||||
vec4 Normal;
|
||||
vec2 TexCoord0;
|
||||
vec2 TexCoord1;
|
||||
vec4 Color;
|
||||
};
|
||||
|
||||
layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VPositionRef {
|
||||
vec4 Positions[];
|
||||
};
|
||||
|
||||
layout(std430, buffer_reference, buffer_reference_align=16) readonly buffer VDataRef {
|
||||
VertexData Data[];
|
||||
};
|
||||
|
||||
layout(set=1, binding=0) uniform Camera {
|
||||
mat4 View; // 64
|
||||
mat4 Projection; // 128
|
||||
mat4 InvView; // 192
|
||||
mat4 InvProjection; // 256
|
||||
vec4 Position; // 272
|
||||
} camera;
|
||||
|
||||
layout(push_constant) uniform Constants {
|
||||
mat4 globalTransform;
|
||||
VPositionRef vertexPos;
|
||||
VDataRef vertexDat;
|
||||
VPositionRef pVertexPosition;
|
||||
VDataRef pVertexData;
|
||||
uint64_t materialIdx;
|
||||
} pcb;
|
||||
};
|
||||
|
||||
void main() {
|
||||
vec3 colors[] = {
|
||||
|
|
@ -45,12 +25,10 @@ void main() {
|
|||
vec3( 0.0f, 0.0f, 1.0f ),
|
||||
};
|
||||
|
||||
gl_Position = camera.Projection * camera.View * pcb.globalTransform * vec4(pcb.vertexPos.Positions[gl_VertexIndex].xyz, 1.0f);
|
||||
outColor = vec4(pcb.vertexDat.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]);
|
||||
|
||||
// TODO
|
||||
// layout(location=0) out vec4 outWorldNormal;
|
||||
// layout(location=1) out vec4 outWorldPosition;
|
||||
// layout(location=2) out vec4 outColor;
|
||||
// layout(location=3) out vec2 outUV0;
|
||||
outWorldNormal = vec4(normalize(transpose(inverse(mat3(globalTransform))) * pVertexData.Data[gl_VertexIndex].Normal.xyz), 0.0f);
|
||||
|
||||
outWorldPosition = globalTransform * vec4(pVertexPosition.Positions[gl_VertexIndex].xyz, 1.0f);
|
||||
gl_Position = camera.m_Projection * camera.m_View * outWorldPosition;
|
||||
outColor = vec4(pVertexData.Data[gl_VertexIndex].Color.rgb, 1.0f); //vec3(colors[gl_VertexIndex % 3]);
|
||||
outUV0 = pVertexData.Data[gl_VertexIndex].TexCoord0;
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
|
||||
struct VS_Input
|
||||
{
|
||||
uint VertexIndex : SV_VertexID;
|
||||
};
|
||||
|
||||
struct VS_Out {
|
||||
float4 WorldNormal : NORMAL;
|
||||
float4 WorldPosition : POSITION;
|
||||
float4 Color : COLOR0;
|
||||
float2 TexCoord0 : TEXCOORD0;
|
||||
};
|
||||
|
||||
struct CameraData
|
||||
{
|
||||
float4x4 View; // 64
|
||||
float4x4 Projection; // 128
|
||||
float4x4 InvView; // 192
|
||||
float4x4 InvProjection; // 256
|
||||
float4 Position; // 272
|
||||
};
|
||||
|
||||
[[vk::binding(0, 1)]] ConstantBuffer<CameraData> Camera;
|
||||
|
||||
struct VertexData {
|
||||
float4 Normal;
|
||||
float2 TexCoord0;
|
||||
float2 TexCoord1;
|
||||
float4 Color;
|
||||
};
|
||||
|
||||
[[vk::binding(0, 0)]] ByteAddressBuffer GeometryBuffer[];
|
||||
|
||||
layout(set=1, binding=0) uniform Camera {
|
||||
float4x4 View; // 64
|
||||
float4x4 Projection; // 128
|
||||
float4x4 InvView; // 192
|
||||
float4x4 InvProjection; // 256
|
||||
float4 Position; // 272
|
||||
} Camera;
|
||||
|
||||
layout(push_constant) uniform Constants {
|
||||
mat4 globalTransform;
|
||||
uint64 vertexPos;
|
||||
VDataRef vertexDat;
|
||||
uint64_t materialIdx;
|
||||
} pcb;
|
||||
|
||||
void main() {
|
||||
VS_Output Output;
|
||||
|
||||
float4 GlobalPosition = mul(globalTransform, GeometryBuffer[0].Load<float4>());
|
||||
float4 ClipSpace = mul(Camera.View, GlobalPosition);
|
||||
|
||||
Output.VertexPosition = mul(Camera.Projection, ClipSpace);
|
||||
Output.WorldPosition = GlobalPosition;
|
||||
Output.UV0 = GetUV(StageInput.VertexIndex);
|
||||
Output.VertexColor = GetColor(StageInput.VertexIndex);
|
||||
|
||||
Output.WorldNormal = mul(GetNormalTransform(PushConstant.NodeIdx), GetNormal(StageInput.VertexIndex));
|
||||
return Output;
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
#include "ibl_common.hlsli"
|
||||
|
||||
struct Block
|
||||
{
|
||||
uint SkyboxHandle;
|
||||
uint OutputTextureHandle;
|
||||
int CubeSide;
|
||||
float Roughness;
|
||||
uint EnvRes;
|
||||
};
|
||||
|
||||
[[vk::push_constant]]
|
||||
Block Pcb;
|
||||
|
||||
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