ADD: IBL added
This commit is contained in:
@@ -25,9 +25,21 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine *engine)
|
||||
VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
nullptr, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
|
||||
// Ensure IBL layout exists; add placeholder for set=2
|
||||
// Create a persistent empty set layout placeholder (lifetime = GLTFMetallic_Roughness)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
VK_CHECK(vkCreateDescriptorSetLayout(engine->_deviceManager->device(), &info, nullptr, &emptySetLayout));
|
||||
}
|
||||
VkDescriptorSetLayout iblLayout = emptySetLayout;
|
||||
if (engine->_context->ibl && engine->_context->ibl->ensureLayout())
|
||||
iblLayout = engine->_context->ibl->descriptorLayout();
|
||||
|
||||
VkDescriptorSetLayout layouts[] = {
|
||||
engine->_descriptorManager->gpuSceneDataLayout(),
|
||||
materialLayout
|
||||
engine->_descriptorManager->gpuSceneDataLayout(), // set=0
|
||||
materialLayout, // set=1
|
||||
emptySetLayout, // set=2 (unused)
|
||||
iblLayout // set=3
|
||||
};
|
||||
|
||||
// Register pipelines with the central PipelineManager
|
||||
@@ -87,6 +99,8 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine *engine)
|
||||
};
|
||||
engine->_pipelineManager->registerGraphics("mesh.gbuffer", gbufferInfo);
|
||||
|
||||
// Keep emptySetLayout until clear_resources()
|
||||
|
||||
engine->_pipelineManager->getMaterialPipeline("mesh.opaque", opaquePipeline);
|
||||
engine->_pipelineManager->getMaterialPipeline("mesh.transparent", transparentPipeline);
|
||||
engine->_pipelineManager->getMaterialPipeline("mesh.gbuffer", gBufferPipeline);
|
||||
@@ -95,6 +109,7 @@ void GLTFMetallic_Roughness::build_pipelines(VulkanEngine *engine)
|
||||
void GLTFMetallic_Roughness::clear_resources(VkDevice device) const
|
||||
{
|
||||
vkDestroyDescriptorSetLayout(device, materialLayout, nullptr);
|
||||
if (emptySetLayout) vkDestroyDescriptorSetLayout(device, emptySetLayout, nullptr);
|
||||
}
|
||||
|
||||
MaterialInstance GLTFMetallic_Roughness::write_material(VkDevice device, MaterialPass pass,
|
||||
|
||||
@@ -12,6 +12,7 @@ struct GLTFMetallic_Roughness
|
||||
MaterialPipeline gBufferPipeline;
|
||||
|
||||
VkDescriptorSetLayout materialLayout;
|
||||
VkDescriptorSetLayout emptySetLayout = VK_NULL_HANDLE; // placeholder for set=2
|
||||
|
||||
struct MaterialConstants
|
||||
{
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
#include "core/vk_pipeline_manager.h"
|
||||
#include "core/asset_manager.h"
|
||||
#include "render/rg_graph.h"
|
||||
#include <cstring>
|
||||
|
||||
#include "frame_resources.h"
|
||||
#include "ibl_manager.h"
|
||||
#include "vk_descriptor_manager.h"
|
||||
#include "vk_device.h"
|
||||
#include "vk_sampler_manager.h"
|
||||
|
||||
void BackgroundPass::init(EngineContext *context)
|
||||
{
|
||||
@@ -43,6 +50,63 @@ void BackgroundPass::init_background_pipelines()
|
||||
|
||||
_backgroundEffects.push_back(gradient);
|
||||
_backgroundEffects.push_back(sky);
|
||||
// Graphics env (cubemap) background mode
|
||||
ComputeEffect env{}; env.name = "env";
|
||||
_backgroundEffects.push_back(env);
|
||||
|
||||
// Prepare graphics pipeline for environment background (cubemap)
|
||||
// Create an empty descriptor set layout to occupy sets 1 and 2 (shader uses set=0 and set=3)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
info.bindingCount = 0;
|
||||
info.pBindings = nullptr;
|
||||
vkCreateDescriptorSetLayout(_context->getDevice()->device(), &info, nullptr, &_emptySetLayout);
|
||||
}
|
||||
|
||||
GraphicsPipelineCreateInfo gp{};
|
||||
gp.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
|
||||
gp.fragmentShaderPath = _context->getAssets()->shaderPath("background_env.frag.spv");
|
||||
VkDescriptorSetLayout sl0 = _context->getDescriptorLayouts()->gpuSceneDataLayout();
|
||||
VkDescriptorSetLayout sl1 = _emptySetLayout; // placeholder for set=1
|
||||
VkDescriptorSetLayout sl2 = _emptySetLayout; // placeholder for set=2
|
||||
// Ensure IBL layout exists (now owned by IBLManager)
|
||||
VkDescriptorSetLayout sl3 = _emptySetLayout;
|
||||
if (_context->ibl && _context->ibl->ensureLayout())
|
||||
sl3 = _context->ibl->descriptorLayout();
|
||||
gp.setLayouts = { sl0, sl1, sl2, sl3 };
|
||||
gp.configure = [this](PipelineBuilder &b) {
|
||||
b.set_input_topology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
|
||||
b.set_polygon_mode(VK_POLYGON_MODE_FILL);
|
||||
b.set_cull_mode(VK_CULL_MODE_NONE, VK_FRONT_FACE_CLOCKWISE);
|
||||
b.set_multisampling_none();
|
||||
b.disable_depthtest();
|
||||
b.disable_blending();
|
||||
b.set_color_attachment_format(_context->getSwapchain()->drawImage().imageFormat);
|
||||
};
|
||||
_context->pipelines->createGraphicsPipeline("background.env", gp);
|
||||
|
||||
// Create fallback 1x1x6 black cube
|
||||
{
|
||||
const uint32_t faceCount = 6;
|
||||
const uint32_t pixel = 0x00000000u; // RGBA8 black
|
||||
std::vector<uint8_t> bytes(faceCount * 4);
|
||||
for (uint32_t f = 0; f < faceCount; ++f) std::memcpy(bytes.data() + f * 4, &pixel, 4);
|
||||
std::vector<VkBufferImageCopy> copies;
|
||||
copies.reserve(faceCount);
|
||||
for (uint32_t f = 0; f < faceCount; ++f) {
|
||||
VkBufferImageCopy r{};
|
||||
r.bufferOffset = f * 4;
|
||||
r.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
r.imageSubresource.mipLevel = 0;
|
||||
r.imageSubresource.baseArrayLayer = f;
|
||||
r.imageSubresource.layerCount = 1;
|
||||
r.imageExtent = {1,1,1};
|
||||
copies.push_back(r);
|
||||
}
|
||||
_fallbackIblCube = _context->getResources()->create_image_compressed_layers(
|
||||
bytes.data(), bytes.size(), VK_FORMAT_R8G8B8A8_UNORM, 1, faceCount, copies,
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT, VK_IMAGE_CREATE_CUBE_COMPATIBLE_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPass::execute(VkCommandBuffer)
|
||||
@@ -56,33 +120,92 @@ void BackgroundPass::register_graph(RenderGraph *graph, RGImageHandle drawHandle
|
||||
if (!graph || !drawHandle.valid() || !_context) return;
|
||||
if (_backgroundEffects.empty()) return;
|
||||
|
||||
graph->add_pass(
|
||||
"Background",
|
||||
RGPassType::Compute,
|
||||
[drawHandle](RGPassBuilder &builder, EngineContext *) {
|
||||
builder.write(drawHandle, RGImageUsage::ComputeWrite);
|
||||
},
|
||||
[this, drawHandle](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
|
||||
VkImageView drawView = res.image_view(drawHandle);
|
||||
if (drawView != VK_NULL_HANDLE)
|
||||
{
|
||||
_context->pipelines->setComputeInstanceStorageImage("background.gradient", 0, drawView);
|
||||
_context->pipelines->setComputeInstanceStorageImage("background.sky", 0, drawView);
|
||||
// Route to compute or graphics depending on selected mode
|
||||
const ComputeEffect &effect = _backgroundEffects[_currentEffect];
|
||||
if (std::string_view(effect.name) == std::string_view("env"))
|
||||
{
|
||||
graph->add_pass(
|
||||
"BackgroundEnv",
|
||||
RGPassType::Graphics,
|
||||
[drawHandle](RGPassBuilder &builder, EngineContext *) {
|
||||
builder.write_color(drawHandle);
|
||||
},
|
||||
[this, drawHandle](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
|
||||
VkImageView drawView = res.image_view(drawHandle);
|
||||
(void) drawView; // handled by RG
|
||||
|
||||
// pipeline + layout
|
||||
if (!ctx->pipelines->getGraphics("background.env", _envPipeline, _envPipelineLayout)) return;
|
||||
|
||||
// Per-frame scene UBO
|
||||
AllocatedBuffer ubo = ctx->getResources()->create_buffer(sizeof(GPUSceneData),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
ctx->currentFrame->_deletionQueue.push_function([rm = ctx->getResources(), ubo]() { rm->destroy_buffer(ubo); });
|
||||
VmaAllocationInfo ai{}; vmaGetAllocationInfo(ctx->getDevice()->allocator(), ubo.allocation, &ai);
|
||||
*reinterpret_cast<GPUSceneData*>(ai.pMappedData) = ctx->getSceneData();
|
||||
vmaFlushAllocation(ctx->getDevice()->allocator(), ubo.allocation, 0, sizeof(GPUSceneData));
|
||||
|
||||
VkDescriptorSet global = ctx->currentFrame->_frameDescriptors.allocate(
|
||||
ctx->getDevice()->device(), ctx->getDescriptorLayouts()->gpuSceneDataLayout());
|
||||
DescriptorWriter w0; w0.write_buffer(0, ubo.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
w0.update_set(ctx->getDevice()->device(), global);
|
||||
|
||||
// IBL set
|
||||
VkImageView specView = _fallbackIblCube.imageView;
|
||||
if (ctx->ibl && ctx->ibl->specular().imageView) specView = ctx->ibl->specular().imageView;
|
||||
VkDescriptorSetLayout iblLayout = (ctx->ibl ? ctx->ibl->descriptorLayout() : _emptySetLayout);
|
||||
VkDescriptorSet ibl = ctx->currentFrame->_frameDescriptors.allocate(
|
||||
ctx->getDevice()->device(), iblLayout);
|
||||
DescriptorWriter w3;
|
||||
// Bind only specular at binding 0; other bindings are unused in this shader
|
||||
w3.write_image(0, specView, ctx->getSamplers()->defaultLinear(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
w3.update_set(ctx->getDevice()->device(), ibl);
|
||||
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _envPipeline);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _envPipelineLayout, 0, 1, &global, 0, nullptr);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _envPipelineLayout, 3, 1, &ibl, 0, nullptr);
|
||||
|
||||
VkExtent2D extent = ctx->getDrawExtent();
|
||||
VkViewport vp{0.f, 0.f, float(extent.width), float(extent.height), 0.f, 1.f};
|
||||
VkRect2D sc{{0,0}, extent};
|
||||
vkCmdSetViewport(cmd, 0, 1, &vp);
|
||||
vkCmdSetScissor(cmd, 0, 1, &sc);
|
||||
vkCmdDraw(cmd, 3, 1, 0, 0);
|
||||
}
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
graph->add_pass(
|
||||
"Background",
|
||||
RGPassType::Compute,
|
||||
[drawHandle](RGPassBuilder &builder, EngineContext *) {
|
||||
builder.write(drawHandle, RGImageUsage::ComputeWrite);
|
||||
},
|
||||
[this, drawHandle](VkCommandBuffer cmd, const RGPassResources &res, EngineContext *ctx) {
|
||||
VkImageView drawView = res.image_view(drawHandle);
|
||||
if (drawView != VK_NULL_HANDLE)
|
||||
{
|
||||
_context->pipelines->setComputeInstanceStorageImage("background.gradient", 0, drawView);
|
||||
_context->pipelines->setComputeInstanceStorageImage("background.sky", 0, drawView);
|
||||
}
|
||||
|
||||
ComputeEffect &effect = _backgroundEffects[_currentEffect];
|
||||
ComputeEffect &eff = _backgroundEffects[_currentEffect];
|
||||
|
||||
ComputeDispatchInfo dispatchInfo = ComputeManager::createDispatch2D(
|
||||
ctx->getDrawExtent().width, ctx->getDrawExtent().height);
|
||||
dispatchInfo.pushConstants = &effect.data;
|
||||
dispatchInfo.pushConstantSize = sizeof(ComputePushConstants);
|
||||
ComputeDispatchInfo dispatchInfo = ComputeManager::createDispatch2D(
|
||||
ctx->getDrawExtent().width, ctx->getDrawExtent().height);
|
||||
dispatchInfo.pushConstants = &eff.data;
|
||||
dispatchInfo.pushConstantSize = sizeof(ComputePushConstants);
|
||||
|
||||
const char *instanceName = (std::string_view(effect.name) == std::string_view("gradient"))
|
||||
? "background.gradient"
|
||||
: "background.sky";
|
||||
ctx->pipelines->dispatchComputeInstance(cmd, instanceName, dispatchInfo);
|
||||
}
|
||||
);
|
||||
const char *instanceName = (std::string_view(eff.name) == std::string_view("gradient"))
|
||||
? "background.gradient"
|
||||
: "background.sky";
|
||||
ctx->pipelines->dispatchComputeInstance(cmd, instanceName, dispatchInfo);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void BackgroundPass::cleanup()
|
||||
@@ -94,6 +217,22 @@ void BackgroundPass::cleanup()
|
||||
_context->pipelines->destroyComputePipeline("gradient");
|
||||
_context->pipelines->destroyComputePipeline("sky");
|
||||
}
|
||||
if (_envPipeline != VK_NULL_HANDLE || _envPipelineLayout != VK_NULL_HANDLE)
|
||||
{
|
||||
// Pipelines are owned by PipelineManager and destroyed there on cleanup/hot-reload
|
||||
_envPipeline = VK_NULL_HANDLE;
|
||||
_envPipelineLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
if (_emptySetLayout)
|
||||
{
|
||||
vkDestroyDescriptorSetLayout(_context->getDevice()->device(), _emptySetLayout, nullptr);
|
||||
_emptySetLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
if (_fallbackIblCube.image)
|
||||
{
|
||||
_context->getResources()->destroy_image(_fallbackIblCube);
|
||||
_fallbackIblCube = {};
|
||||
}
|
||||
fmt::print("BackgroundPass::cleanup()\n");
|
||||
_backgroundEffects.clear();
|
||||
}
|
||||
|
||||
@@ -25,4 +25,12 @@ private:
|
||||
EngineContext *_context = nullptr;
|
||||
|
||||
void init_background_pipelines();
|
||||
|
||||
// Graphics env background pipeline
|
||||
VkPipeline _envPipeline = VK_NULL_HANDLE;
|
||||
VkPipelineLayout _envPipelineLayout = VK_NULL_HANDLE;
|
||||
// Empty descriptor layout used as placeholder for sets 1 and 2
|
||||
VkDescriptorSetLayout _emptySetLayout = VK_NULL_HANDLE;
|
||||
// Fallback 1x1x6 black cube if IBL not loaded
|
||||
AllocatedImage _fallbackIblCube{};
|
||||
};
|
||||
|
||||
@@ -17,13 +17,22 @@
|
||||
#include "vk_swapchain.h"
|
||||
#include "render/rg_graph.h"
|
||||
#include <array>
|
||||
#include <cstring>
|
||||
|
||||
#include "ibl_manager.h"
|
||||
#include "vk_raytracing.h"
|
||||
|
||||
void LightingPass::init(EngineContext *context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
// Placeholder empty set layout to keep array sizes stable if needed
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo info{ VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO };
|
||||
info.bindingCount = 0; info.pBindings = nullptr;
|
||||
vkCreateDescriptorSetLayout(_context->getDevice()->device(), &info, nullptr, &_emptySetLayout);
|
||||
}
|
||||
|
||||
// Build descriptor layout for GBuffer inputs
|
||||
{
|
||||
DescriptorLayoutBuilder builder;
|
||||
@@ -59,10 +68,16 @@ void LightingPass::init(EngineContext *context)
|
||||
}
|
||||
|
||||
// Build lighting pipelines (RT and non-RT) through PipelineManager
|
||||
// Ensure IBL layout exists (moved to IBLManager)
|
||||
VkDescriptorSetLayout iblLayout = _emptySetLayout;
|
||||
if (_context->ibl && _context->ibl->ensureLayout())
|
||||
iblLayout = _context->ibl->descriptorLayout();
|
||||
|
||||
VkDescriptorSetLayout layouts[] = {
|
||||
_context->getDescriptorLayouts()->gpuSceneDataLayout(),
|
||||
_gBufferInputDescriptorLayout,
|
||||
_shadowDescriptorLayout
|
||||
_context->getDescriptorLayouts()->gpuSceneDataLayout(), // set=0
|
||||
_gBufferInputDescriptorLayout, // set=1
|
||||
_shadowDescriptorLayout, // set=2
|
||||
iblLayout // set=3
|
||||
};
|
||||
|
||||
GraphicsPipelineCreateInfo baseInfo{};
|
||||
@@ -92,7 +107,23 @@ void LightingPass::init(EngineContext *context)
|
||||
// Pipelines are owned by PipelineManager; only destroy our local descriptor set layout
|
||||
vkDestroyDescriptorSetLayout(_context->getDevice()->device(), _gBufferInputDescriptorLayout, nullptr);
|
||||
vkDestroyDescriptorSetLayout(_context->getDevice()->device(), _shadowDescriptorLayout, nullptr);
|
||||
if (_emptySetLayout) vkDestroyDescriptorSetLayout(_context->getDevice()->device(), _emptySetLayout, nullptr);
|
||||
});
|
||||
|
||||
// Create tiny fallback textures for IBL (grey 2D and RG LUT)
|
||||
// so shaders can safely sample even when IBL isn't loaded.
|
||||
{
|
||||
const uint32_t pixel = 0xFF333333u; // RGBA8 grey
|
||||
_fallbackIbl2D = _context->getResources()->create_image(&pixel, VkExtent3D{1,1,1},
|
||||
VK_FORMAT_R8G8B8A8_UNORM,
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
}
|
||||
{
|
||||
// 1x1 RG UNORM for BRDF LUT fallback
|
||||
const uint16_t rg = 0x0000u; // R=0,G=0
|
||||
_fallbackBrdfLut2D = _context->getResources()->create_image(
|
||||
&rg, VkExtent3D{1,1,1}, VK_FORMAT_R8G8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
}
|
||||
}
|
||||
|
||||
void LightingPass::execute(VkCommandBuffer)
|
||||
@@ -223,6 +254,41 @@ void LightingPass::draw_lighting(VkCommandBuffer cmd,
|
||||
}
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 2, 1, &shadowSet, 0, nullptr);
|
||||
|
||||
// IBL descriptor set (set = 3). Use loaded IBL if present, otherwise fall back to black.
|
||||
VkImageView specView = _fallbackIbl2D.imageView;
|
||||
VkImageView brdfView = _fallbackBrdfLut2D.imageView;
|
||||
VkBuffer shBuf = VK_NULL_HANDLE; VkDeviceSize shSize = sizeof(glm::vec4)*9;
|
||||
if (ctxLocal->ibl)
|
||||
{
|
||||
if (ctxLocal->ibl->specular().imageView) specView = ctxLocal->ibl->specular().imageView;
|
||||
if (ctxLocal->ibl->brdf().imageView) brdfView = ctxLocal->ibl->brdf().imageView;
|
||||
if (ctxLocal->ibl->hasSH()) shBuf = ctxLocal->ibl->shBuffer().buffer;
|
||||
}
|
||||
// If SH missing, create a zero buffer for this frame
|
||||
AllocatedBuffer shZero{};
|
||||
if (shBuf == VK_NULL_HANDLE)
|
||||
{
|
||||
shZero = resourceManager->create_buffer(shSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
std::memset(shZero.info.pMappedData, 0, shSize);
|
||||
vmaFlushAllocation(deviceManager->allocator(), shZero.allocation, 0, shSize);
|
||||
shBuf = shZero.buffer;
|
||||
ctxLocal->currentFrame->_deletionQueue.push_function([resourceManager, shZero]() { resourceManager->destroy_buffer(shZero); });
|
||||
}
|
||||
// Allocate from IBL layout (must exist because pipeline was created with it)
|
||||
VkDescriptorSetLayout iblSetLayout = (ctxLocal->ibl ? ctxLocal->ibl->descriptorLayout() : _emptySetLayout);
|
||||
VkDescriptorSet iblSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
|
||||
deviceManager->device(), iblSetLayout);
|
||||
{
|
||||
DescriptorWriter w;
|
||||
w.write_image(0, specView, ctxLocal->getSamplers()->defaultLinear(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
w.write_image(1, brdfView, ctxLocal->getSamplers()->defaultLinear(),
|
||||
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
w.write_buffer(2, shBuf, shSize, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
w.update_set(deviceManager->device(), iblSet);
|
||||
}
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 3, 1, &iblSet, 0, nullptr);
|
||||
|
||||
VkViewport viewport{};
|
||||
viewport.x = 0;
|
||||
viewport.y = 0;
|
||||
|
||||
@@ -27,9 +27,13 @@ private:
|
||||
VkDescriptorSetLayout _gBufferInputDescriptorLayout = VK_NULL_HANDLE;
|
||||
VkDescriptorSet _gBufferInputDescriptorSet = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout _shadowDescriptorLayout = VK_NULL_HANDLE; // set=2 (array)
|
||||
// Fallbacks if IBL is not loaded
|
||||
AllocatedImage _fallbackIbl2D{}; // 1x1 black
|
||||
AllocatedImage _fallbackBrdfLut2D{}; // 1x1 RG, black
|
||||
|
||||
VkPipelineLayout _pipelineLayout = VK_NULL_HANDLE;
|
||||
VkPipeline _pipeline = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout _emptySetLayout = VK_NULL_HANDLE; // placeholder if IBL layout missing
|
||||
|
||||
void draw_lighting(VkCommandBuffer cmd,
|
||||
EngineContext *context,
|
||||
|
||||
@@ -3,7 +3,9 @@
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "ibl_manager.h"
|
||||
#include "texture_cache.h"
|
||||
#include "vk_sampler_manager.h"
|
||||
#include "vk_scene.h"
|
||||
#include "vk_swapchain.h"
|
||||
#include "core/engine_context.h"
|
||||
@@ -16,6 +18,12 @@
|
||||
void TransparentPass::init(EngineContext *context)
|
||||
{
|
||||
_context = context;
|
||||
// Create fallback images
|
||||
const uint32_t pixel = 0x00000000u;
|
||||
_fallbackIbl2D = _context->getResources()->create_image(&pixel, VkExtent3D{1,1,1},
|
||||
VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
_fallbackBrdf2D = _context->getResources()->create_image(&pixel, VkExtent3D{1,1,1},
|
||||
VK_FORMAT_R8G8B8A8_UNORM, VK_IMAGE_USAGE_SAMPLED_BIT);
|
||||
}
|
||||
|
||||
void TransparentPass::execute(VkCommandBuffer)
|
||||
@@ -94,6 +102,41 @@ void TransparentPass::draw_transparent(VkCommandBuffer cmd,
|
||||
writer.write_buffer(0, gpuSceneDataBuffer.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
writer.update_set(deviceManager->device(), globalDescriptor);
|
||||
|
||||
// Build IBL descriptor set (set=3) once for this pass
|
||||
VkDescriptorSet iblSet = VK_NULL_HANDLE;
|
||||
VkDescriptorSetLayout iblLayout = ctxLocal->ibl ? ctxLocal->ibl->descriptorLayout() : VK_NULL_HANDLE;
|
||||
VkImageView specView = VK_NULL_HANDLE, brdfView = VK_NULL_HANDLE;
|
||||
VkBuffer shBuf = VK_NULL_HANDLE; VkDeviceSize shSize = sizeof(glm::vec4)*9;
|
||||
if (iblLayout)
|
||||
{
|
||||
// Fallbacks: use black if any missing
|
||||
specView = (ctxLocal->ibl && ctxLocal->ibl->specular().imageView) ? ctxLocal->ibl->specular().imageView
|
||||
: _fallbackIbl2D.imageView;
|
||||
brdfView = (ctxLocal->ibl && ctxLocal->ibl->brdf().imageView) ? ctxLocal->ibl->brdf().imageView
|
||||
: _fallbackBrdf2D.imageView;
|
||||
if (ctxLocal->ibl && ctxLocal->ibl->hasSH()) shBuf = ctxLocal->ibl->shBuffer().buffer;
|
||||
|
||||
// If SH missing, allocate zero UBO for this frame
|
||||
AllocatedBuffer shZero{};
|
||||
if (shBuf == VK_NULL_HANDLE)
|
||||
{
|
||||
shZero = resourceManager->create_buffer(shSize, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
std::memset(shZero.info.pMappedData, 0, shSize);
|
||||
vmaFlushAllocation(deviceManager->allocator(), shZero.allocation, 0, shSize);
|
||||
shBuf = shZero.buffer;
|
||||
ctxLocal->currentFrame->_deletionQueue.push_function([resourceManager, shZero]() { resourceManager->destroy_buffer(shZero); });
|
||||
}
|
||||
|
||||
iblSet = ctxLocal->currentFrame->_frameDescriptors.allocate(deviceManager->device(), iblLayout);
|
||||
DescriptorWriter iw;
|
||||
iw.write_image(0, specView, ctxLocal->getSamplers()->defaultLinear(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
iw.write_image(1, brdfView, ctxLocal->getSamplers()->defaultLinear(), VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL,
|
||||
VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
iw.write_buffer(2, shBuf, shSize, 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
iw.update_set(deviceManager->device(), iblSet);
|
||||
}
|
||||
|
||||
// Sort transparent back-to-front using camera-space depth.
|
||||
// We approximate object depth by transforming the mesh bounds origin.
|
||||
// For better results consider using per-object center or per-draw depth range.
|
||||
@@ -132,6 +175,11 @@ void TransparentPass::draw_transparent(VkCommandBuffer cmd,
|
||||
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, r.material->pipeline->pipeline);
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, r.material->pipeline->layout, 0, 1,
|
||||
&globalDescriptor, 0, nullptr);
|
||||
if (iblSet)
|
||||
{
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, r.material->pipeline->layout, 3, 1,
|
||||
&iblSet, 0, nullptr);
|
||||
}
|
||||
}
|
||||
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, r.material->pipeline->layout, 1, 1,
|
||||
&r.material->materialSet, 0, nullptr);
|
||||
@@ -163,5 +211,10 @@ void TransparentPass::draw_transparent(VkCommandBuffer cmd,
|
||||
|
||||
void TransparentPass::cleanup()
|
||||
{
|
||||
if (_context && _context->getResources())
|
||||
{
|
||||
if (_fallbackIbl2D.image) _context->getResources()->destroy_image(_fallbackIbl2D);
|
||||
if (_fallbackBrdf2D.image) _context->getResources()->destroy_image(_fallbackBrdf2D);
|
||||
}
|
||||
fmt::print("TransparentPass::cleanup()\n");
|
||||
}
|
||||
|
||||
@@ -24,5 +24,7 @@ private:
|
||||
RGImageHandle depthHandle) const;
|
||||
|
||||
EngineContext *_context{};
|
||||
mutable AllocatedImage _fallbackIbl2D{}; // 1x1 black (created in init)
|
||||
mutable AllocatedImage _fallbackBrdf2D{}; // 1x1 black RG
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user