From 4cb4d2f118ac97a194d5b80b39550851137249c1 Mon Sep 17 00:00:00 2001 From: Paul Molodowitch Date: Tue, 30 Jul 2024 00:27:48 -0700 Subject: [PATCH] [hdEmbree] add dome light suppport --- pxr/imaging/plugin/hdEmbree/light.cpp | 5 ++ pxr/imaging/plugin/hdEmbree/light.h | 9 +++ .../plugin/hdEmbree/renderDelegate.cpp | 3 + pxr/imaging/plugin/hdEmbree/renderer.cpp | 73 +++++++++++++++++++ pxr/imaging/plugin/hdEmbree/renderer.h | 1 + 5 files changed, 91 insertions(+) diff --git a/pxr/imaging/plugin/hdEmbree/light.cpp b/pxr/imaging/plugin/hdEmbree/light.cpp index e8bcbd88eb..a556d385d3 100644 --- a/pxr/imaging/plugin/hdEmbree/light.cpp +++ b/pxr/imaging/plugin/hdEmbree/light.cpp @@ -91,6 +91,8 @@ HdEmbree_Light::HdEmbree_Light(SdfPath const& id, TfToken const& lightType) _lightData.lightVariant = HdEmbree_Cylinder(); } else if (lightType == HdSprimTypeTokens->diskLight) { _lightData.lightVariant = HdEmbree_Disk(); + } else if (lightType == HdSprimTypeTokens->domeLight) { + _lightData.lightVariant = HdEmbree_Dome(); } else if (lightType == HdSprimTypeTokens->rectLight) { // Get shape parameters _lightData.lightVariant = HdEmbree_Rect(); @@ -164,6 +166,9 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate, sceneDelegate->GetLightParamValue(id, HdLightTokens->radius) .GetWithDefault(0.5f), }; + } else if constexpr (std::is_same_v) { + typedLight = HdEmbree_Dome{}; + _SyncLightTexture(id, _lightData, sceneDelegate); } else if constexpr (std::is_same_v) { typedLight = HdEmbree_Rect{ sceneDelegate->GetLightParamValue(id, HdLightTokens->width) diff --git a/pxr/imaging/plugin/hdEmbree/light.h b/pxr/imaging/plugin/hdEmbree/light.h index fece3056d8..05ab262cb2 100644 --- a/pxr/imaging/plugin/hdEmbree/light.h +++ b/pxr/imaging/plugin/hdEmbree/light.h @@ -35,6 +35,10 @@ struct HdEmbree_Disk float radius; }; +// Needed for HdEmbree_LightVariant +struct HdEmbree_Dome +{}; + struct HdEmbree_Rect { float width; @@ -50,6 +54,7 @@ using HdEmbree_LightVariant = std::variant< HdEmbree_UnknownLight, HdEmbree_Cylinder, HdEmbree_Disk, + HdEmbree_Dome, HdEmbree_Rect, HdEmbree_Sphere>; @@ -108,6 +113,10 @@ class HdEmbree_Light final : public HdLight return _lightData; } + bool IsDome() const { + return std::holds_alternative(_lightData.lightVariant); + } + private: HdEmbree_LightData _lightData; }; diff --git a/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp b/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp index 100e9c133a..27cad002da 100644 --- a/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderDelegate.cpp @@ -38,6 +38,7 @@ const TfTokenVector HdEmbreeRenderDelegate::SUPPORTED_SPRIM_TYPES = HdPrimTypeTokens->extComputation, HdPrimTypeTokens->cylinderLight, HdPrimTypeTokens->diskLight, + HdPrimTypeTokens->domeLight, HdPrimTypeTokens->rectLight, HdPrimTypeTokens->sphereLight, }; @@ -338,6 +339,7 @@ HdEmbreeRenderDelegate::CreateSprim(TfToken const& typeId, return new HdExtComputation(sprimId); } else if (typeId == HdPrimTypeTokens->light || typeId == HdPrimTypeTokens->diskLight || + typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight || typeId == HdPrimTypeTokens->sphereLight || typeId == HdPrimTypeTokens->cylinderLight) { @@ -360,6 +362,7 @@ HdEmbreeRenderDelegate::CreateFallbackSprim(TfToken const& typeId) return new HdExtComputation(SdfPath::EmptyPath()); } else if (typeId == HdPrimTypeTokens->light || typeId == HdPrimTypeTokens->diskLight || + typeId == HdPrimTypeTokens->domeLight || typeId == HdPrimTypeTokens->rectLight || typeId == HdPrimTypeTokens->sphereLight || typeId == HdPrimTypeTokens->cylinderLight) { diff --git a/pxr/imaging/plugin/hdEmbree/renderer.cpp b/pxr/imaging/plugin/hdEmbree/renderer.cpp index 281d091e8c..23e4c72e4b 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderer.cpp @@ -491,6 +491,45 @@ _EvalAreaLight(HdEmbree_LightData const& light, _ShapeSample const& ss, }; } +_LightSample +_SampleDomeLight(HdEmbree_LightData const& light, GfVec3f const& direction) +{ + float t = acosf(direction[1]) / _pi; + float s = atan2f(direction[0], direction[2]) / (2.0f * _pi); + s = 1.0f - fmodf(s+0.5f, 1.0f); + + GfVec3f Li = light.texture.pixels.empty() ? + GfVec3f(1.0f) + : _SampleLightTexture(light.texture, s, t); + + return _LightSample { + Li, + direction, + std::numeric_limits::max(), + 4.0f * _pi + }; +} + +_LightSample +_EvalDomeLight(HdEmbree_LightData const& light, GfVec3f const& W, + float u1, float u2) +{ + GfVec3f U, V; + GfBuildOrthonormalFrame(W, &U, &V); + + float z = u1; + float r = sqrtf(std::max(0.0f, 1.0f - _Sqr(z))); + float phi = 2.0f * _pi * u2; + + const GfVec3f wI = + (W * z + r * cosf(phi) * U + r * sinf(phi) * V).GetNormalized(); + + _LightSample ls = _SampleDomeLight(light, wI); + ls.invPdfW = 2.0f * _pi; // We only picked from the hemisphere + + return ls; +} + class _LightSampler { public: static _LightSample GetLightSample(HdEmbree_LightData const& lightData, @@ -558,6 +597,10 @@ class _LightSampler { return _EvalAreaLight(_lightData, shapeSample, _hitPosition); } + _LightSample operator()(HdEmbree_Dome const& dome) { + return _EvalDomeLight(_lightData, _normal, _u1, _u2); + } + private: _LightSampler(HdEmbree_LightData const& lightData, GfVec3f const& hitPosition, @@ -677,6 +720,10 @@ HdEmbreeRenderer::AddLight(SdfPath const& lightPath, { ScopedLock lightsWriteLock(_lightsWriteMutex); _lightMap[lightPath] = light; + + if (light->IsDome()) { + _domes.push_back(light); + } } void @@ -684,6 +731,9 @@ HdEmbreeRenderer::RemoveLight(SdfPath const& lightPath, HdEmbree_Light* light) { ScopedLock lightsWriteLock(_lightsWriteMutex); _lightMap.erase(lightPath); + _domes.erase(std::remove_if(_domes.begin(), _domes.end(), + [&light](auto& l){ return l == light; }), + _domes.end()); } bool @@ -1475,6 +1525,29 @@ HdEmbreeRenderer::_ComputeColor(RTCRayHit const& rayHit, std::default_random_engine &random, GfVec4f const& clearColor) { + if (rayHit.hit.geomID == RTC_INVALID_GEOMETRY_ID) { + if (_domes.empty()) { + return clearColor; + } + + // if we missed all geometry in the scene, evaluate the infinite lights + // directly + GfVec4f domeColor(0.0f, 0.0f, 0.0f, 1.0f); + + for (auto* dome : _domes) { + _LightSample ls = _SampleDomeLight( + dome->LightData(), + GfVec3f(rayHit.ray.dir_x, + rayHit.ray.dir_y, + rayHit.ray.dir_z) + ); + domeColor[0] += ls.Li[0]; + domeColor[1] += ls.Li[1]; + domeColor[2] += ls.Li[2]; + } + return domeColor; + } + // Get the instance and prototype context structures for the hit prim. // We don't use embree's multi-level instancing; we // flatten everything in hydra. So instID[0] should always be correct. diff --git a/pxr/imaging/plugin/hdEmbree/renderer.h b/pxr/imaging/plugin/hdEmbree/renderer.h index 85a44ad4b2..e5a8426136 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.h +++ b/pxr/imaging/plugin/hdEmbree/renderer.h @@ -242,6 +242,7 @@ class HdEmbreeRenderer final // Lights mutable WriteMutex _lightsWriteMutex; // protects the 2 below std::map _lightMap; + std::vector _domes; }; PXR_NAMESPACE_CLOSE_SCOPE