This commit is contained in:
2025-12-01 19:35:38 +09:00
parent 637a83bf6f
commit 64528f2c4a
10 changed files with 479 additions and 5 deletions

View File

@@ -68,6 +68,8 @@ add_executable (vulkan_engine
render/passes/lighting.cpp
render/passes/shadow.h
render/passes/shadow.cpp
render/passes/ssr.h
render/passes/ssr.cpp
render/passes/transparent.h
render/passes/transparent.cpp
render/passes/imgui_pass.h

View File

@@ -237,7 +237,29 @@ bool IBLManager::load(const IBLPaths &paths)
_diff = _spec;
}
// If background is still missing but specular is valid, reuse the specular environment.
if (!paths.background2D.empty())
{
ktxutil::Ktx2D bg{};
if (ktxutil::load_ktx2_2d(paths.background2D.c_str(), bg))
{
std::vector<ResourceManager::MipLevelCopy> lv;
lv.reserve(bg.mipLevels);
for (uint32_t mip = 0; mip < bg.mipLevels; ++mip)
{
const auto &r = bg.copies[mip];
lv.push_back(ResourceManager::MipLevelCopy{
.offset = r.bufferOffset,
.length = 0,
.width = r.imageExtent.width,
.height = r.imageExtent.height,
});
}
_background = rm->create_image_compressed(
bg.bytes.data(), bg.bytes.size(), bg.fmt, lv,
VK_IMAGE_USAGE_SAMPLED_BIT);
}
}
if (_background.image == VK_NULL_HANDLE && _spec.image != VK_NULL_HANDLE)
{
_background = _spec;

View File

@@ -75,6 +75,7 @@ public:
// Runtime settings visible to passes/shaders
ShadowSettings shadowSettings{};
bool enableSSR = false; // optional screen-space reflections toggle
// Ray tracing manager (optional, nullptr if unsupported)
RayTracingManager* ray = nullptr;

View File

@@ -268,6 +268,8 @@ void VulkanEngine::init()
_resourceManager->set_deferred_uploads(true);
_context->enableSSR = true;
//everything went fine
_isInitialized = true;
}
@@ -716,16 +718,49 @@ void VulkanEngine::draw()
lighting->register_graph(_renderGraph.get(), hDraw, hGBufferPosition, hGBufferNormal, hGBufferAlbedo,
std::span<RGImageHandle>(hShadowCascades.data(), hShadowCascades.size()));
}
// Optional Screen Space Reflections pass: consumes HDR draw + G-Buffer and
// produces an SSR-augmented HDR image. Controlled by EngineContext::enableSSR.
RGImageHandle hSSR{};
SSRPass *ssr = _renderPassManager->getPass<SSRPass>();
const bool ssrEnabled = (_context && _context->enableSSR && ssr != nullptr);
if (ssrEnabled)
{
RGImageDesc ssrDesc{};
ssrDesc.name = "hdr.ssr";
ssrDesc.format = _swapchainManager->drawImage().imageFormat;
ssrDesc.extent = _drawExtent;
ssrDesc.usage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT
| VK_IMAGE_USAGE_SAMPLED_BIT
| VK_IMAGE_USAGE_STORAGE_BIT;
hSSR = _renderGraph->create_image(ssrDesc);
ssr->register_graph(_renderGraph.get(),
hDraw,
hGBufferPosition,
hGBufferNormal,
hGBufferAlbedo,
hSSR);
}
if (auto *transparent = _renderPassManager->getPass<TransparentPass>())
{
transparent->register_graph(_renderGraph.get(), hDraw, hDepth);
// Transparent objects draw on top of either the SSR output or the raw HDR draw.
RGImageHandle hdrTarget = (ssrEnabled && hSSR.valid()) ? hSSR : hDraw;
transparent->register_graph(_renderGraph.get(), hdrTarget, hDepth);
}
imguiPass = _renderPassManager->getImGuiPass();
// Optional Tonemap pass: sample HDR draw -> LDR intermediate
if (auto *tonemap = _renderPassManager->getPass<TonemapPass>())
{
finalColor = tonemap->register_graph(_renderGraph.get(), hDraw);
RGImageHandle hdrInput = (ssrEnabled && hSSR.valid()) ? hSSR : hDraw;
finalColor = tonemap->register_graph(_renderGraph.get(), hdrInput);
}
else
{
// If tonemapping is disabled, present whichever HDR buffer we ended up with.
finalColor = (ssrEnabled && hSSR.valid()) ? hSSR : hDraw;
}
}

View File

@@ -26,6 +26,7 @@
#include "frame/resources.h"
#include "descriptor/manager.h"
#include "pipeline/sampler.h"
#include "render/passes/ssr.h"
#include "core/context.h"
#include "core/pipeline/manager.h"
#include "core/assets/manager.h"

208
src/render/passes/ssr.cpp Normal file
View File

@@ -0,0 +1,208 @@
#include "ssr.h"
#include "core/frame/resources.h"
#include "core/descriptor/manager.h"
#include "core/descriptor/descriptors.h"
#include "core/device/device.h"
#include "core/device/resource.h"
#include "core/device/swapchain.h"
#include "core/context.h"
#include "core/pipeline/manager.h"
#include "core/assets/manager.h"
#include "core/pipeline/sampler.h"
#include "render/graph/graph.h"
#include "render/pipelines.h"
void SSRPass::init(EngineContext *context)
{
_context = context;
if (!_context || !_context->getDevice() || !_context->getDescriptorLayouts() || !_context->pipelines)
{
return;
}
VkDevice device = _context->getDevice()->device();
// Set 1 layout: HDR + G-Buffer inputs (all sampled images).
{
DescriptorLayoutBuilder builder;
builder.add_binding(0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // hdrColor
builder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // posTex
builder.add_binding(2, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // normalTex
builder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER); // albedoTex
_inputSetLayout = builder.build(
device,
VK_SHADER_STAGE_FRAGMENT_BIT,
nullptr,
VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
}
// Graphics pipeline: fullscreen triangle, no depth, HDR color attachment.
GraphicsPipelineCreateInfo info{};
info.vertexShaderPath = _context->getAssets()->shaderPath("fullscreen.vert.spv");
info.fragmentShaderPath = _context->getAssets()->shaderPath("ssr.frag.spv");
info.setLayouts = {
_context->getDescriptorLayouts()->gpuSceneDataLayout(), // set = 0 (sceneData UBO)
_inputSetLayout // set = 1 (HDR + GBuffer)
};
info.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();
if (_context && _context->getSwapchain())
{
b.set_color_attachment_format(_context->getSwapchain()->drawImage().imageFormat);
}
};
_context->pipelines->createGraphicsPipeline("ssr", info);
}
void SSRPass::cleanup()
{
if (_context && _context->getDevice() && _inputSetLayout)
{
vkDestroyDescriptorSetLayout(_context->getDevice()->device(), _inputSetLayout, nullptr);
_inputSetLayout = VK_NULL_HANDLE;
}
_deletionQueue.flush();
}
void SSRPass::execute(VkCommandBuffer)
{
// Executed via render graph; nothing to do here.
}
void SSRPass::register_graph(RenderGraph *graph,
RGImageHandle hdrInput,
RGImageHandle gbufPos,
RGImageHandle gbufNorm,
RGImageHandle gbufAlbedo,
RGImageHandle hdrOutput)
{
if (!graph || !hdrInput.valid() || !hdrOutput.valid())
{
return;
}
graph->add_pass(
"SSR",
RGPassType::Graphics,
[hdrInput, gbufPos, gbufNorm, gbufAlbedo, hdrOutput](RGPassBuilder &builder, EngineContext *)
{
// Read current HDR lighting + G-Buffer; write to an HDR output.
builder.read(hdrInput, RGImageUsage::SampledFragment);
if (gbufPos.valid())
{
builder.read(gbufPos, RGImageUsage::SampledFragment);
}
if (gbufNorm.valid())
{
builder.read(gbufNorm, RGImageUsage::SampledFragment);
}
if (gbufAlbedo.valid())
{
builder.read(gbufAlbedo, RGImageUsage::SampledFragment);
}
builder.write_color(hdrOutput, false /*load existing contents*/);
},
[this, hdrInput, gbufPos, gbufNorm, gbufAlbedo](VkCommandBuffer cmd,
const RGPassResources &res,
EngineContext *ctx)
{
draw_ssr(cmd, ctx, res, hdrInput, gbufPos, gbufNorm, gbufAlbedo);
});
}
void SSRPass::draw_ssr(VkCommandBuffer cmd,
EngineContext *context,
const RGPassResources &resources,
RGImageHandle hdrInput,
RGImageHandle gbufPos,
RGImageHandle gbufNorm,
RGImageHandle gbufAlbedo)
{
EngineContext *ctxLocal = context ? context : _context;
if (!ctxLocal || !ctxLocal->currentFrame) return;
ResourceManager *resourceManager = ctxLocal->getResources();
DeviceManager *deviceManager = ctxLocal->getDevice();
DescriptorManager *descriptorLayouts = ctxLocal->getDescriptorLayouts();
PipelineManager *pipelineManager = ctxLocal->pipelines;
if (!resourceManager || !deviceManager || !descriptorLayouts || !pipelineManager) return;
VkImageView hdrView = resources.image_view(hdrInput);
VkImageView posView = resources.image_view(gbufPos);
VkImageView normView = resources.image_view(gbufNorm);
VkImageView albedoView = resources.image_view(gbufAlbedo);
if (hdrView == VK_NULL_HANDLE || posView == VK_NULL_HANDLE ||
normView == VK_NULL_HANDLE || albedoView == VK_NULL_HANDLE)
{
return;
}
// Fetch (or refresh) pipeline for hot-reload support.
if (!pipelineManager->getGraphics("ssr", _pipeline, _pipelineLayout))
{
return;
}
// Scene UBO (set=0, binding=0) mirror LightingPass behavior.
AllocatedBuffer sceneBuf = resourceManager->create_buffer(
sizeof(GPUSceneData),
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
VMA_MEMORY_USAGE_CPU_TO_GPU);
ctxLocal->currentFrame->_deletionQueue.push_function([resourceManager, sceneBuf]()
{
resourceManager->destroy_buffer(sceneBuf);
});
VmaAllocationInfo allocInfo{};
vmaGetAllocationInfo(deviceManager->allocator(), sceneBuf.allocation, &allocInfo);
auto *sceneUniformData = static_cast<GPUSceneData *>(allocInfo.pMappedData);
*sceneUniformData = ctxLocal->getSceneData();
vmaFlushAllocation(deviceManager->allocator(), sceneBuf.allocation, 0, sizeof(GPUSceneData));
VkDescriptorSet globalSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
deviceManager->device(), descriptorLayouts->gpuSceneDataLayout());
{
DescriptorWriter writer;
writer.write_buffer(0, sceneBuf.buffer, sizeof(GPUSceneData), 0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
writer.update_set(deviceManager->device(), globalSet);
}
// Input set (set=1): HDR color + G-Buffer textures.
VkDescriptorSet inputSet = ctxLocal->currentFrame->_frameDescriptors.allocate(
deviceManager->device(), _inputSetLayout);
{
DescriptorWriter writer;
writer.write_image(0, hdrView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(1, posView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(2, normView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.write_image(3, albedoView, ctxLocal->getSamplers()->defaultLinear(),
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
writer.update_set(deviceManager->device(), inputSet);
}
vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipeline);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 0, 1, &globalSet, 0, nullptr);
vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, _pipelineLayout, 1, 1, &inputSet, 0, nullptr);
VkExtent2D extent = ctxLocal->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);
}

49
src/render/passes/ssr.h Normal file
View File

@@ -0,0 +1,49 @@
#pragma once
#include "render/renderpass.h"
#include "render/graph/types.h"
class RenderGraph;
class RGPassResources;
// Screen Space Reflections (SSR) pass.
// In v1 this is a lightweight stub wired into the RenderGraph by the engine.
// The full pipeline and shader implementation is added in later plan steps.
class SSRPass : public IRenderPass
{
public:
void init(EngineContext *context) override;
void cleanup() override;
void execute(VkCommandBuffer cmd) override;
const char *getName() const override { return "SSR"; }
// Register SSR in the render graph.
// hdrInput : HDR color buffer produced by deferred lighting.
// gbufPos : G-Buffer world-space position (RGBA32F).
// gbufNorm : G-Buffer world-space normal + roughness.
// gbufAlbedo : G-Buffer albedo + metallic.
// hdrOutput : HDR color buffer that will carry lighting + SSR.
void register_graph(RenderGraph *graph,
RGImageHandle hdrInput,
RGImageHandle gbufPos,
RGImageHandle gbufNorm,
RGImageHandle gbufAlbedo,
RGImageHandle hdrOutput);
private:
EngineContext *_context = nullptr;
VkDescriptorSetLayout _inputSetLayout = VK_NULL_HANDLE; // set=1: HDR + GBuffer inputs
VkPipeline _pipeline = VK_NULL_HANDLE;
VkPipelineLayout _pipelineLayout = VK_NULL_HANDLE;
void draw_ssr(VkCommandBuffer cmd,
EngineContext *context,
const class RGPassResources &resources,
RGImageHandle hdrInput,
RGImageHandle gbufPos,
RGImageHandle gbufNorm,
RGImageHandle gbufAlbedo);
DeletionQueue _deletionQueue;
};

View File

@@ -4,6 +4,7 @@
#include "passes/geometry.h"
#include "passes/imgui_pass.h"
#include "passes/lighting.h"
#include "passes/ssr.h"
#include "passes/transparent.h"
#include "passes/tonemap.h"
#include "passes/shadow.h"
@@ -29,6 +30,11 @@ void RenderPassManager::init(EngineContext *context)
lightingPass->init(context);
addPass(std::move(lightingPass));
// Screen Space Reflections pass (wired between lighting and transparent)
auto ssrPass = std::make_unique<SSRPass>();
ssrPass->init(context);
addPass(std::move(ssrPass));
auto transparentPass = std::make_unique<TransparentPass>();
transparentPass->init(context);
addPass(std::move(transparentPass));