diff --git a/pxr/imaging/plugin/hdEmbree/CMakeLists.txt b/pxr/imaging/plugin/hdEmbree/CMakeLists.txt index 2efef685eb..c3c92aa552 100644 --- a/pxr/imaging/plugin/hdEmbree/CMakeLists.txt +++ b/pxr/imaging/plugin/hdEmbree/CMakeLists.txt @@ -46,6 +46,7 @@ pxr_plugin(hdEmbree renderParam.h PRIVATE_CLASSES + debugCodes implicitSurfaceSceneIndexPlugin RESOURCE_FILES diff --git a/pxr/imaging/plugin/hdEmbree/debugCodes.cpp b/pxr/imaging/plugin/hdEmbree/debugCodes.cpp new file mode 100644 index 0000000000..e38f776489 --- /dev/null +++ b/pxr/imaging/plugin/hdEmbree/debugCodes.cpp @@ -0,0 +1,20 @@ +// +// Copyright 2024 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#include "pxr/pxr.h" +#include "pxr/imaging/plugin/hdEmbree/debugCodes.h" + +#include "pxr/base/tf/debug.h" +#include "pxr/base/tf/registryManager.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_REGISTRY_FUNCTION(TfDebug) +{ + TF_DEBUG_ENVIRONMENT_SYMBOL(HDEMBREE_LIGHT_CREATE, "Creation of HdEmbree lights"); +} + +PXR_NAMESPACE_CLOSE_SCOPE diff --git a/pxr/imaging/plugin/hdEmbree/debugCodes.h b/pxr/imaging/plugin/hdEmbree/debugCodes.h new file mode 100644 index 0000000000..c65002452b --- /dev/null +++ b/pxr/imaging/plugin/hdEmbree/debugCodes.h @@ -0,0 +1,21 @@ +// +// Copyright 2024 Pixar +// +// Licensed under the terms set forth in the LICENSE.txt file available at +// https://openusd.org/license. +// +#ifndef PXR_IMAGING_PLUGIN_HD_EMBREE_DEBUG_CODES_H +#define PXR_IMAGING_PLUGIN_HD_EMBREE_DEBUG_CODES_H + +#include "pxr/pxr.h" +#include "pxr/base/tf/debug.h" + +PXR_NAMESPACE_OPEN_SCOPE + +TF_DEBUG_CODES( + HDEMBREE_LIGHT_CREATE +); + +PXR_NAMESPACE_CLOSE_SCOPE + +#endif // PXR_IMAGING_PLUGIN_HD_EMBREE_DEBUG_CODES_H diff --git a/pxr/imaging/plugin/hdEmbree/light.cpp b/pxr/imaging/plugin/hdEmbree/light.cpp index 082e4605d2..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) @@ -29,6 +84,8 @@ HdEmbree_Light::HdEmbree_Light(SdfPath const& id, TfToken const& lightType) return; } + TF_DEBUG(HDEMBREE_LIGHT_CREATE).Msg("Creating light %s: %s\n", id.GetText(), lightType.GetText()); + // Set the variant to the right type - Sync will fill rest of data if (lightType == HdSprimTypeTokens->cylinderLight) { _lightData.lightVariant = HdEmbree_Cylinder(); @@ -73,6 +130,8 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate, // Store luminance parameters _lightData.intensity = sceneDelegate->GetLightParamValue( id, HdLightTokens->intensity).GetWithDefault(1.0f); + _lightData.diffuse = sceneDelegate->GetLightParamValue( + id, HdLightTokens->diffuse).GetWithDefault(1.0f); _lightData.exposure = sceneDelegate->GetLightParamValue( id, HdLightTokens->exposure).GetWithDefault(0.0f); _lightData.color = sceneDelegate->GetLightParamValue( @@ -112,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 906ba185bc..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,7 +74,9 @@ struct HdEmbree_LightData GfMatrix3f normalXformLightToWorld; GfMatrix4f xformWorldToLight; GfVec3f color; + HdEmbree_LightTexture texture; float intensity = 1.0f; + float diffuse = 1.0f; float exposure = 0.0f; float colorTemperature = 6500.0f; bool enableColorTemperature = false; diff --git a/pxr/imaging/plugin/hdEmbree/mesh.h b/pxr/imaging/plugin/hdEmbree/mesh.h index bbb006302f..2d1ff9a257 100644 --- a/pxr/imaging/plugin/hdEmbree/mesh.h +++ b/pxr/imaging/plugin/hdEmbree/mesh.h @@ -99,6 +99,11 @@ class HdEmbreeMesh final : public HdMesh { /// embree state. virtual void Finalize(HdRenderParam *renderParam) override; + bool EmbreeMeshIsDoubleSided() const + { + return _doubleSided; + } + protected: // Initialize the given representation of this Rprim. // This is called prior to syncing the prim, the first time the repr diff --git a/pxr/imaging/plugin/hdEmbree/renderer.cpp b/pxr/imaging/plugin/hdEmbree/renderer.cpp index c05ee0d27c..0a02725665 100644 --- a/pxr/imaging/plugin/hdEmbree/renderer.cpp +++ b/pxr/imaging/plugin/hdEmbree/renderer.cpp @@ -246,6 +246,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) @@ -364,7 +377,9 @@ _SampleCylinder(GfMatrix4f const& xf, GfMatrix3f const& normalXform, GfVec3f _EvalLightBasic(HdEmbree_LightData const& light) { - GfVec3f Le = light.color * light.intensity * powf(2.0f, light.exposure); + // Our current material model is always 100% diffuse, so diffuse parameter + // is a stright multiplier + GfVec3f Le = light.color * light.intensity * light.diffuse * powf(2.0f, light.exposure); if (light.enableColorTemperature) { Le = GfCompMult(Le, _BlackbodyTemperatureAsRgb(light.colorTemperature)); @@ -393,6 +408,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 @@ -1617,8 +1638,23 @@ HdEmbreeRenderer::_ComputeLighting( float vis = _Visibility(position, ls.wI, ls.dist * 0.99f); // Add exitant luminance + float cosOffNormal = GfDot(ls.wI, normal); + if (cosOffNormal < 0.0f) { + bool doubleSided = false; + HdEmbreeMesh *mesh = + dynamic_cast(prototypeContext->rprim); + if (mesh) { + doubleSided = mesh->EmbreeMeshIsDoubleSided(); + } + + if (doubleSided) { + cosOffNormal *= -1.0f; + } else { + cosOffNormal = 0.0f; + } + } finalColor += ls.Li - * _DotZeroClip(ls.wI, normal) + * cosOffNormal * brdf * vis * ls.invPdfW;