ADD: Lighting modifier and IBL probe
This commit is contained in:
@@ -1,7 +0,0 @@
|
||||
[requires]
|
||||
taskflow/3.10.0
|
||||
[generators]
|
||||
CMakeDeps
|
||||
CMakeToolchain
|
||||
[layout]
|
||||
cmake_layout
|
||||
@@ -16,6 +16,8 @@ void main()
|
||||
vec3 worldDir = normalize((inverse(sceneData.view) * vec4(viewDir, 0.0)).xyz);
|
||||
|
||||
vec2 uv = dir_to_equirect(worldDir);
|
||||
vec3 col = textureLod(iblSpec2D, uv, 0.0).rgb;
|
||||
// Sample a dedicated background environment map when available.
|
||||
// The engine binds iblBackground2D to a texture that may differ from the IBL specular map.
|
||||
vec3 col = textureLod(iblBackground2D, uv, 0.0).rgb;
|
||||
outColor = vec4(col, 1.0);
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
#ifndef IBL_COMMON_GLSL
|
||||
#define IBL_COMMON_GLSL
|
||||
|
||||
// IBL bindings (set=3): specular equirect 2D, BRDF LUT, SH UBO.
|
||||
// IBL bindings (set=3): specular equirect 2D, BRDF LUT, SH UBO, optional background map.
|
||||
layout(set=3, binding=0) uniform sampler2D iblSpec2D;
|
||||
layout(set=3, binding=1) uniform sampler2D iblBRDF;
|
||||
layout(std140, set=3, binding=2) uniform IBL_SH { vec4 sh[9]; } iblSH;
|
||||
layout(set=3, binding=3) uniform sampler2D iblBackground2D;
|
||||
|
||||
// Evaluate diffuse irradiance from 2nd-order SH coefficients (9 coeffs).
|
||||
// Coefficients are pre-convolved with the Lambert kernel on the CPU.
|
||||
|
||||
@@ -10,14 +10,29 @@
|
||||
#include <SDL_stdinc.h>
|
||||
|
||||
#include "core/device/device.h"
|
||||
#include "core/assets/texture_cache.h"
|
||||
|
||||
bool IBLManager::load(const IBLPaths &paths)
|
||||
{
|
||||
if (_ctx == nullptr || _ctx->getResources() == nullptr) return false;
|
||||
ensureLayout();
|
||||
ResourceManager *rm = _ctx->getResources();
|
||||
|
||||
// Load specular environment: prefer cubemap; fallback to 2D equirect with mips
|
||||
// When uploads are deferred into the RenderGraph, any previously queued
|
||||
// image uploads might still reference VkImage handles owned by this
|
||||
// manager. Before destroying or recreating IBL images, flush those
|
||||
// uploads via the immediate path so we never record barriers or copies
|
||||
// for images that have been destroyed.
|
||||
if (rm->deferred_uploads() && rm->has_pending_uploads())
|
||||
{
|
||||
rm->process_queued_uploads_immediate();
|
||||
}
|
||||
|
||||
// Allow reloading at runtime: destroy previous images/SH but keep layout.
|
||||
destroy_images_and_sh();
|
||||
ensureLayout();
|
||||
|
||||
// Load specular environment: prefer cubemap; fallback to 2D equirect with mips.
|
||||
// Also hint the TextureCache (if present) so future switches are cheap.
|
||||
if (!paths.specularCube.empty())
|
||||
{
|
||||
// Try as cubemap first
|
||||
@@ -222,6 +237,12 @@ bool IBLManager::load(const IBLPaths &paths)
|
||||
_diff = _spec;
|
||||
}
|
||||
|
||||
// If background is still missing but specular is valid, reuse the specular environment.
|
||||
if (_background.image == VK_NULL_HANDLE && _spec.image != VK_NULL_HANDLE)
|
||||
{
|
||||
_background = _spec;
|
||||
}
|
||||
|
||||
// BRDF LUT
|
||||
if (!paths.brdfLut2D.empty())
|
||||
{
|
||||
@@ -251,34 +272,16 @@ bool IBLManager::load(const IBLPaths &paths)
|
||||
void IBLManager::unload()
|
||||
{
|
||||
if (_ctx == nullptr || _ctx->getResources() == nullptr) return;
|
||||
auto *rm = _ctx->getResources();
|
||||
if (_spec.image)
|
||||
{
|
||||
rm->destroy_image(_spec);
|
||||
}
|
||||
// Handle potential aliasing: _diff may have been set to _spec in load().
|
||||
if (_diff.image && _diff.image != _spec.image)
|
||||
{
|
||||
rm->destroy_image(_diff);
|
||||
}
|
||||
if (_brdf.image)
|
||||
{
|
||||
rm->destroy_image(_brdf);
|
||||
}
|
||||
|
||||
_spec = {};
|
||||
_diff = {};
|
||||
_brdf = {};
|
||||
// Destroy images and SH buffer first.
|
||||
destroy_images_and_sh();
|
||||
|
||||
// Then release descriptor layout.
|
||||
if (_iblSetLayout && _ctx && _ctx->getDevice())
|
||||
{
|
||||
vkDestroyDescriptorSetLayout(_ctx->getDevice()->device(), _iblSetLayout, nullptr);
|
||||
_iblSetLayout = VK_NULL_HANDLE;
|
||||
}
|
||||
if (_shBuffer.buffer)
|
||||
{
|
||||
rm->destroy_buffer(_shBuffer);
|
||||
_shBuffer = {};
|
||||
}
|
||||
}
|
||||
|
||||
bool IBLManager::ensureLayout()
|
||||
@@ -293,8 +296,48 @@ bool IBLManager::ensureLayout()
|
||||
builder.add_binding(1, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
// binding 2: SH coefficients UBO (vec4[9])
|
||||
builder.add_binding(2, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER);
|
||||
// binding 3: optional background environment texture (2D equirect)
|
||||
builder.add_binding(3, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
_iblSetLayout = builder.build(
|
||||
_ctx->getDevice()->device(), VK_SHADER_STAGE_FRAGMENT_BIT,
|
||||
nullptr, VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT);
|
||||
return _iblSetLayout != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
void IBLManager::destroy_images_and_sh()
|
||||
{
|
||||
if (_ctx == nullptr || _ctx->getResources() == nullptr) return;
|
||||
auto *rm = _ctx->getResources();
|
||||
|
||||
if (_spec.image)
|
||||
{
|
||||
rm->destroy_image(_spec);
|
||||
}
|
||||
// Handle potential aliasing: _diff may have been set to _spec in load().
|
||||
if (_diff.image && _diff.image != _spec.image)
|
||||
{
|
||||
rm->destroy_image(_diff);
|
||||
}
|
||||
// _background may alias _spec or _diff; only destroy when unique.
|
||||
if (_background.image &&
|
||||
_background.image != _spec.image &&
|
||||
_background.image != _diff.image)
|
||||
{
|
||||
rm->destroy_image(_background);
|
||||
}
|
||||
if (_brdf.image)
|
||||
{
|
||||
rm->destroy_image(_brdf);
|
||||
}
|
||||
|
||||
if (_shBuffer.buffer)
|
||||
{
|
||||
rm->destroy_buffer(_shBuffer);
|
||||
_shBuffer = {};
|
||||
}
|
||||
|
||||
_spec = {};
|
||||
_diff = {};
|
||||
_background = {};
|
||||
_brdf = {};
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
#include <core/types.h>
|
||||
#include <string>
|
||||
|
||||
class TextureCache;
|
||||
|
||||
class EngineContext;
|
||||
|
||||
struct IBLPaths
|
||||
@@ -10,6 +12,9 @@ struct IBLPaths
|
||||
std::string specularCube; // .ktx2 (GPU-ready BC6H or R16G16B16A16)
|
||||
std::string diffuseCube; // .ktx2
|
||||
std::string brdfLut2D; // .ktx2 (BC5 RG UNORM or similar)
|
||||
// Optional separate background environment map (2D equirect .ktx2).
|
||||
// When empty, the IBL system falls back to using specularCube for the background.
|
||||
std::string background2D;
|
||||
};
|
||||
|
||||
class IBLManager
|
||||
@@ -17,6 +22,8 @@ class IBLManager
|
||||
public:
|
||||
void init(EngineContext *ctx) { _ctx = ctx; }
|
||||
|
||||
void set_texture_cache(TextureCache *cache) { _cache = cache; }
|
||||
|
||||
// Load all three textures. Returns true when specular+diffuse (and optional LUT) are resident.
|
||||
bool load(const IBLPaths &paths);
|
||||
|
||||
@@ -28,6 +35,9 @@ public:
|
||||
AllocatedImage specular() const { return _spec; }
|
||||
AllocatedImage diffuse() const { return _diff; }
|
||||
AllocatedImage brdf() const { return _brdf; }
|
||||
// Background environment texture used by the background pass.
|
||||
// May alias specular() when a dedicated background is not provided.
|
||||
AllocatedImage background() const { return _background; }
|
||||
AllocatedBuffer shBuffer() const { return _shBuffer; }
|
||||
bool hasSH() const { return _shBuffer.buffer != VK_NULL_HANDLE; }
|
||||
|
||||
@@ -39,9 +49,14 @@ public:
|
||||
|
||||
private:
|
||||
EngineContext *_ctx{nullptr};
|
||||
TextureCache *_cache{nullptr};
|
||||
AllocatedImage _spec{};
|
||||
AllocatedImage _diff{};
|
||||
AllocatedImage _brdf{};
|
||||
AllocatedImage _background{};
|
||||
VkDescriptorSetLayout _iblSetLayout = VK_NULL_HANDLE;
|
||||
AllocatedBuffer _shBuffer{}; // 9*vec4 coefficients (RGB in .xyz)
|
||||
|
||||
// Destroy current GPU images/SH buffer but keep descriptor layout alive.
|
||||
void destroy_images_and_sh();
|
||||
};
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <array>
|
||||
|
||||
#include <iostream>
|
||||
#include <cmath>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
|
||||
#include "config.h"
|
||||
@@ -229,6 +230,10 @@ void VulkanEngine::init()
|
||||
// Create IBL manager early so set=3 layout exists before pipelines are built
|
||||
_iblManager = std::make_unique<IBLManager>();
|
||||
_iblManager->init(_context.get());
|
||||
if (_textureCache)
|
||||
{
|
||||
_iblManager->set_texture_cache(_textureCache.get());
|
||||
}
|
||||
// Publish to context for passes and pipeline layout assembly
|
||||
_context->ibl = _iblManager.get();
|
||||
|
||||
@@ -238,6 +243,13 @@ void VulkanEngine::init()
|
||||
ibl.specularCube = _assetManager->assetPath("ibl/docklands.ktx2");
|
||||
ibl.diffuseCube = _assetManager->assetPath("ibl/docklands.ktx2"); // temporary: reuse if separate diffuse not provided
|
||||
ibl.brdfLut2D = _assetManager->assetPath("ibl/brdf_lut.ktx2");
|
||||
// By default, use the same texture for lighting and background; users can point background2D
|
||||
// at a different .ktx2 to decouple them.
|
||||
ibl.background2D = ibl.specularCube;
|
||||
// Treat this as the global/fallback IBL used outside any local volume.
|
||||
_globalIBLPaths = ibl;
|
||||
_hasGlobalIBL = true;
|
||||
_activeIBLVolume = -1;
|
||||
_iblManager->load(ibl);
|
||||
}
|
||||
|
||||
@@ -469,6 +481,44 @@ void VulkanEngine::draw()
|
||||
{
|
||||
_sceneManager->update_scene();
|
||||
|
||||
// Update IBL based on camera position and user-defined reflection volumes.
|
||||
if (_iblManager && _sceneManager)
|
||||
{
|
||||
glm::vec3 camPos = _sceneManager->getMainCamera().position;
|
||||
int newVolume = -1;
|
||||
for (size_t i = 0; i < _iblVolumes.size(); ++i)
|
||||
{
|
||||
const IBLVolume &v = _iblVolumes[i];
|
||||
if (!v.enabled) continue;
|
||||
glm::vec3 local = camPos - v.center;
|
||||
if (std::abs(local.x) <= v.halfExtents.x &&
|
||||
std::abs(local.y) <= v.halfExtents.y &&
|
||||
std::abs(local.z) <= v.halfExtents.z)
|
||||
{
|
||||
newVolume = static_cast<int>(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (newVolume != _activeIBLVolume)
|
||||
{
|
||||
const IBLPaths *paths = nullptr;
|
||||
if (newVolume >= 0)
|
||||
{
|
||||
paths = &_iblVolumes[newVolume].paths;
|
||||
}
|
||||
else if (_hasGlobalIBL)
|
||||
{
|
||||
paths = &_globalIBLPaths;
|
||||
}
|
||||
|
||||
if (paths)
|
||||
{
|
||||
_iblManager->load(*paths);
|
||||
}
|
||||
_activeIBLVolume = newVolume;
|
||||
}
|
||||
}
|
||||
|
||||
// Per-frame hover raycast based on last mouse position.
|
||||
if (_sceneManager && _mousePosPixels.x >= 0.0f && _mousePosPixels.y >= 0.0f)
|
||||
{
|
||||
@@ -558,6 +608,8 @@ void VulkanEngine::draw()
|
||||
RGImageHandle hGBufferNormal = _renderGraph->import_gbuffer_normal();
|
||||
RGImageHandle hGBufferAlbedo = _renderGraph->import_gbuffer_albedo();
|
||||
RGImageHandle hSwapchain = _renderGraph->import_swapchain_image(swapchainImageIndex);
|
||||
// For debug overlays (IBL volumes), re-use HDR draw image as a color target.
|
||||
RGImageHandle hDebugColor = hDraw;
|
||||
|
||||
// Create transient depth targets for cascaded shadow maps (even if RT-only, to keep descriptors stable)
|
||||
const VkExtent2D shadowExtent{2048, 2048};
|
||||
|
||||
@@ -116,6 +116,21 @@ public:
|
||||
// Debug helpers: track spawned IBL test meshes to remove them easily
|
||||
std::vector<std::string> _iblTestNames;
|
||||
|
||||
// Simple world-space IBL reflection volumes (axis-aligned boxes).
|
||||
struct IBLVolume
|
||||
{
|
||||
glm::vec3 center{0.0f, 0.0f, 0.0f};
|
||||
glm::vec3 halfExtents{10.0f, 10.0f, 10.0f};
|
||||
IBLPaths paths{}; // HDRI paths for this volume
|
||||
bool enabled{true};
|
||||
};
|
||||
// Global/default IBL used when no volume contains the camera.
|
||||
IBLPaths _globalIBLPaths{};
|
||||
bool _hasGlobalIBL{false};
|
||||
// User-defined local IBL volumes and currently active index (-1 = global).
|
||||
std::vector<IBLVolume> _iblVolumes;
|
||||
int _activeIBLVolume{-1};
|
||||
|
||||
struct PickInfo
|
||||
{
|
||||
MeshAsset *mesh = nullptr;
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "core/assets/ibl_manager.h"
|
||||
#include "context.h"
|
||||
#include <core/types.h>
|
||||
#include <cstring>
|
||||
|
||||
#include "mesh_bvh.h"
|
||||
|
||||
@@ -138,11 +139,102 @@ namespace
|
||||
static void ui_ibl(VulkanEngine *eng)
|
||||
{
|
||||
if (!eng) return;
|
||||
|
||||
if (ImGui::Button("Spawn IBL Test Grid")) { spawn_ibl_test(eng); }
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Clear IBL Test")) { clear_ibl_test(eng); }
|
||||
ImGui::TextUnformatted(
|
||||
"5x5 spheres: metallic across columns, roughness across rows.\nExtra: chrome + glass.");
|
||||
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("IBL Volumes (reflection probes)");
|
||||
|
||||
if (!eng->_iblManager)
|
||||
{
|
||||
ImGui::TextUnformatted("IBLManager not available");
|
||||
return;
|
||||
}
|
||||
|
||||
if (eng->_activeIBLVolume < 0)
|
||||
{
|
||||
ImGui::TextUnformatted("Active IBL: Global");
|
||||
}
|
||||
else
|
||||
{
|
||||
ImGui::Text("Active IBL: Volume %d", eng->_activeIBLVolume);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Add IBL Volume"))
|
||||
{
|
||||
VulkanEngine::IBLVolume vol{};
|
||||
if (eng->_sceneManager)
|
||||
{
|
||||
vol.center = eng->_sceneManager->getMainCamera().position;
|
||||
}
|
||||
vol.halfExtents = glm::vec3(10.0f, 10.0f, 10.0f);
|
||||
vol.paths = eng->_globalIBLPaths;
|
||||
eng->_iblVolumes.push_back(vol);
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < eng->_iblVolumes.size(); ++i)
|
||||
{
|
||||
auto &vol = eng->_iblVolumes[i];
|
||||
ImGui::PushID(static_cast<int>(i));
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Volume %zu", i);
|
||||
ImGui::Checkbox("Enabled", &vol.enabled);
|
||||
ImGui::InputFloat3("Center", &vol.center.x);
|
||||
ImGui::InputFloat3("Half Extents", &vol.halfExtents.x);
|
||||
|
||||
// Simple path editors; store absolute or engine-local paths.
|
||||
char specBuf[256]{};
|
||||
char diffBuf[256]{};
|
||||
char bgBuf[256]{};
|
||||
char brdfBuf[256]{};
|
||||
std::strncpy(specBuf, vol.paths.specularCube.c_str(), sizeof(specBuf) - 1);
|
||||
std::strncpy(diffBuf, vol.paths.diffuseCube.c_str(), sizeof(diffBuf) - 1);
|
||||
std::strncpy(bgBuf, vol.paths.background2D.c_str(), sizeof(bgBuf) - 1);
|
||||
std::strncpy(brdfBuf, vol.paths.brdfLut2D.c_str(), sizeof(brdfBuf) - 1);
|
||||
|
||||
if (ImGui::InputText("Specular path", specBuf, IM_ARRAYSIZE(specBuf)))
|
||||
{
|
||||
vol.paths.specularCube = specBuf;
|
||||
}
|
||||
if (ImGui::InputText("Diffuse path", diffBuf, IM_ARRAYSIZE(diffBuf)))
|
||||
{
|
||||
vol.paths.diffuseCube = diffBuf;
|
||||
}
|
||||
if (ImGui::InputText("Background path", bgBuf, IM_ARRAYSIZE(bgBuf)))
|
||||
{
|
||||
vol.paths.background2D = bgBuf;
|
||||
}
|
||||
if (ImGui::InputText("BRDF LUT path", brdfBuf, IM_ARRAYSIZE(brdfBuf)))
|
||||
{
|
||||
vol.paths.brdfLut2D = brdfBuf;
|
||||
}
|
||||
|
||||
if (ImGui::Button("Reload This Volume IBL"))
|
||||
{
|
||||
if (eng->_iblManager && vol.enabled)
|
||||
{
|
||||
eng->_iblManager->load(vol.paths);
|
||||
eng->_activeIBLVolume = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set As Global IBL"))
|
||||
{
|
||||
eng->_globalIBLPaths = vol.paths;
|
||||
eng->_hasGlobalIBL = true;
|
||||
eng->_activeIBLVolume = -1;
|
||||
if (eng->_iblManager)
|
||||
{
|
||||
eng->_iblManager->load(eng->_globalIBLPaths);
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::PopID();
|
||||
}
|
||||
}
|
||||
|
||||
// Quick stats & targets overview
|
||||
@@ -600,6 +692,99 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
// Point light editor
|
||||
if (eng->_sceneManager)
|
||||
{
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("Point lights");
|
||||
|
||||
SceneManager *sceneMgr = eng->_sceneManager.get();
|
||||
const auto &lights = sceneMgr->getPointLights();
|
||||
ImGui::Text("Active lights: %zu", lights.size());
|
||||
|
||||
static int selectedLight = -1;
|
||||
if (selectedLight >= static_cast<int>(lights.size()))
|
||||
{
|
||||
selectedLight = static_cast<int>(lights.size()) - 1;
|
||||
}
|
||||
|
||||
if (ImGui::BeginListBox("Light list"))
|
||||
{
|
||||
for (size_t i = 0; i < lights.size(); ++i)
|
||||
{
|
||||
std::string label = fmt::format("Light {}", i);
|
||||
const bool isSelected = (selectedLight == static_cast<int>(i));
|
||||
if (ImGui::Selectable(label.c_str(), isSelected))
|
||||
{
|
||||
selectedLight = static_cast<int>(i);
|
||||
}
|
||||
}
|
||||
ImGui::EndListBox();
|
||||
}
|
||||
|
||||
// Controls for the selected light
|
||||
if (selectedLight >= 0 && selectedLight < static_cast<int>(lights.size()))
|
||||
{
|
||||
SceneManager::PointLight pl{};
|
||||
if (sceneMgr->getPointLight(static_cast<size_t>(selectedLight), pl))
|
||||
{
|
||||
float pos[3] = {pl.position.x, pl.position.y, pl.position.z};
|
||||
float col[3] = {pl.color.r, pl.color.g, pl.color.b};
|
||||
bool changed = false;
|
||||
|
||||
changed |= ImGui::InputFloat3("Position", pos);
|
||||
changed |= ImGui::SliderFloat("Radius", &pl.radius, 0.1f, 1000.0f);
|
||||
changed |= ImGui::ColorEdit3("Color", col);
|
||||
changed |= ImGui::SliderFloat("Intensity", &pl.intensity, 0.0f, 100.0f);
|
||||
|
||||
if (changed)
|
||||
{
|
||||
pl.position = glm::vec3(pos[0], pos[1], pos[2]);
|
||||
pl.color = glm::vec3(col[0], col[1], col[2]);
|
||||
sceneMgr->setPointLight(static_cast<size_t>(selectedLight), pl);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Remove selected light"))
|
||||
{
|
||||
sceneMgr->removePointLight(static_cast<size_t>(selectedLight));
|
||||
selectedLight = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Controls for adding a new light
|
||||
ImGui::Separator();
|
||||
ImGui::TextUnformatted("Add point light");
|
||||
static float newPos[3] = {0.0f, 1.0f, 0.0f};
|
||||
static float newRadius = 10.0f;
|
||||
static float newColor[3] = {1.0f, 1.0f, 1.0f};
|
||||
static float newIntensity = 5.0f;
|
||||
|
||||
ImGui::InputFloat3("New position", newPos);
|
||||
ImGui::SliderFloat("New radius", &newRadius, 0.1f, 1000.0f);
|
||||
ImGui::ColorEdit3("New color", newColor);
|
||||
ImGui::SliderFloat("New intensity", &newIntensity, 0.0f, 100.0f);
|
||||
|
||||
if (ImGui::Button("Add point light"))
|
||||
{
|
||||
SceneManager::PointLight pl{};
|
||||
pl.position = glm::vec3(newPos[0], newPos[1], newPos[2]);
|
||||
pl.radius = newRadius;
|
||||
pl.color = glm::vec3(newColor[0], newColor[1], newColor[2]);
|
||||
pl.intensity = newIntensity;
|
||||
|
||||
const size_t oldCount = sceneMgr->getPointLightCount();
|
||||
sceneMgr->addPointLight(pl);
|
||||
selectedLight = static_cast<int>(oldCount);
|
||||
}
|
||||
|
||||
if (ImGui::Button("Clear all lights"))
|
||||
{
|
||||
sceneMgr->clearPointLights();
|
||||
selectedLight = -1;
|
||||
}
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
// Delete selected model/primitive (uses last pick if valid, otherwise hover)
|
||||
static std::string deleteStatus;
|
||||
|
||||
@@ -151,21 +151,37 @@ void BackgroundPass::register_graph(RenderGraph *graph, RGImageHandle drawHandle
|
||||
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);
|
||||
// IBL/background set (set = 3)
|
||||
VkDescriptorSet ibl = VK_NULL_HANDLE;
|
||||
if (ctx->ibl)
|
||||
{
|
||||
VkImageView envView = _fallbackIblCube.imageView;
|
||||
// Prefer a dedicated background texture when available, otherwise reuse specular.
|
||||
if (ctx->ibl->background().imageView)
|
||||
{
|
||||
envView = ctx->ibl->background().imageView;
|
||||
}
|
||||
else if (ctx->ibl->specular().imageView)
|
||||
{
|
||||
envView = ctx->ibl->specular().imageView;
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout iblLayout = ctx->ibl->descriptorLayout();
|
||||
ibl = ctx->currentFrame->_frameDescriptors.allocate(
|
||||
ctx->getDevice()->device(), iblLayout);
|
||||
DescriptorWriter w3;
|
||||
// Bind background map at binding 3; other bindings are unused in this shader.
|
||||
w3.write_image(3, envView, 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);
|
||||
if (ibl != VK_NULL_HANDLE)
|
||||
{
|
||||
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};
|
||||
|
||||
@@ -37,6 +37,36 @@ void SceneManager::clearPointLights()
|
||||
pointLights.clear();
|
||||
}
|
||||
|
||||
bool SceneManager::getPointLight(size_t index, PointLight &outLight) const
|
||||
{
|
||||
if (index >= pointLights.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
outLight = pointLights[index];
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SceneManager::setPointLight(size_t index, const PointLight &light)
|
||||
{
|
||||
if (index >= pointLights.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pointLights[index] = light;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SceneManager::removePointLight(size_t index)
|
||||
{
|
||||
if (index >= pointLights.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pointLights.erase(pointLights.begin() + index);
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneManager::init(EngineContext *context)
|
||||
{
|
||||
_context = context;
|
||||
|
||||
@@ -136,6 +136,10 @@ public:
|
||||
|
||||
void addPointLight(const PointLight &light);
|
||||
void clearPointLights();
|
||||
size_t getPointLightCount() const { return pointLights.size(); }
|
||||
bool getPointLight(size_t index, PointLight &outLight) const;
|
||||
bool setPointLight(size_t index, const PointLight &light);
|
||||
bool removePointLight(size_t index);
|
||||
const std::vector<PointLight> &getPointLights() const { return pointLights; }
|
||||
|
||||
struct SceneStats
|
||||
|
||||
Reference in New Issue
Block a user