From f35d1a62784094bb38e11f7a0ae79c1e07ea0753 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Mon, 3 Jul 2023 15:08:28 +0200 Subject: [PATCH 1/4] USD: improve light units conversion Use pi factor to convert from radiant flux to intensity, and fix wrong unnormalized point/sphere light unit in Cycles. Overall it should be much closer now for all lights. But point and distant lights are still off and seemingly without a consistent convention in other Hydra renderers. Ref #109404 --- intern/cycles/hydra/light.cpp | 3 +++ intern/cycles/kernel/light/point.h | 6 +++--- intern/cycles/scene/light.cpp | 5 +++-- source/blender/io/usd/intern/usd_reader_light.cc | 4 +++- source/blender/io/usd/intern/usd_writer_light.cc | 16 ++++------------ 5 files changed, 16 insertions(+), 18 deletions(-) diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp index 244b6226e44..58575f5b2b6 100644 --- a/intern/cycles/hydra/light.cpp +++ b/intern/cycles/hydra/light.cpp @@ -94,6 +94,9 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, strength *= value.Get(); } + /* Convert from intensity to radiant flux. */ + strength *= M_PI; + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->normalize); _light->set_normalize(value.IsHolding() && value.UncheckedGet()); diff --git a/intern/cycles/kernel/light/point.h b/intern/cycles/kernel/light/point.h index a4dbd1850fe..08272c1b4ee 100644 --- a/intern/cycles/kernel/light/point.h +++ b/intern/cycles/kernel/light/point.h @@ -44,7 +44,7 @@ ccl_device_inline bool point_light_sample(const ccl_global KernelLight *klight, ls->P = P + ls->D * ls->t; - ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + ls->eval_fac = M_1_PI_F * klight->spot.invarea; if (r_sq == 0) { /* Use intensity instead of radiance for point light. */ ls->eval_fac /= sqr(ls->t); @@ -100,7 +100,7 @@ ccl_device_forceinline void point_light_mnee_sample_update(const ccl_global Kern ls->Ng = normalize(ls->P - klight->co); } else { - ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + ls->eval_fac = M_1_PI_F * klight->spot.invarea; ls->Ng = -ls->D; @@ -136,7 +136,7 @@ ccl_device_inline bool point_light_sample_from_intersection( const uint32_t path_flag, ccl_private LightSample *ccl_restrict ls) { - ls->eval_fac = M_1_PI_F * 0.25f * klight->spot.invarea; + ls->eval_fac = M_1_PI_F * klight->spot.invarea; const float radius = klight->spot.radius; diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 12cfdae4eb1..988c9227f69 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1217,8 +1217,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce /* TODO: `invarea` was used for disk sampling, with the current solid angle sampling this * becomes unnecessary. We could store `eval_fac` instead, but currently it shares the same * #KernelSpotLight type with #LIGHT_SPOT, so keep it know until refactor for spot light. */ - float invarea = (light->normalize && radius > 0.0f) ? 1.0f / (M_PI_F * radius * radius) : - 1.0f; + float invarea = (light->normalize && radius > 0.0f) ? + 1.0f / (4.0f * M_PI_F * radius * radius) : + 1.0f; if (light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc index e8de47965eb..c564fdfe156 100644 --- a/source/blender/io/usd/intern/usd_reader_light.cc +++ b/source/blender/io/usd/intern/usd_reader_light.cc @@ -80,7 +80,9 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime if (pxr::UsdAttribute intensity_attr = light_api.GetIntensityAttr()) { float intensity = 0.0f; if (intensity_attr.Get(&intensity, motionSampleTime)) { - blight->energy = intensity * this->import_params_.light_intensity_scale; + /* Convert from intensity to radiant flux. */ + blight->energy = intensity * M_PI; + blight->energy *= this->import_params_.light_intensity_scale; } } diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc index 5406d40d284..8c30f6c880b 100644 --- a/source/blender/io/usd/intern/usd_writer_light.cc +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -10,6 +10,7 @@ #include #include "BLI_assert.h" +#include "BLI_math_rotation.h" #include "BLI_utildefines.h" #include "DNA_light_types.h" @@ -39,6 +40,9 @@ void USDLightWriter::do_write(HierarchyContext &context) #endif + /* Convert from radiant flux to intensity. */ + float usd_intensity = light->energy / M_PI; + switch (light->type) { case LA_AREA: switch (light->area_shape) { @@ -101,19 +105,7 @@ void USDLightWriter::do_write(HierarchyContext &context) BLI_assert_msg(0, "is_supported() returned true for unsupported light type"); } - /* Scale factor to get to somewhat-similar illumination. Since the USDViewer had similar - * over-exposure as Blender Internal with the same values, this code applies the reverse of the - * versioning code in light_emission_unify(). */ - float usd_intensity; - if (light->type == LA_SUN) { - /* Untested, as the Hydra GL viewport of USDViewer doesn't support distant lights. */ - usd_intensity = light->energy; - } - else { - usd_intensity = light->energy / 100.0f; - } usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode); - usd_light_api.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode); usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode); } -- 2.30.2 From 053fd7850fd4c4703c75ebf55922b9fc9bcc03f0 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 6 Jul 2023 21:03:25 +0200 Subject: [PATCH 2/4] Fix wrong unnormalized disk light --- intern/cycles/scene/light.cpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 988c9227f69..5ca7924fd65 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1152,9 +1152,13 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce float3 axis_v = normalize_len(extentv, &len_v); float area = len_u * len_v; if (light->ellipse) { - area *= -M_PI_4_F; + area *= M_PI_4_F; } float invarea = (area != 0.0f) ? 1.0f / area : 1.0f; + if (light->ellipse) { + /* Negative inverse area indicates ellipse. */ + invarea = -invarea; + } float3 dir = light->dir; dir = safe_normalize(dir); @@ -1278,9 +1282,13 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce float3 axis_v = normalize_len(extentv, &len_v); float area = len_u * len_v; if (light->ellipse) { - area *= -M_PI_4_F; + area *= M_PI_4_F; } float invarea = (light->normalize && area != 0.0f) ? 1.0f / area : 1.0f; + if (light->ellipse) { + /* Negative inverse area indicates ellipse. */ + invarea = -invarea; + } float3 dir = light->dir; const float half_spread = 0.5f * light->spread; -- 2.30.2 From d417968af2a64604087f651130020c0e6e07eb74 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Thu, 6 Jul 2023 21:20:25 +0200 Subject: [PATCH 3/4] Make normalized distant lights match Karma more closely --- intern/cycles/hydra/light.cpp | 10 ++++++++-- source/blender/io/usd/intern/usd_reader_light.cc | 10 ++++++++-- source/blender/io/usd/intern/usd_writer_light.cc | 13 ++++++++++--- 3 files changed, 26 insertions(+), 7 deletions(-) diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp index 58575f5b2b6..1c614f6527b 100644 --- a/intern/cycles/hydra/light.cpp +++ b/intern/cycles/hydra/light.cpp @@ -94,8 +94,14 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, strength *= value.Get(); } - /* Convert from intensity to radiant flux. */ - strength *= M_PI; + if (_lightType == HdPrimTypeTokens->distantLight) { + /* Unclear why, but approximately matches Karma. */ + strength *= 4.0f; + } + else { + /* Convert from intensity to radiant flux. */ + strength *= M_PI; + } value = sceneDelegate->GetLightParamValue(id, HdLightTokens->normalize); _light->set_normalize(value.IsHolding() && value.UncheckedGet()); diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc index c564fdfe156..893265b684e 100644 --- a/source/blender/io/usd/intern/usd_reader_light.cc +++ b/source/blender/io/usd/intern/usd_reader_light.cc @@ -80,8 +80,14 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime if (pxr::UsdAttribute intensity_attr = light_api.GetIntensityAttr()) { float intensity = 0.0f; if (intensity_attr.Get(&intensity, motionSampleTime)) { - /* Convert from intensity to radiant flux. */ - blight->energy = intensity * M_PI; + if (blight->type == LA_SUN) { + /* Unclear why, but approximately matches Karma. */ + blight->energy = intensity * 4.0f; + } + else { + /* Convert from intensity to radiant flux. */ + blight->energy = intensity * M_PI; + } blight->energy *= this->import_params_.light_intensity_scale; } } diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc index 8c30f6c880b..1615e21d6ac 100644 --- a/source/blender/io/usd/intern/usd_writer_light.cc +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -40,9 +40,6 @@ void USDLightWriter::do_write(HierarchyContext &context) #endif - /* Convert from radiant flux to intensity. */ - float usd_intensity = light->energy / M_PI; - switch (light->type) { case LA_AREA: switch (light->area_shape) { @@ -105,6 +102,16 @@ void USDLightWriter::do_write(HierarchyContext &context) BLI_assert_msg(0, "is_supported() returned true for unsupported light type"); } + float usd_intensity; + if (light->type == LA_SUN) { + /* Unclear why, but approximately matches Karma. */ + usd_intensity = light->energy / 4.0f; + } + else { + /* Convert from radiant flux to intensity. */ + usd_intensity = light->energy / M_PI; + } + usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode); usd_light_api.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode); usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode); -- 2.30.2 From 7c34688e734f27d4c66fc18bb17b73d649cbf456 Mon Sep 17 00:00:00 2001 From: Brecht Van Lommel Date: Fri, 7 Jul 2023 14:37:16 +0200 Subject: [PATCH 4/4] Better handling of point lights, add spot light export --- intern/cycles/hydra/light.cpp | 12 +++-- intern/cycles/scene/light.cpp | 6 +-- .../blender/io/usd/intern/usd_reader_light.cc | 50 ++++++++----------- .../blender/io/usd/intern/usd_writer_light.cc | 19 ++++++- 4 files changed, 49 insertions(+), 38 deletions(-) diff --git a/intern/cycles/hydra/light.cpp b/intern/cycles/hydra/light.cpp index 1c614f6527b..1cb2b0fb17a 100644 --- a/intern/cycles/hydra/light.cpp +++ b/intern/cycles/hydra/light.cpp @@ -142,9 +142,15 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate, } } else if (_lightType == HdPrimTypeTokens->sphereLight) { - value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius); - if (!value.IsEmpty()) { - _light->set_size(value.Get()); + value = sceneDelegate->GetLightParamValue(id, TfToken("treatAsPoint")); + if (!value.IsEmpty() && value.Get()) { + _light->set_size(0.0f); + } + else { + value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius); + if (!value.IsEmpty()) { + _light->set_size(value.Get()); + } } bool shaping = false; diff --git a/intern/cycles/scene/light.cpp b/intern/cycles/scene/light.cpp index 5ca7924fd65..7d5986de4db 100644 --- a/intern/cycles/scene/light.cpp +++ b/intern/cycles/scene/light.cpp @@ -1221,9 +1221,9 @@ void LightManager::device_update_lights(Device *device, DeviceScene *dscene, Sce /* TODO: `invarea` was used for disk sampling, with the current solid angle sampling this * becomes unnecessary. We could store `eval_fac` instead, but currently it shares the same * #KernelSpotLight type with #LIGHT_SPOT, so keep it know until refactor for spot light. */ - float invarea = (light->normalize && radius > 0.0f) ? - 1.0f / (4.0f * M_PI_F * radius * radius) : - 1.0f; + float invarea = (radius == 0.0f) ? 1.0f / 4.0f : + (light->normalize) ? 1.0f / (4.0f * M_PI_F * radius * radius) : + 1.0f; if (light->use_mis && radius > 0.0f) shader_id |= SHADER_USE_MIS; diff --git a/source/blender/io/usd/intern/usd_reader_light.cc b/source/blender/io/usd/intern/usd_reader_light.cc index 893265b684e..7294e3652b5 100644 --- a/source/blender/io/usd/intern/usd_reader_light.cc +++ b/source/blender/io/usd/intern/usd_reader_light.cc @@ -4,6 +4,8 @@ #include "usd_reader_light.h" +#include "BLI_math_rotation.h" + #include "BKE_light.h" #include "BKE_object.h" @@ -173,22 +175,6 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime } break; case LA_LOCAL: - if (prim_.IsA()) { - - pxr::UsdLuxSphereLight sphere_light(prim_); - - if (!sphere_light) { - break; - } - - if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { - float radius = 0.0f; - if (radius_attr.Get(&radius, motionSampleTime)) { - blight->radius = radius; - } - } - } - break; case LA_SPOT: if (prim_.IsA()) { @@ -198,28 +184,32 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime break; } - if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { + pxr::UsdAttribute treatAsPoint_attr = sphere_light.GetTreatAsPointAttr(); + bool treatAsPoint; + if (treatAsPoint_attr && treatAsPoint_attr.Get(&treatAsPoint, motionSampleTime) && + treatAsPoint) { + blight->radius = 0.0f; + } + else if (pxr::UsdAttribute radius_attr = sphere_light.GetRadiusAttr()) { float radius = 0.0f; if (radius_attr.Get(&radius, motionSampleTime)) { blight->radius = radius; } } - if (!shaping_api) { - break; - } - - if (pxr::UsdAttribute cone_angle_attr = shaping_api.GetShapingConeAngleAttr()) { - float cone_angle = 0.0f; - if (cone_angle_attr.Get(&cone_angle, motionSampleTime)) { - blight->spotsize = cone_angle * (float(M_PI) / 180.0f) * 2.0f; + if (blight->type == LA_SPOT && shaping_api) { + if (pxr::UsdAttribute cone_angle_attr = shaping_api.GetShapingConeAngleAttr()) { + float cone_angle = 0.0f; + if (cone_angle_attr.Get(&cone_angle, motionSampleTime)) { + blight->spotsize = DEG2RADF(cone_angle) * 2.0f; + } } - } - if (pxr::UsdAttribute cone_softness_attr = shaping_api.GetShapingConeSoftnessAttr()) { - float cone_softness = 0.0f; - if (cone_softness_attr.Get(&cone_softness, motionSampleTime)) { - blight->spotblend = cone_softness; + if (pxr::UsdAttribute cone_softness_attr = shaping_api.GetShapingConeSoftnessAttr()) { + float cone_softness = 0.0f; + if (cone_softness_attr.Get(&cone_softness, motionSampleTime)) { + blight->spotblend = cone_softness; + } } } } diff --git a/source/blender/io/usd/intern/usd_writer_light.cc b/source/blender/io/usd/intern/usd_writer_light.cc index 1615e21d6ac..a4dae5956f1 100644 --- a/source/blender/io/usd/intern/usd_writer_light.cc +++ b/source/blender/io/usd/intern/usd_writer_light.cc @@ -7,6 +7,7 @@ #include #include #include +#include #include #include "BLI_assert.h" @@ -23,7 +24,7 @@ USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWrite bool USDLightWriter::is_supported(const HierarchyContext *context) const { Light *light = static_cast(context->object->data); - return ELEM(light->type, LA_AREA, LA_LOCAL, LA_SUN); + return ELEM(light->type, LA_AREA, LA_LOCAL, LA_SUN, LA_SPOT); } void USDLightWriter::do_write(HierarchyContext &context) @@ -78,9 +79,22 @@ void USDLightWriter::do_write(HierarchyContext &context) } } break; - case LA_LOCAL: { + case LA_LOCAL: + case LA_SPOT: { pxr::UsdLuxSphereLight sphere_light = pxr::UsdLuxSphereLight::Define(stage, usd_path); sphere_light.CreateRadiusAttr().Set(light->radius, timecode); + if (light->radius == 0.0f) { + sphere_light.CreateTreatAsPointAttr().Set(true, timecode); + } + + if (light->type == LA_SPOT) { + pxr::UsdLuxShapingAPI shaping_api = pxr::UsdLuxShapingAPI::Apply(sphere_light.GetPrim()); + if (shaping_api) { + shaping_api.CreateShapingConeAngleAttr().Set(RAD2DEGF(light->spotsize) / 2.0f, timecode); + shaping_api.CreateShapingConeSoftnessAttr().Set(light->spotblend, timecode); + } + } + #if PXR_VERSION >= 2111 usd_light_api = sphere_light.LightAPI(); #else @@ -115,6 +129,7 @@ void USDLightWriter::do_write(HierarchyContext &context) usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode); usd_light_api.CreateColorAttr().Set(pxr::GfVec3f(light->r, light->g, light->b), timecode); usd_light_api.CreateSpecularAttr().Set(light->spec_fac, timecode); + usd_light_api.CreateNormalizeAttr().Set(true, timecode); } } // namespace blender::io::usd -- 2.30.2