diff --git a/Data/Shaders/VPT/VptHeader.glsl b/Data/Shaders/VPT/VptHeader.glsl index b1d30ac..b4eddbb 100644 --- a/Data/Shaders/VPT/VptHeader.glsl +++ b/Data/Shaders/VPT/VptHeader.glsl @@ -254,6 +254,9 @@ struct Light { // Point light & spotlight. vec3 position; ///< POINT & SPOT; distance for DIRECTIONAL. uint useDistance; ///< POINT & SPOT + + vec3 spotDirection; + float padding; }; layout (binding = 31) uniform LightsBuffer { Light lights[NUM_LIGHTS]; diff --git a/Data/Shaders/VPT/VptUtils.glsl b/Data/Shaders/VPT/VptUtils.glsl index f2623b0..d151522 100644 --- a/Data/Shaders/VPT/VptUtils.glsl +++ b/Data/Shaders/VPT/VptUtils.glsl @@ -428,7 +428,11 @@ vec3 sampleHeadlight(vec3 pos, uint lightIdx) { const float falloffStart = light.spotTotalWidth > light.spotFalloffStart ? cos(light.spotFalloffStart) : totalWidth; vec3 p = pos - lightPosition; - vec3 z = light.lightSpace == LIGHT_SPACE_VIEW ? parameters.camForward : normalize(-lightPosition); + //vec3 z = light.lightSpace == LIGHT_SPACE_VIEW ? parameters.camForward : normalize(-lightPosition); + vec3 z = normalize(light.spotDirection); + if (light.lightSpace == LIGHT_SPACE_VIEW || light.lightSpace == LIGHT_SPACE_VIEW_ORIENTATION) { + z = (parameters.inverseViewMatrix * vec4(z, 0.0)).xyz; + } float cosTheta = dot(z, p) / (length(z) * length(p)); float coneFactor = smoothstep(totalWidth, falloffStart, cosTheta); lightFactor *= coneFactor; diff --git a/src/MainApp.cpp b/src/MainApp.cpp index daa6335..c05d76a 100644 --- a/src/MainApp.cpp +++ b/src/MainApp.cpp @@ -126,6 +126,7 @@ MainApp::MainApp() checkpointWindow.setStandardWindowPosition(841, 53); lightEditorWidget = new LightEditorWidget(rendererVk); + lightEditorWidget->setCamera(camera); lightEditorWidget->setShowWindow(false); propertyEditor.setInitWidthValues(sgl::ImGuiWrapper::get()->getScaleDependentSize(280.0f)); diff --git a/src/PathTracer/LightEditorWidget.cpp b/src/PathTracer/LightEditorWidget.cpp index 99b1ad4..176dcd5 100644 --- a/src/PathTracer/LightEditorWidget.cpp +++ b/src/PathTracer/LightEditorWidget.cpp @@ -34,6 +34,7 @@ #include #include #include +#include #include #include #include @@ -231,6 +232,7 @@ bool LightEditorWidget::renderGui() { addLight(light); reRender = true; } + ImGui::SameLine(); if (ImGui::Button("Save to File...")) { fileDialogModeSave = true; openSelectLightFileDialog(); @@ -240,6 +242,7 @@ bool LightEditorWidget::renderGui() { fileDialogModeSave = false; openSelectLightFileDialog(); } + ImGui::Checkbox("Transform on Space Change", &shallTransformOnSpaceChange); } propertyEditor->end(); @@ -295,6 +298,8 @@ void LightEditorWidget::setLightProperty(uint32_t lightIdx, const std::string& k light.spotTotalWidth = sgl::fromString(value); } else if (key == "spot_falloff_start") { light.spotFalloffStart = sgl::fromString(value); + } else if (key == "spot_direction") { + light.spotDirection = stringToVec3(value); } } @@ -386,6 +391,12 @@ bool LightEditorWidget::loadFromFile(const std::string& filePath) { light.spotFalloffStart = lightNode["spot_falloff_start"].asFloat(); } + if (lightNode.isMember("spot_direction")) { + auto spotDirectionNode = lightNode["spot_direction"]; + light.spotDirection = glm::vec3( + spotDirectionNode["x"].asFloat(), spotDirectionNode["y"].asFloat(), spotDirectionNode["z"].asFloat()); + } + newLights.push_back(light); newLightCreationIndices.push_back(newCurrentLightIdx++); } @@ -417,6 +428,11 @@ bool LightEditorWidget::saveToFile(const std::string& filePath) { positionNode["y"] = light.position.y; positionNode["z"] = light.position.z; + Json::Value spotDirectionNode; + spotDirectionNode["x"] = light.spotDirection.x; + spotDirectionNode["y"] = light.spotDirection.y; + spotDirectionNode["z"] = light.spotDirection.z; + Json::Value lightNode; lightNode["type"] = LIGHT_TYPE_NAMES[int(light.lightType)]; lightNode["color"] = colorNode; @@ -426,6 +442,7 @@ bool LightEditorWidget::saveToFile(const std::string& filePath) { lightNode["space"] = LIGHT_SPACE_NAMES[int(light.lightSpace)]; lightNode["spot_total_width"] = light.spotTotalWidth; lightNode["spot_falloff_start"] = light.spotFalloffStart; + lightNode["spot_direction"] = spotDirectionNode; lightsNode.append(lightNode); } root["lights"] = lightsNode; @@ -473,9 +490,14 @@ bool LightEditorWidget::renderGuiLight(size_t lightIdx) { light.lightType == LightType::DIRECTIONAL ? "Direction" : "Position", &light.position.x, -2.0f, 2.0f)) { reRender = true; } + + auto lightSpaceOld = light.lightSpace; if (propertyEditor->addCombo( "Light Space", reinterpret_cast(&light.lightSpace), LIGHT_SPACE_NAMES, IM_ARRAYSIZE(LIGHT_SPACE_NAMES))) { reRender = true; + if (shallTransformOnSpaceChange) { + transformLightSpace(light, lightSpaceOld); + } } if (light.lightType == LightType::SPOT) { @@ -485,7 +507,40 @@ bool LightEditorWidget::renderGuiLight(size_t lightIdx) { if (propertyEditor->addSliderFloat("Falloff Start Angle", (float*)&light.spotFalloffStart, 0.0, sgl::HALF_PI)) { reRender = true; } + if (propertyEditor->addSliderFloat3("Spot Direction", &light.spotDirection.x, -1.0f, 1.0f)) { + reRender = true; + } } return reRender; } + +void LightEditorWidget::transformLightSpace(Light& light, LightSpace lightSpaceOld) { + LightSpace lightSpaceNew = light.lightSpace; + const auto& viewMatrix = camera->getViewMatrix(); + auto inverseViewMatrix = glm::inverse(viewMatrix); + + // Transform old position (or direction) from old to new world space. + glm::vec3 lightPosition = light.position; + if (lightSpaceOld == LightSpace::VIEW || lightSpaceOld == LightSpace::VIEW_ORIENTATION) { + const float homComp = + light.lightType == LightType::DIRECTIONAL || lightSpaceOld == LightSpace::VIEW_ORIENTATION ? 0.0f : 1.0f; + lightPosition = (inverseViewMatrix * glm::vec4(lightPosition, homComp)); + } + if (lightSpaceNew == LightSpace::VIEW || lightSpaceNew == LightSpace::VIEW_ORIENTATION) { + const float homComp = + light.lightType == LightType::DIRECTIONAL || lightSpaceNew == LightSpace::VIEW_ORIENTATION ? 0.0f : 1.0f; + lightPosition = (viewMatrix * glm::vec4(lightPosition, homComp)); + } + light.position = lightPosition; + + // Transform old spot direction from old to new world space. + glm::vec3 spotDirection = light.spotDirection; + if (lightSpaceOld == LightSpace::VIEW || lightSpaceOld == LightSpace::VIEW_ORIENTATION) { + spotDirection = (inverseViewMatrix * glm::vec4(spotDirection, 0.0f)); + } + if (lightSpaceNew == LightSpace::VIEW || lightSpaceNew == LightSpace::VIEW_ORIENTATION) { + spotDirection = (viewMatrix * glm::vec4(spotDirection, 0.0f)); + } + light.spotDirection = spotDirection; +} diff --git a/src/PathTracer/LightEditorWidget.hpp b/src/PathTracer/LightEditorWidget.hpp index 1d6f2bc..f649a46 100644 --- a/src/PathTracer/LightEditorWidget.hpp +++ b/src/PathTracer/LightEditorWidget.hpp @@ -40,6 +40,8 @@ typedef std::shared_ptr BufferPtr; }} namespace sgl { class PropertyEditor; +class Camera; +typedef std::shared_ptr CameraPtr; } namespace IGFD { @@ -77,6 +79,9 @@ struct Light { // Point light & spotlight. glm::vec3 position{}; ///< POINT & SPOT; distance for DIRECTIONAL. uint32_t useDistance = true; ///< POINT & SPOT + + glm::vec3 spotDirection = glm::vec3(0.0f, 0.0f, -1.0f); ///< SPOT + float padding{}; ///< SPOT }; class LightEditorWidget { @@ -96,6 +101,7 @@ class LightEditorWidget { inline void setStandardWindowSize(int width, int height) { standardWidth = width; standardHeight = height; } inline void setStandardWindowPosition(int x, int y) { standardPositionX = x; standardPositionY = y; } void setFileDialogInstance(ImGuiFileDialog* _fileDialogInstance); + inline void setCamera(const sgl::CameraPtr& _camera) { camera = _camera; } /// Property interface. void setLightProperty(uint32_t lightIdx, const std::string& key, const std::string& value); @@ -107,12 +113,17 @@ class LightEditorWidget { private: sgl::vk::Renderer* renderer; + sgl::CameraPtr camera; void recreateLightBuffer(); void updateLightBuffer(); std::vector lights; sgl::vk::BufferPtr lightsBuffer; + // Whether to transform positions and directions on changing light spaces. + bool shallTransformOnSpaceChange = false; + void transformLightSpace(Light& light, LightSpace lightSpaceOld); + // Counts lights by their creation so that ImGui can keep track when lights are removed. ptrdiff_t currentLightIdx = 0; std::vector lightCreationIndices;