Skip to content

Commit

Permalink
Merge branch 'dev/ralston/light_transform_fixes' into 'main'
Browse files Browse the repository at this point in the history
[REMIX-2544] Light Transform Fixes

See merge request lightspeedrtx/dxvk-remix-nv!699
  • Loading branch information
anon-apple committed Feb 13, 2024
2 parents d3dadde + 60dce29 commit 6061236
Show file tree
Hide file tree
Showing 12 changed files with 336 additions and 209 deletions.
10 changes: 5 additions & 5 deletions src/dxvk/rtx_render/rtx_game_capturer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -362,12 +362,12 @@ namespace dxvk {
sphereLight.xforms.reserve(m_options.numFrames - m_pCap->numFramesCaptured);
sphereLight.firstTime = m_pCap->currentFrameNum;
const dxvk::RtLightShaping& shaping = rtLight.getShaping();
if (shaping.enabled) {
if (shaping.getEnabled()) {
sphereLight.shapingEnabled = true;
sphereLight.coneAngleDegrees = acos(shaping.cosConeAngle) * kRadiansToDegrees;
sphereLight.coneSoftness = shaping.coneSoftness;
sphereLight.focusExponent = shaping.focusExponent;
rotation = pxr::GfRotation(-pxr::GfVec3d::ZAxis(), pxr::GfVec3f(&shaping.primaryAxis[0]));
sphereLight.coneAngleDegrees = std::acos(shaping.getCosConeAngle()) * kRadiansToDegrees;
sphereLight.coneSoftness = shaping.getConeSoftness();
sphereLight.focusExponent = shaping.getFocusExponent();
rotation = pxr::GfRotation(-pxr::GfVec3d::ZAxis(), pxr::GfVec3f(&shaping.getPrimaryAxis()[0]));
}
Logger::debug("[GameCapturer][" + m_pCap->idStr + "][SphereLight:" + name + "] New");
}
Expand Down
65 changes: 38 additions & 27 deletions src/dxvk/rtx_render/rtx_light_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -164,8 +164,8 @@ namespace dxvk {
void LightManager::dynamicLightMatching() {
ScopedCpuProfileZone();
// Try match up any stragglers now we have the full light list this frame.
for (auto it = m_lights.begin(); it != m_lights.end(); ) {
RtLight& light = it->second;
for (auto it = m_lights.cbegin(); it != m_lights.cend(); ) {
const RtLight& light = it->second;
// Only looking for instances of dynamic lights that have been updated on the previous frame
if (light.getFrameLastTouched() + 1 != m_device->getCurrentFrameId()) {
++it;
Expand All @@ -183,24 +183,26 @@ namespace dxvk {
}

float currentSimilarity = -1.f;
std::optional<XXH64_hash_t> similarLight;
for (auto&& newPair : m_lights) {
RtLight& newLight = newPair.second;
// Note: Using an iterator for the found similar light is safe here because the m_lights map will not change between where
// it is found and where it is accessed.
std::optional<decltype(m_lights)::iterator> similarLight;
for (auto similarLightIterator = m_lights.begin(); similarLightIterator != m_lights.end(); ++similarLightIterator) {
const RtLight& newLight = similarLightIterator->second;
// Skip comparing to old lights, this check implicitly avoids comparing the exact same light.
if (newLight.getBufferIdx() != kNewLightIdx || newLight.isChildOfMesh())
continue;

float similarity = isSimilar(light, newLight, RtxOptions::uniqueObjectDistance());
const float similarity = isSimilar(light, newLight, RtxOptions::uniqueObjectDistance());
// Update the cached light if it's similar.
if (similarity > currentSimilarity) {
similarLight = newPair.first;
similarLight = similarLightIterator;
currentSimilarity = similarity;
}
}

if (currentSimilarity >= 0 && similarLight.has_value()) {
// This is a dynamic light!
RtLight& dynamicLight = m_lights[similarLight.value()];
RtLight& dynamicLight = (*similarLight)->second;
dynamicLight.isDynamic = true;

// This is the same light, so update our new light
Expand Down Expand Up @@ -250,24 +252,26 @@ namespace dxvk {

const auto oldSphereLightBufferIndex = oldFallbackLightPresent ? m_fallbackLight->getBufferIdx() : 0;

RtLightShaping shaping{};

const auto enableFallback = enableFallbackLightShaping();

if (enableFallback) {
shaping.enabled = true;
const bool shapingEnabled = enableFallback;
Vector3 primaryAxis = Vector3(0.0f, 0.0f, 1.0f);
float cosConeAngle = 0.0f;
float coneSoftness = 0.0f;
float focusExponent = 0.0f;

if (enableFallback) {
if (enableFallbackLightViewPrimaryAxis()) {
shaping.primaryAxis = mainCamera.getDirection();
primaryAxis = mainCamera.getDirection();
} else {
shaping.primaryAxis = fallbackLightPrimaryAxis();
// Note: Must normalize the fallback light's primary axis as it is specified by options or ImGui and has
// no hard requirement to be normalized.
primaryAxis = safeNormalize(fallbackLightPrimaryAxis(), Vector3(0.0f, 0.0f, 1.0f));
}

shaping.cosConeAngle = std::cos(fallbackLightConeAngle() * kDegreesToRadians);
shaping.coneSoftness = fallbackLightConeSoftness();
shaping.focusExponent = fallbackLightFocusExponent();
} else {
shaping.enabled = false;
cosConeAngle = std::cos(fallbackLightConeAngle() * kDegreesToRadians);
coneSoftness = fallbackLightConeSoftness();
focusExponent = fallbackLightFocusExponent();
}

// Note: Will be recreated every frame due to the need to be dynamic. Not super effecient but this is only
Expand All @@ -276,7 +280,7 @@ namespace dxvk {
mainCamera.getPosition() + fallbackLightPositionOffset(),
fallbackLightRadiance(),
fallbackLightRadius(),
shaping
RtLightShaping(shapingEnabled, primaryAxis, cosConeAngle, coneSoftness, focusExponent)
));

// Update light dynamic properties
Expand Down Expand Up @@ -487,20 +491,20 @@ namespace dxvk {
if (a.getType() == RtLightType::Sphere) {
const RtLightShaping& aShaping = a.getSphereLight().getShaping();
const RtLightShaping& bShaping = b.getSphereLight().getShaping();
if (aShaping.enabled != bShaping.enabled) {
if (aShaping.getEnabled() != bShaping.getEnabled()) {
return kNotSimilar;
}

if (aShaping.enabled && bShaping.enabled) {
float cosAxis = dot(aShaping.primaryAxis, bShaping.primaryAxis);
if (aShaping.getEnabled() && bShaping.getEnabled()) {
const float cosAxis = dot(aShaping.getPrimaryAxis(), bShaping.getPrimaryAxis());
if (cosAxis < kCosAngleSimilarityThreshold) {
return kNotSimilar;
}
float coneAngleDelta = abs(aShaping.cosConeAngle - bShaping.cosConeAngle);
const float coneAngleDelta = std::abs(aShaping.getCosConeAngle() - bShaping.getCosConeAngle());
if (coneAngleDelta > 0.01f) {
return kNotSimilar;
}
float coneSoftnessDelta = abs(aShaping.coneSoftness - bShaping.coneSoftness);
float coneSoftnessDelta = abs(aShaping.getConeSoftness() - bShaping.getConeSoftness());
if (coneSoftnessDelta > 0.01f) {
return kNotSimilar;
}
Expand Down Expand Up @@ -635,8 +639,15 @@ namespace dxvk {
}

// Add as a new light (with/out updated data depending on if a similar light was found)
RtLight& localLight = m_lights[rtLight.getInstanceHash()];
localLight = rtLight;
const auto& [localLightIterator, addedSuccessfully] = m_lights.try_emplace(rtLight.getInstanceHash(), rtLight);
RtLight& localLight = localLightIterator->second;

// Note: Ensure that the new light was added successfully (meaning that no existing light existed in the light map at the
// given Light instance hash). This should always be the case as this code is in the "else" branch of a check to see if the
// light exists in the map already, meaning a light with this hash should not be present in this case.
// If this fact ever changes, use insert_or_assign instead of emplace to insert or overwrite the light in the map if that is
// the desired behavior.
assert(addedSuccessfully);

// Copy/interpolate any state we like from the similar light.
if (similarLight.has_value())
Expand Down
6 changes: 3 additions & 3 deletions src/dxvk/rtx_render/rtx_light_manager.h
Original file line number Diff line number Diff line change
Expand Up @@ -156,20 +156,20 @@ struct LightManager : public CommonDeviceObject {
RTX_OPTION("rtx", FallbackLightType, fallbackLightType, FallbackLightType::Distant, "The light type to use for the fallback light. Determines which other fallback light options are used.");
RTX_OPTION("rtx", Vector3, fallbackLightRadiance, Vector3(1.6f, 1.8f, 2.0f), "The radiance to use for the fallback light (used across all light types).");
RTX_OPTION("rtx", Vector3, fallbackLightDirection, Vector3(-0.2f, -1.0f, 0.4f), "The direction to use for the fallback light (used only for Distant light types)");
RTX_OPTION("rtx", float, fallbackLightAngle, 5.0f, "The spread angle to use for the fallback light (used only for Distant light types).");
RTX_OPTION("rtx", float, fallbackLightAngle, 5.0f, "The angular size in degrees to use for the fallback light (used only for Distant light types). Should only be within the range [0, 180].");
RTX_OPTION("rtx", float, fallbackLightRadius, 5.0f, "The radius to use for the fallback light (used only for Sphere light types).");
RTX_OPTION("rtx", Vector3, fallbackLightPositionOffset, Vector3(0.0f, 0.0f, 0.0f), "The position offset from the camera origin to use for the fallback light (used only for non-Distant light types).");
RTX_OPTION("rtx", bool, enableFallbackLightShaping, false, "Enables light shaping on the fallback light (only used for non-Distant light types).");
RTX_OPTION("rtx", bool, enableFallbackLightViewPrimaryAxis, false,
R"(Enables usage of the camera's view axis as the primary axis for the fallback light's shaping (only used for non - Distant light types). Typically the shaping primary axis may be specified directly, but if desired it may be set to the camera's view axis for a "flashlight" effect.)");
RTX_OPTION("rtx", Vector3, fallbackLightPrimaryAxis, Vector3(0.0f, 0.0f, -1.0f), "The primary axis to use for the fallback light shaping (used only for non-Distant light types).");
RTX_OPTION("rtx", float, fallbackLightConeAngle, 25.0f, "The cone angle to use for the fallback light shaping (used only for non-Distant light types with shaping enabled).");
RTX_OPTION("rtx", float, fallbackLightConeAngle, 25.0f, "The cone angle in degrees to use for the fallback light shaping (used only for non-Distant light types with shaping enabled). Should only be within the range [0, 180].");
RTX_OPTION("rtx", float, fallbackLightConeSoftness, 0.1f, "The cone softness to use for the fallback light shaping (used only for non-Distant light types with shaping enabled).");
RTX_OPTION("rtx", float, fallbackLightFocusExponent, 2.0f, "The focus exponent to use for the fallback light shaping (used only for non-Distant light types with shaping enabled).");
RTX_OPTION("rtx", bool, calculateLightIntensityUsingLeastSquares, true, "Enable usage of least squares for approximating a light's falloff curve rather than a more basic single point approach. This will generally result in more accurate matching of the original application's custom light attenuation curves, especially with non physically based linear-style attenuation.");
RTX_OPTION("rtx", float, lightConversionSphereLightFixedRadius, 4.f, "The fixed radius in world units to use for legacy lights converted to sphere lights (currently point and spot lights will convert to sphere lights). Use caution with large light radii as many legacy lights will be placed close to geometry and intersect it, causing suboptimal light sampling performance or other visual artifacts (lights clipping through walls, etc).");
RTX_OPTION("rtx", float, lightConversionDistantLightFixedIntensity, 1.0f, "The fixed intensity (in W/sr) to use for legacy lights converted to distant lights (currently directional lights will convert to distant lights).");
RTX_OPTION("rtx", float, lightConversionDistantLightFixedAngle, 0.0349f, "The angular size in radiance of the distant light source for legacy lights converted to distant lights. Set to ~2 degrees in radians by default.");
RTX_OPTION("rtx", float, lightConversionDistantLightFixedAngle, 0.0349f, "The angular size in radians of the distant light source for legacy lights converted to distant lights. Set to ~2 degrees in radians by default. Should only be within the range [0, pi].");
};

} // namespace dxvk
Expand Down
60 changes: 33 additions & 27 deletions src/dxvk/rtx_render/rtx_light_manager_gui.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -84,18 +84,18 @@ namespace dxvk {


void LightManager::showImguiSettings() {
bool lightSettingsDirty = false;

const auto separator = []() {
ImGui::Dummy({ 0,2 });
ImGui::Separator();
ImGui::Dummy({ 0,2 });
};

if (ImGui::CollapsingHeader("Light Translation", ImGuiTreeNodeFlags_CollapsingHeader)) {
ImGui::Dummy({ 0,2 });
ImGui::Indent();

auto separator = []() {
ImGui::Dummy({ 0,2 });
ImGui::Separator();
ImGui::Dummy({ 0,2 });
};

bool lightSettingsDirty = false;

lightSettingsDirty |= ImGui::Checkbox("Suppress Light Keeping", &suppressLightKeepingObject());

separator();
Expand All @@ -110,7 +110,7 @@ namespace dxvk {

ImGui::BeginDisabled(disableDirectional);
lightSettingsDirty |= ImGui::DragFloat("Distant Light Fixed Intensity", &lightConversionDistantLightFixedIntensityObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Distant Light Fixed Angle", &lightConversionDistantLightFixedAngleObject(), 0.01f, 0.0f, kPi, "%.4f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Distant Light Fixed Angle", &lightConversionDistantLightFixedAngleObject(), 0.01f, 0.0f, kPi, "%.4f rad", ImGuiSliderFlags_AlwaysClamp);
ImGui::EndDisabled();

ImGui::Text("Ignore Game Lights:");
Expand All @@ -122,7 +122,12 @@ namespace dxvk {
lightSettingsDirty |= ImGui::Checkbox("Spot", &ignoreGameSpotLightsObject());
ImGui::Unindent();

separator();
ImGui::Unindent();
}

if (ImGui::CollapsingHeader("Fallback Light", ImGuiTreeNodeFlags_CollapsingHeader)) {
ImGui::Dummy({ 0,2 });
ImGui::Indent();

lightSettingsDirty |= fallbackLightModeCombo.getKey(&fallbackLightModeObject());

Expand All @@ -134,7 +139,7 @@ namespace dxvk {

if (fallbackLightType() == FallbackLightType::Distant) {
lightSettingsDirty |= ImGui::DragFloat3("Fallback Light Direction", &fallbackLightDirectionObject(), 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Angle", &fallbackLightAngleObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Angle", &fallbackLightAngleObject(), 0.01f, 0.0f, FLT_MAX, "%.3f deg", ImGuiSliderFlags_AlwaysClamp);
} else if (fallbackLightType() == FallbackLightType::Sphere) {
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Radius", &fallbackLightRadiusObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat3("Fallback Light Position Offset", &fallbackLightPositionOffsetObject(), 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp);
Expand All @@ -152,7 +157,7 @@ namespace dxvk {
lightSettingsDirty |= ImGui::DragFloat3("Fallback Light Primary Axis", &fallbackLightPrimaryAxisObject(), 0.1f, 0.0f, 0.0f, "%.3f", ImGuiSliderFlags_AlwaysClamp);
}

lightSettingsDirty |= ImGui::DragFloat("Fallback Light Cone Angle", &fallbackLightConeAngleObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Cone Angle", &fallbackLightConeAngleObject(), 0.01f, 0.0f, FLT_MAX, "%.3f deg", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Cone Softness", &fallbackLightConeSoftnessObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);
lightSettingsDirty |= ImGui::DragFloat("Fallback Light Focus Exponent", &fallbackLightFocusExponentObject(), 0.01f, 0.0f, FLT_MAX, "%.3f", ImGuiSliderFlags_AlwaysClamp);

Expand All @@ -163,15 +168,15 @@ namespace dxvk {
ImGui::EndDisabled();

ImGui::Unindent();
}

// Clear the lights and fallback light if the settings are dirty to recreate the lights on the next frame.
if (lightSettingsDirty) {
clear();
// Clear the lights and fallback light if the settings are dirty to recreate the lights on the next frame.
if (lightSettingsDirty) {
clear();

// Note: Fallback light reset here so that changes to its settings will take effect, does not need to be part
// of usual light clearing logic though.
m_fallbackLight.reset();
}
// Note: Fallback light reset here so that changes to its settings will take effect, does not need to be part
// of usual light clearing logic though.
m_fallbackLight.reset();
}
}

Expand Down Expand Up @@ -272,12 +277,12 @@ namespace dxvk {
}

if (pShaping) {
if (pShaping->enabled) {
if (pShaping->getEnabled()) {
ImGui::Text("Light Shaping: Enabled");
ImGui::Text("\tPrimary Axis: %.2f %.2f %.2f", pShaping->primaryAxis.x, pShaping->primaryAxis.y, pShaping->primaryAxis.z);
ImGui::Text("\tCosine Cone Angle: %.2f", pShaping->cosConeAngle);
ImGui::Text("\tCone Softness: %.2f", pShaping->coneSoftness);
ImGui::Text("\tFocus Exponent: %.2f", pShaping->focusExponent);
ImGui::Text("\tPrimary Axis: %.2f %.2f %.2f", pShaping->getPrimaryAxis().x, pShaping->getPrimaryAxis().y, pShaping->getPrimaryAxis().z);
ImGui::Text("\tCone Angle: %.2f deg", std::acos(pShaping->getCosConeAngle()) * kRadiansToDegrees);
ImGui::Text("\tCone Softness: %.2f", pShaping->getConeSoftness());
ImGui::Text("\tFocus Exponent: %.2f", pShaping->getFocusExponent());
} else {
ImGui::Text("Light Shaping: Disabled");
}
Expand All @@ -294,8 +299,9 @@ namespace dxvk {
ImGui::SetClipboardText(hashToString(light.getInitialHash()).c_str());
}

ImGui::End();
}
// Note: End must always be called even if Begin returns false (unlike other ImGui patterns).
ImGui::End();
ImGui::PopStyleColor();
}

Expand Down Expand Up @@ -406,7 +412,7 @@ namespace dxvk {
ImGui::SetNextWindowPos(viewport->Pos);
ImGui::SetNextWindowSize(viewport->Size);
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.f, 0.f, 0.f, 0.0f));
if (ImGui::Begin("Light Debug View", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs)) {
if (ImGui::Begin("Light Debug View", nullptr, ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoSavedSettings)) {
ImDrawList* drawList = ImGui::GetWindowDrawList();
drawList->PushClipRectFullScreen();
const RtCamera& camera = device()->getCommon()->getSceneManager().getCamera();
Expand Down Expand Up @@ -462,8 +468,8 @@ namespace dxvk {
}

drawList->PopClipRect();
ImGui::End();
}
ImGui::End();
ImGui::PopStyleColor();
}

Expand Down

0 comments on commit 6061236

Please sign in to comment.