ADD: spot light

This commit is contained in:
2025-12-20 23:43:34 +09:00
parent 9ebc01b3a9
commit 0ec865e0ee
15 changed files with 585 additions and 5 deletions

View File

@@ -66,7 +66,7 @@ inline constexpr float kShadowDepthBiasSlope = 1.5f;
// Texture streaming / VRAM budget configuration
// Fraction of total device-local VRAM reserved for streamed textures.
// The remaining budget is left for attachments, swapchain images, meshes, AS, etc.
inline constexpr double kTextureBudgetFraction = 0.35;
inline constexpr double kTextureBudgetFraction = 0.7;
// Fallback texture budget in bytes when Vulkan memory properties are unavailable.
inline constexpr size_t kTextureBudgetFallbackBytes = 512ull * 1024ull * 1024ull;
// Minimum texture budget clamp in bytes.

View File

@@ -1267,7 +1267,7 @@ void VulkanEngine::draw()
get_current_frame()._renderSemaphore);
VkSubmitInfo2 submit = vkinit::submit_info(&cmdinfo, &signalInfo, &waitInfo);
//------------
VK_CHECK(vkQueueSubmit2(_deviceManager->graphicsQueue(), 1, &submit, get_current_frame()._renderFence));
VkPresentInfoKHR presentInfo = vkinit::present_info();

View File

@@ -1425,6 +1425,115 @@ namespace
sceneMgr->clearPointLights();
selectedLight = -1;
}
// Spot light editor
ImGui::Separator();
ImGui::TextUnformatted("Spot lights");
const auto &spotLights = sceneMgr->getSpotLights();
ImGui::Text("Active spot lights: %zu", spotLights.size());
static int selectedSpot = -1;
if (selectedSpot >= static_cast<int>(spotLights.size()))
{
selectedSpot = static_cast<int>(spotLights.size()) - 1;
}
if (ImGui::BeginListBox("Spot light list##spot_list"))
{
for (size_t i = 0; i < spotLights.size(); ++i)
{
std::string label = fmt::format("Spot {}", i);
const bool isSelected = (selectedSpot == static_cast<int>(i));
if (ImGui::Selectable(label.c_str(), isSelected))
{
selectedSpot = static_cast<int>(i);
}
}
ImGui::EndListBox();
}
if (selectedSpot >= 0 && selectedSpot < static_cast<int>(spotLights.size()))
{
SceneManager::SpotLight sl{};
if (sceneMgr->getSpotLight(static_cast<size_t>(selectedSpot), sl))
{
double pos[3] = {sl.position_world.x, sl.position_world.y, sl.position_world.z};
float dir[3] = {sl.direction.x, sl.direction.y, sl.direction.z};
float col[3] = {sl.color.r, sl.color.g, sl.color.b};
bool changed = false;
changed |= ImGui::InputScalarN("Position (world)##spot_pos", ImGuiDataType_Double, pos, 3, nullptr, nullptr, "%.3f");
changed |= ImGui::InputFloat3("Direction##spot_dir", dir, "%.3f");
changed |= ImGui::SliderFloat("Radius##spot_radius", &sl.radius, 0.1f, 1000.0f);
changed |= ImGui::SliderFloat("Inner angle (deg)##spot_inner", &sl.inner_angle_deg, 0.0f, 89.0f);
changed |= ImGui::SliderFloat("Outer angle (deg)##spot_outer", &sl.outer_angle_deg, 0.0f, 89.9f);
changed |= ImGui::ColorEdit3("Color##spot_color", col);
changed |= ImGui::SliderFloat("Intensity##spot_intensity", &sl.intensity, 0.0f, 100.0f);
if (changed)
{
sl.position_world = WorldVec3(pos[0], pos[1], pos[2]);
glm::vec3 d{dir[0], dir[1], dir[2]};
sl.direction = (glm::length(d) > 1.0e-6f) ? glm::normalize(d) : glm::vec3(0.0f, -1.0f, 0.0f);
sl.color = glm::vec3(col[0], col[1], col[2]);
sl.inner_angle_deg = std::clamp(sl.inner_angle_deg, 0.0f, 89.0f);
sl.outer_angle_deg = std::clamp(sl.outer_angle_deg, sl.inner_angle_deg, 89.9f);
sceneMgr->setSpotLight(static_cast<size_t>(selectedSpot), sl);
}
if (ImGui::Button("Remove selected spot light##spot_remove"))
{
sceneMgr->removeSpotLight(static_cast<size_t>(selectedSpot));
selectedSpot = -1;
}
}
}
ImGui::Separator();
ImGui::TextUnformatted("Add spot light");
static double newSpotPos[3] = {0.0, 2.0, 0.0};
static float newSpotDir[3] = {0.0f, -1.0f, 0.0f};
static float newSpotRadius = 10.0f;
static float newSpotInner = 15.0f;
static float newSpotOuter = 25.0f;
static float newSpotColor[3] = {1.0f, 1.0f, 1.0f};
static float newSpotIntensity = 10.0f;
ImGui::InputScalarN("New position (world)##spot_new_pos", ImGuiDataType_Double, newSpotPos, 3, nullptr, nullptr, "%.3f");
ImGui::InputFloat3("New direction##spot_new_dir", newSpotDir, "%.3f");
ImGui::SliderFloat("New radius##spot_new_radius", &newSpotRadius, 0.1f, 1000.0f);
ImGui::SliderFloat("New inner angle (deg)##spot_new_inner", &newSpotInner, 0.0f, 89.0f);
ImGui::SliderFloat("New outer angle (deg)##spot_new_outer", &newSpotOuter, 0.0f, 89.9f);
if (newSpotInner > newSpotOuter)
{
newSpotOuter = newSpotInner;
}
ImGui::ColorEdit3("New color##spot_new_color", newSpotColor);
ImGui::SliderFloat("New intensity##spot_new_intensity", &newSpotIntensity, 0.0f, 100.0f);
if (ImGui::Button("Add spot light##spot_add"))
{
SceneManager::SpotLight sl{};
sl.position_world = WorldVec3(newSpotPos[0], newSpotPos[1], newSpotPos[2]);
glm::vec3 d{newSpotDir[0], newSpotDir[1], newSpotDir[2]};
sl.direction = (glm::length(d) > 1.0e-6f) ? glm::normalize(d) : glm::vec3(0.0f, -1.0f, 0.0f);
sl.radius = newSpotRadius;
sl.color = glm::vec3(newSpotColor[0], newSpotColor[1], newSpotColor[2]);
sl.intensity = newSpotIntensity;
sl.inner_angle_deg = std::clamp(newSpotInner, 0.0f, 89.0f);
sl.outer_angle_deg = std::clamp(newSpotOuter, sl.inner_angle_deg, 89.9f);
const size_t oldCount = sceneMgr->getSpotLightCount();
sceneMgr->addSpotLight(sl);
selectedSpot = static_cast<int>(oldCount);
}
if (ImGui::Button("Clear all spot lights##spot_clear"))
{
sceneMgr->clearSpotLights();
selectedSpot = -1;
}
}
ImGui::Separator();

View File

@@ -796,6 +796,142 @@ void Engine::clear_point_lights()
}
}
// ----------------------------------------------------------------------------
// Lighting - Spot Lights
// ----------------------------------------------------------------------------
size_t Engine::add_spot_light(const SpotLight& light)
{
if (!_engine->_sceneManager) return 0;
SceneManager::SpotLight sl;
sl.position_world = WorldVec3(light.position);
sl.direction = (glm::length(light.direction) > 1.0e-6f)
? glm::normalize(light.direction)
: glm::vec3(0.0f, -1.0f, 0.0f);
sl.radius = light.radius;
sl.color = light.color;
sl.intensity = light.intensity;
sl.inner_angle_deg = light.inner_angle_deg;
sl.outer_angle_deg = light.outer_angle_deg;
size_t idx = _engine->_sceneManager->getSpotLightCount();
_engine->_sceneManager->addSpotLight(sl);
return idx;
}
size_t Engine::add_spot_light(const SpotLightD& light)
{
if (!_engine->_sceneManager) return 0;
SceneManager::SpotLight sl;
sl.position_world = WorldVec3(light.position);
sl.direction = (glm::length(light.direction) > 1.0e-6f)
? glm::normalize(light.direction)
: glm::vec3(0.0f, -1.0f, 0.0f);
sl.radius = light.radius;
sl.color = light.color;
sl.intensity = light.intensity;
sl.inner_angle_deg = light.inner_angle_deg;
sl.outer_angle_deg = light.outer_angle_deg;
size_t idx = _engine->_sceneManager->getSpotLightCount();
_engine->_sceneManager->addSpotLight(sl);
return idx;
}
bool Engine::remove_spot_light(size_t index)
{
return _engine->_sceneManager ? _engine->_sceneManager->removeSpotLight(index) : false;
}
bool Engine::get_spot_light(size_t index, SpotLight& out) const
{
if (!_engine->_sceneManager) return false;
SceneManager::SpotLight sl;
if (_engine->_sceneManager->getSpotLight(index, sl))
{
out.position = glm::vec3(sl.position_world);
out.direction = sl.direction;
out.radius = sl.radius;
out.color = sl.color;
out.intensity = sl.intensity;
out.inner_angle_deg = sl.inner_angle_deg;
out.outer_angle_deg = sl.outer_angle_deg;
return true;
}
return false;
}
bool Engine::get_spot_light(size_t index, SpotLightD& out) const
{
if (!_engine->_sceneManager) return false;
SceneManager::SpotLight sl;
if (_engine->_sceneManager->getSpotLight(index, sl))
{
out.position = sl.position_world;
out.direction = sl.direction;
out.radius = sl.radius;
out.color = sl.color;
out.intensity = sl.intensity;
out.inner_angle_deg = sl.inner_angle_deg;
out.outer_angle_deg = sl.outer_angle_deg;
return true;
}
return false;
}
bool Engine::set_spot_light(size_t index, const SpotLight& light)
{
if (!_engine->_sceneManager) return false;
SceneManager::SpotLight sl;
sl.position_world = WorldVec3(light.position);
sl.direction = (glm::length(light.direction) > 1.0e-6f)
? glm::normalize(light.direction)
: glm::vec3(0.0f, -1.0f, 0.0f);
sl.radius = light.radius;
sl.color = light.color;
sl.intensity = light.intensity;
sl.inner_angle_deg = light.inner_angle_deg;
sl.outer_angle_deg = light.outer_angle_deg;
return _engine->_sceneManager->setSpotLight(index, sl);
}
bool Engine::set_spot_light(size_t index, const SpotLightD& light)
{
if (!_engine->_sceneManager) return false;
SceneManager::SpotLight sl;
sl.position_world = WorldVec3(light.position);
sl.direction = (glm::length(light.direction) > 1.0e-6f)
? glm::normalize(light.direction)
: glm::vec3(0.0f, -1.0f, 0.0f);
sl.radius = light.radius;
sl.color = light.color;
sl.intensity = light.intensity;
sl.inner_angle_deg = light.inner_angle_deg;
sl.outer_angle_deg = light.outer_angle_deg;
return _engine->_sceneManager->setSpotLight(index, sl);
}
size_t Engine::get_spot_light_count() const
{
return _engine->_sceneManager ? _engine->_sceneManager->getSpotLightCount() : 0;
}
void Engine::clear_spot_lights()
{
if (_engine->_sceneManager)
{
_engine->_sceneManager->clearSpotLights();
}
}
// ----------------------------------------------------------------------------
// Post Processing - FXAA
// ----------------------------------------------------------------------------

View File

@@ -69,6 +69,30 @@ struct PointLightD
float intensity{1.0f};
};
// Spot light data (cone half-angles in degrees; inner <= outer)
struct SpotLight
{
glm::vec3 position{0.0f};
glm::vec3 direction{0.0f, -1.0f, 0.0f};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
float inner_angle_deg{15.0f};
float outer_angle_deg{25.0f};
};
// Double-precision world-space spot light data (position only).
struct SpotLightD
{
glm::dvec3 position{0.0};
glm::vec3 direction{0.0f, -1.0f, 0.0f};
float radius{10.0f};
glm::vec3 color{1.0f};
float intensity{1.0f};
float inner_angle_deg{15.0f};
float outer_angle_deg{25.0f};
};
// IBL (Image-Based Lighting) paths
struct IBLPaths
{
@@ -333,6 +357,29 @@ public:
// Clear all point lights
void clear_point_lights();
// ------------------------------------------------------------------------
// Lighting - Spot Lights
// ------------------------------------------------------------------------
// Add spot light (returns index)
size_t add_spot_light(const SpotLight& light);
size_t add_spot_light(const SpotLightD& light);
// Remove spot light by index
bool remove_spot_light(size_t index);
// Get/set spot light properties
bool get_spot_light(size_t index, SpotLight& out) const;
bool set_spot_light(size_t index, const SpotLight& light);
bool get_spot_light(size_t index, SpotLightD& out) const;
bool set_spot_light(size_t index, const SpotLightD& light);
// Get spot light count
size_t get_spot_light_count() const;
// Clear all spot lights
void clear_spot_lights();
// ------------------------------------------------------------------------
// Post Processing - FXAA
// ------------------------------------------------------------------------

View File

@@ -117,6 +117,15 @@ struct GPUPunctualLight {
static constexpr uint32_t kMaxPunctualLights = 64;
struct GPUSpotLight {
glm::vec4 position_radius; // xyz: position (local), w: radius
glm::vec4 direction_cos_outer; // xyz: direction (unit), w: cos(outer_angle)
glm::vec4 color_intensity; // rgb: color, a: intensity
glm::vec4 cone; // x: cos(inner_angle), yzw: unused
};
static constexpr uint32_t kMaxSpotLights = 32;
struct GPUSceneData {
glm::mat4 view;
glm::mat4 proj;
@@ -139,6 +148,7 @@ struct GPUSceneData {
glm::vec4 rtParams;
GPUPunctualLight punctualLights[kMaxPunctualLights];
GPUSpotLight spotLights[kMaxSpotLights];
glm::uvec4 lightCounts;
};

View File

@@ -67,6 +67,46 @@ bool SceneManager::removePointLight(size_t index)
return true;
}
void SceneManager::addSpotLight(const SpotLight &light)
{
spotLights.push_back(light);
}
void SceneManager::clearSpotLights()
{
spotLights.clear();
}
bool SceneManager::getSpotLight(size_t index, SpotLight &outLight) const
{
if (index >= spotLights.size())
{
return false;
}
outLight = spotLights[index];
return true;
}
bool SceneManager::setSpotLight(size_t index, const SpotLight &light)
{
if (index >= spotLights.size())
{
return false;
}
spotLights[index] = light;
return true;
}
bool SceneManager::removeSpotLight(size_t index)
{
if (index >= spotLights.size())
{
return false;
}
spotLights.erase(spotLights.begin() + index);
return true;
}
void SceneManager::init(EngineContext *context)
{
_context = context;
@@ -414,7 +454,45 @@ void SceneManager::update_scene()
sceneData.punctualLights[i].position_radius = glm::vec4(0.0f);
sceneData.punctualLights[i].color_intensity = glm::vec4(0.0f);
}
sceneData.lightCounts = glm::uvec4(lightCount, 0u, 0u, 0u);
// Fill spot lights into GPUSceneData
const uint32_t spotCount = static_cast<uint32_t>(std::min(spotLights.size(), static_cast<size_t>(kMaxSpotLights)));
for (uint32_t i = 0; i < spotCount; ++i)
{
const SpotLight &sl = spotLights[i];
glm::vec3 posLocal = world_to_local(sl.position_world, _origin_world);
glm::vec3 dir = sl.direction;
const float dirLen2 = glm::length2(dir);
if (dirLen2 > 1.0e-8f)
{
dir *= 1.0f / std::sqrt(dirLen2);
}
else
{
dir = glm::vec3(0.0f, -1.0f, 0.0f);
}
const float radius = std::max(sl.radius, 0.0001f);
const float innerDeg = std::clamp(sl.inner_angle_deg, 0.0f, 89.0f);
const float outerDeg = std::clamp(sl.outer_angle_deg, innerDeg, 89.9f);
const float cosInner = glm::cos(glm::radians(innerDeg));
const float cosOuter = glm::cos(glm::radians(outerDeg));
sceneData.spotLights[i].position_radius = glm::vec4(posLocal, radius);
sceneData.spotLights[i].direction_cos_outer = glm::vec4(dir, cosOuter);
sceneData.spotLights[i].color_intensity = glm::vec4(sl.color, sl.intensity);
sceneData.spotLights[i].cone = glm::vec4(cosInner, 0.0f, 0.0f, 0.0f);
}
for (uint32_t i = spotCount; i < kMaxSpotLights; ++i)
{
sceneData.spotLights[i].position_radius = glm::vec4(0.0f);
sceneData.spotLights[i].direction_cos_outer = glm::vec4(0.0f);
sceneData.spotLights[i].color_intensity = glm::vec4(0.0f);
sceneData.spotLights[i].cone = glm::vec4(0.0f);
}
sceneData.lightCounts = glm::uvec4(lightCount, spotCount, 0u, 0u);
auto end = std::chrono::system_clock::now();
auto elapsed = std::chrono::duration_cast<std::chrono::microseconds>(end - start);

View File

@@ -184,6 +184,26 @@ public:
bool removePointLight(size_t index);
const std::vector<PointLight> &getPointLights() const { return pointLights; }
struct SpotLight
{
WorldVec3 position_world;
glm::vec3 direction{0.0f, -1.0f, 0.0f}; // world-space unit vector
float radius = 10.0f;
glm::vec3 color{1.0f, 1.0f, 1.0f};
float intensity = 1.0f;
// Cone half-angles in degrees (inner <= outer).
float inner_angle_deg = 15.0f;
float outer_angle_deg = 25.0f;
};
void addSpotLight(const SpotLight &light);
void clearSpotLights();
size_t getSpotLightCount() const { return spotLights.size(); }
bool getSpotLight(size_t index, SpotLight &outLight) const;
bool setSpotLight(size_t index, const SpotLight &light);
bool removeSpotLight(size_t index);
const std::vector<SpotLight> &getSpotLights() const { return spotLights; }
struct SceneStats
{
float scene_update_time = 0.f;
@@ -210,6 +230,7 @@ private:
GPUSceneData sceneData = {};
DrawContext mainDrawContext;
std::vector<PointLight> pointLights;
std::vector<SpotLight> spotLights;
WorldVec3 _origin_world{0.0, 0.0, 0.0};
glm::vec3 _camera_position_local{0.0f, 0.0f, 0.0f};
double _floating_origin_recenter_threshold = 1000.0;