diff --git a/pxr/imaging/plugin/hdEmbree/light.cpp b/pxr/imaging/plugin/hdEmbree/light.cpp index ee03567894..e8bcbd88eb 100644 --- a/pxr/imaging/plugin/hdEmbree/light.cpp +++ b/pxr/imaging/plugin/hdEmbree/light.cpp @@ -21,6 +21,61 @@ #include #include +namespace { + +PXR_NAMESPACE_USING_DIRECTIVE + +HdEmbree_LightTexture +_LoadLightTexture(std::string const& path) +{ + if (path.empty()) { + return HdEmbree_LightTexture(); + } + + HioImageSharedPtr img = HioImage::OpenForReading(path); + if (!img) { + return HdEmbree_LightTexture(); + } + + int width = img->GetWidth(); + int height = img->GetHeight(); + + std::vector pixels(width * height * 3.0f); + + HioImage::StorageSpec storage; + storage.width = width; + storage.height = height; + storage.depth = 1; + storage.format = HioFormatFloat32Vec3; + storage.data = &pixels.front(); + + if (img->Read(storage)) { + return {std::move(pixels), width, height}; + } + TF_WARN("Could not read image %s", path.c_str()); + return { std::vector(), 0, 0 }; +} + + +void +_SyncLightTexture(const SdfPath& id, HdEmbree_LightData& light, HdSceneDelegate *sceneDelegate) +{ + std::string path; + if (VtValue textureValue = sceneDelegate->GetLightParamValue( + id, HdLightTokens->textureFile); + textureValue.IsHolding()) { + SdfAssetPath texturePath = + textureValue.UncheckedGet(); + path = texturePath.GetResolvedPath(); + if (path.empty()) { + path = texturePath.GetAssetPath(); + } + } + light.texture = _LoadLightTexture(path); +} + + +} // anonymous namespace PXR_NAMESPACE_OPEN_SCOPE HdEmbree_Light::HdEmbree_Light(SdfPath const& id, TfToken const& lightType) @@ -116,6 +171,7 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate, sceneDelegate->GetLightParamValue(id, HdLightTokens->height) .Get(), }; + _SyncLightTexture(id, _lightData, sceneDelegate); } else if constexpr (std::is_same_v) { typedLight = HdEmbree_Sphere{ sceneDelegate->GetLightParamValue(id, HdLightTokens->radius) diff --git a/pxr/imaging/plugin/hdEmbree/light.h b/pxr/imaging/plugin/hdEmbree/light.h index 2dd44d0898..fece3056d8 100644 --- a/pxr/imaging/plugin/hdEmbree/light.h +++ b/pxr/imaging/plugin/hdEmbree/light.h @@ -53,6 +53,13 @@ using HdEmbree_LightVariant = std::variant< HdEmbree_Rect, HdEmbree_Sphere>; +struct HdEmbree_LightTexture +{ + std::vector pixels; + int width = 0; + int height = 0; +}; + struct HdEmbree_Shaping { GfVec3f focusTint; @@ -67,6 +74,7 @@ struct HdEmbree_LightData GfMatrix3f normalXformLightToWorld; GfMatrix4f xformWorldToLight; GfVec3f color; + HdEmbree_LightTexture texture; float intensity = 1.0f; float diffuse = 1.0f; float exposure = 0.0f; diff --git a/pxr/imaging/plugin/hdEmbree/renderer.cpp b/pxr/imaging/plugin/hdEmbree/renderer.cpp index fa2d8074e9..281d091e8c 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderer.cpp @@ -294,6 +294,19 @@ struct _LightSample { float invPdfW; }; +GfVec3f +_SampleLightTexture(HdEmbree_LightTexture const& texture, float s, float t) +{ + if (texture.pixels.empty()) { + return GfVec3f(0.0f); + } + + int x = float(texture.width) * s; + int y = float(texture.height) * t; + + return texture.pixels.at(y*texture.width + x); +} + _ShapeSample _SampleRect(GfMatrix4f const& xf, GfMatrix3f const& normalXform, float width, float height, float u1, float u2) @@ -443,6 +456,12 @@ _EvalAreaLight(HdEmbree_LightData const& light, _ShapeSample const& ss, _EvalLightBasic(light) : GfVec3f(0.0f); + // Multiply by the texture, if there is one + if (!light.texture.pixels.empty()) { + Le = GfCompMult(Le, _SampleLightTexture(light.texture, ss.uv[0], + 1.0f - ss.uv[1])); + } + // If normalize is enabled, we need to divide the luminance by the surface // area of the light, which for an area light is equivalent to multiplying // by the area pdf, which is itself the reciprocal of the surface area