USD: improve light units conversion #109795

Manually merged
Brecht Van Lommel merged 4 commits from brecht/blender:usd-light-units into main 2023-07-07 18:27:19 +02:00
5 changed files with 86 additions and 50 deletions

View File

@ -94,6 +94,15 @@ void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
strength *= value.Get<float>();
}
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<bool>() && value.UncheckedGet<bool>());
@ -133,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<float>());
value = sceneDelegate->GetLightParamValue(id, TfToken("treatAsPoint"));
if (!value.IsEmpty() && value.Get<bool>()) {
_light->set_size(0.0f);
}
else {
value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius);
if (!value.IsEmpty()) {
_light->set_size(value.Get<float>());
}
}
bool shaping = false;

View File

@ -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;

View File

@ -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);
@ -1217,8 +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 / (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;
@ -1277,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;

View File

@ -4,6 +4,8 @@
#include "usd_reader_light.h"
#include "BLI_math_rotation.h"
#include "BKE_light.h"
#include "BKE_object.h"
@ -80,7 +82,15 @@ 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;
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;
}
}
@ -165,22 +175,6 @@ void USDLightReader::read_object_data(Main *bmain, const double motionSampleTime
}
break;
case LA_LOCAL:
if (prim_.IsA<pxr::UsdLuxSphereLight>()) {
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<pxr::UsdLuxSphereLight>()) {
@ -190,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;
}
}
}
}

View File

@ -7,9 +7,11 @@
#include <pxr/usd/usdLux/diskLight.h>
#include <pxr/usd/usdLux/distantLight.h>
#include <pxr/usd/usdLux/rectLight.h>
#include <pxr/usd/usdLux/shapingAPI.h>
#include <pxr/usd/usdLux/sphereLight.h>
#include "BLI_assert.h"
brecht marked this conversation as resolved Outdated

Seems unnecessary?

Seems unnecessary?

Indeed, though after the latest changes I did need it for spot lights.

Indeed, though after the latest changes I did need it for spot lights.
#include "BLI_math_rotation.h"
#include "BLI_utildefines.h"
#include "DNA_light_types.h"
@ -22,7 +24,7 @@ USDLightWriter::USDLightWriter(const USDExporterContext &ctx) : USDAbstractWrite
bool USDLightWriter::is_supported(const HierarchyContext *context) const
{
Light *light = static_cast<Light *>(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)
@ -77,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
@ -101,21 +116,20 @@ 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;
/* Unclear why, but approximately matches Karma. */
usd_intensity = light->energy / 4.0f;
}
else {
usd_intensity = light->energy / 100.0f;
/* Convert from radiant flux to intensity. */
usd_intensity = light->energy / M_PI;
}
usd_light_api.CreateIntensityAttr().Set(usd_intensity, timecode);
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