Skip to content

Commit

Permalink
[hdEmbree] Initial UsdLux reference implementation
Browse files Browse the repository at this point in the history
This modifies the hdEmbree example plugin to do direct lighting so as to
provide a reference implementation of the expected behaviour for UsdLux.

If no lights are present in the stage, the old ambient occlusion path
will be used.
  • Loading branch information
anderslanglands authored and pmolodo committed Oct 23, 2024
1 parent 89e974f commit 76c2135
Show file tree
Hide file tree
Showing 8 changed files with 987 additions and 68 deletions.
1 change: 1 addition & 0 deletions pxr/imaging/plugin/hdEmbree/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pxr_plugin(hdEmbree
PUBLIC_CLASSES
config
instancer
light
mesh
meshSamplers
renderBuffer
Expand Down
171 changes: 171 additions & 0 deletions pxr/imaging/plugin/hdEmbree/light.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
//
// Copyright 2024 Pixar
//
// Licensed under the terms set forth in the LICENSE.txt file available at
// https://openusd.org/license.
//
#include "pxr/imaging/plugin/hdEmbree/light.h"

#include "light.h"
#include "pxr/imaging/plugin/hdEmbree/debugCodes.h"
#include "pxr/imaging/plugin/hdEmbree/renderParam.h"
#include "pxr/imaging/plugin/hdEmbree/renderer.h"

#include "pxr/imaging/hd/sceneDelegate.h"
#include "pxr/imaging/hio/image.h"

#include <embree3/rtcore_buffer.h>
#include <embree3/rtcore_scene.h>

#include <fstream>
#include <sstream>
#include <vector>

PXR_NAMESPACE_OPEN_SCOPE

HdEmbree_Light::HdEmbree_Light(SdfPath const& id, TfToken const& lightType)
: HdLight(id) {
if (id.IsEmpty()) {
return;
}

// Set the variant to the right type - Sync will fill rest of data
if (lightType == HdSprimTypeTokens->cylinderLight) {
_lightData.lightVariant = HdEmbree_Cylinder();
} else if (lightType == HdSprimTypeTokens->diskLight) {
_lightData.lightVariant = HdEmbree_Disk();
} else if (lightType == HdSprimTypeTokens->rectLight) {
// Get shape parameters
_lightData.lightVariant = HdEmbree_Rect();
} else if (lightType == HdSprimTypeTokens->sphereLight) {
_lightData.lightVariant = HdEmbree_Sphere();
} else {
TF_WARN("HdEmbree - Unrecognized light type: %s", lightType.GetText());
_lightData.lightVariant = HdEmbree_UnknownLight();
}
}

HdEmbree_Light::~HdEmbree_Light() = default;

void
HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate,
HdRenderParam *renderParam, HdDirtyBits *dirtyBits)
{
HD_TRACE_FUNCTION();
HF_MALLOC_TAG_FUNCTION();

HdEmbreeRenderParam *embreeRenderParam =
static_cast<HdEmbreeRenderParam*>(renderParam);

// calling this bumps the scene version and causes a re-render
embreeRenderParam->AcquireSceneForEdit();

SdfPath const& id = GetId();

// Get _lightData's transform. We'll only consider the first time sample for now
HdTimeSampleArray<GfMatrix4d, 1> xformSamples;
sceneDelegate->SampleTransform(id, &xformSamples);
_lightData.xformLightToWorld = GfMatrix4f(xformSamples.values[0]);
_lightData.xformWorldToLight = _lightData.xformLightToWorld.GetInverse();
_lightData.normalXformLightToWorld =
_lightData.xformWorldToLight.ExtractRotationMatrix().GetTranspose();

// Store luminance parameters
_lightData.intensity = sceneDelegate->GetLightParamValue(
id, HdLightTokens->intensity).GetWithDefault(1.0f);
_lightData.exposure = sceneDelegate->GetLightParamValue(
id, HdLightTokens->exposure).GetWithDefault(0.0f);
_lightData.color = sceneDelegate->GetLightParamValue(
id, HdLightTokens->color).GetWithDefault(GfVec3f{1.0f, 1.0f, 1.0f});
_lightData.normalize = sceneDelegate->GetLightParamValue(
id, HdLightTokens->normalize).GetWithDefault(false);
_lightData.colorTemperature = sceneDelegate->GetLightParamValue(
id, HdLightTokens->colorTemperature).GetWithDefault(6500.0f);
_lightData.enableColorTemperature = sceneDelegate->GetLightParamValue(
id, HdLightTokens->enableColorTemperature).GetWithDefault(false);

// Get visibility
_lightData.visible = sceneDelegate->GetVisible(id);

// Switch on the _lightData type and pull the relevant attributes from the scene
// delegate
std::visit([this, &id, &sceneDelegate](auto& typedLight) {
using T = std::decay_t<decltype(typedLight)>;
if constexpr (std::is_same_v<T, HdEmbree_UnknownLight>) {
// Do nothing
} else if constexpr (std::is_same_v<T, HdEmbree_Cylinder>) {
typedLight = HdEmbree_Cylinder{
sceneDelegate->GetLightParamValue(id, HdLightTokens->radius)
.GetWithDefault(0.5f),
sceneDelegate->GetLightParamValue(id, HdLightTokens->length)
.GetWithDefault(1.0f),
};
} else if constexpr (std::is_same_v<T, HdEmbree_Disk>) {
typedLight = HdEmbree_Disk{
sceneDelegate->GetLightParamValue(id, HdLightTokens->radius)
.GetWithDefault(0.5f),
};
} else if constexpr (std::is_same_v<T, HdEmbree_Rect>) {
typedLight = HdEmbree_Rect{
sceneDelegate->GetLightParamValue(id, HdLightTokens->width)
.Get<float>(),
sceneDelegate->GetLightParamValue(id, HdLightTokens->height)
.Get<float>(),
};
} else if constexpr (std::is_same_v<T, HdEmbree_Sphere>) {
typedLight = HdEmbree_Sphere{
sceneDelegate->GetLightParamValue(id, HdLightTokens->radius)
.GetWithDefault(0.5f),
};
} else {
static_assert(false, "non-exhaustive _LightVariant visitor");
}
}, _lightData.lightVariant);

if (const auto value = sceneDelegate->GetLightParamValue(
id, HdLightTokens->shapingFocus);
value.IsHolding<float>()) {
_lightData.shaping.focus = value.UncheckedGet<float>();
}

if (const auto value = sceneDelegate->GetLightParamValue(
id, HdLightTokens->shapingFocusTint);
value.IsHolding<GfVec3f>()) {
_lightData.shaping.focusTint = value.UncheckedGet<GfVec3f>();
}

if (const auto value = sceneDelegate->GetLightParamValue(
id, HdLightTokens->shapingConeAngle);
value.IsHolding<float>()) {
_lightData.shaping.coneAngle = value.UncheckedGet<float>();
}

if (const auto value = sceneDelegate->GetLightParamValue(
id, HdLightTokens->shapingConeSoftness);
value.IsHolding<float>()) {
_lightData.shaping.coneSoftness = value.UncheckedGet<float>();
}

HdEmbreeRenderer *renderer = embreeRenderParam->GetRenderer();
renderer->AddLight(id, this);

*dirtyBits &= ~HdLight::AllDirty;
}

HdDirtyBits
HdEmbree_Light::GetInitialDirtyBitsMask() const
{
return HdLight::AllDirty;
}

void
HdEmbree_Light::Finalize(HdRenderParam *renderParam)
{
auto* embreeParam = static_cast<HdEmbreeRenderParam*>(renderParam);

// Remove from renderer's light map
HdEmbreeRenderer *renderer = embreeParam->GetRenderer();
renderer->RemoveLight(GetId(), this);
}

PXR_NAMESPACE_CLOSE_SCOPE
109 changes: 109 additions & 0 deletions pxr/imaging/plugin/hdEmbree/light.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
//
// 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_LIGHT_H
#define PXR_IMAGING_PLUGIN_HD_EMBREE_LIGHT_H

#include "pxr/base/gf/vec3f.h"
#include "pxr/base/gf/matrix3f.h"
#include "pxr/base/gf/matrix4f.h"
#include "pxr/imaging/hd/light.h"

#include <embree3/rtcore_common.h>
#include <embree3/rtcore_geometry.h>

#include <limits>
#include <variant>

PXR_NAMESPACE_OPEN_SCOPE

class HdEmbreeRenderer;

struct HdEmbree_UnknownLight
{};
struct HdEmbree_Cylinder
{
float radius;
float length;
};

struct HdEmbree_Disk
{
float radius;
};

struct HdEmbree_Rect
{
float width;
float height;
};

struct HdEmbree_Sphere
{
float radius;
};

using HdEmbree_LightVariant = std::variant<
HdEmbree_UnknownLight,
HdEmbree_Cylinder,
HdEmbree_Disk,
HdEmbree_Rect,
HdEmbree_Sphere>;

struct HdEmbree_Shaping
{
GfVec3f focusTint;
float focus = 0.0f;
float coneAngle = 180.0f;
float coneSoftness = 0.0f;
};

struct HdEmbree_LightData
{
GfMatrix4f xformLightToWorld;
GfMatrix3f normalXformLightToWorld;
GfMatrix4f xformWorldToLight;
GfVec3f color;
float intensity = 1.0f;
float exposure = 0.0f;
float colorTemperature = 6500.0f;
bool enableColorTemperature = false;
HdEmbree_LightVariant lightVariant;
bool normalize = false;
bool visible = true;
HdEmbree_Shaping shaping;
};

class HdEmbree_Light final : public HdLight
{
public:
HdEmbree_Light(SdfPath const& id, TfToken const& lightType);
~HdEmbree_Light();

/// Synchronizes state from the delegate to this object.
void Sync(HdSceneDelegate* sceneDelegate,
HdRenderParam* renderParam,
HdDirtyBits* dirtyBits) override;

/// Returns the minimal set of dirty bits to place in the
/// change tracker for use in the first sync of this prim.
/// Typically this would be all dirty bits.
HdDirtyBits GetInitialDirtyBitsMask() const override;

void Finalize(HdRenderParam *renderParam) override;

HdEmbree_LightData const& LightData() const {
return _lightData;
}

private:
HdEmbree_LightData _lightData;
};


PXR_NAMESPACE_CLOSE_SCOPE

#endif
2 changes: 1 addition & 1 deletion pxr/imaging/plugin/hdEmbree/renderBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ class HdEmbreeRenderBuffer : public HdRenderBuffer
// For multisampled buffers: the input write buffer.
std::vector<uint8_t> _sampleBuffer;
// For multisampled buffers: the sample count buffer.
std::vector<uint8_t> _sampleCount;
std::vector<uint32_t> _sampleCount;

// The number of callers mapping this buffer.
std::atomic<int> _mappers;
Expand Down
21 changes: 19 additions & 2 deletions pxr/imaging/plugin/hdEmbree/renderDelegate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

#include "pxr/imaging/plugin/hdEmbree/config.h"
#include "pxr/imaging/plugin/hdEmbree/instancer.h"
#include "pxr/imaging/plugin/hdEmbree/light.h"
#include "pxr/imaging/plugin/hdEmbree/renderParam.h"
#include "pxr/imaging/plugin/hdEmbree/renderPass.h"

Expand Down Expand Up @@ -35,6 +36,10 @@ const TfTokenVector HdEmbreeRenderDelegate::SUPPORTED_SPRIM_TYPES =
{
HdPrimTypeTokens->camera,
HdPrimTypeTokens->extComputation,
HdPrimTypeTokens->cylinderLight,
HdPrimTypeTokens->diskLight,
HdPrimTypeTokens->rectLight,
HdPrimTypeTokens->sphereLight,
};

const TfTokenVector HdEmbreeRenderDelegate::SUPPORTED_BPRIM_TYPES =
Expand Down Expand Up @@ -147,7 +152,7 @@ HdEmbreeRenderDelegate::_Initialize()
// Store top-level embree objects inside a render param that can be
// passed to prims during Sync(). Also pass a handle to the render thread.
_renderParam = std::make_shared<HdEmbreeRenderParam>(
_rtcDevice, _rtcScene, &_renderThread, &_sceneVersion);
_rtcDevice, _rtcScene, &_renderThread, &_renderer, &_sceneVersion);

// Pass the scene handle to the renderer.
_renderer.SetScene(_rtcScene);
Expand Down Expand Up @@ -230,7 +235,7 @@ HdAovDescriptor
HdEmbreeRenderDelegate::GetDefaultAovDescriptor(TfToken const& name) const
{
if (name == HdAovTokens->color) {
return HdAovDescriptor(HdFormatUNorm8Vec4, true,
return HdAovDescriptor(HdFormatFloat32Vec4, true,
VtValue(GfVec4f(0.0f)));
} else if (name == HdAovTokens->normal || name == HdAovTokens->Neye) {
return HdAovDescriptor(HdFormatFloat32Vec3, false,
Expand Down Expand Up @@ -331,6 +336,12 @@ HdEmbreeRenderDelegate::CreateSprim(TfToken const& typeId,
return new HdCamera(sprimId);
} else if (typeId == HdPrimTypeTokens->extComputation) {
return new HdExtComputation(sprimId);
} else if (typeId == HdPrimTypeTokens->light ||
typeId == HdPrimTypeTokens->diskLight ||
typeId == HdPrimTypeTokens->rectLight ||
typeId == HdPrimTypeTokens->sphereLight ||
typeId == HdPrimTypeTokens->cylinderLight) {
return new HdEmbree_Light(sprimId, typeId);
} else {
TF_CODING_ERROR("Unknown Sprim Type %s", typeId.GetText());
}
Expand All @@ -347,6 +358,12 @@ HdEmbreeRenderDelegate::CreateFallbackSprim(TfToken const& typeId)
return new HdCamera(SdfPath::EmptyPath());
} else if (typeId == HdPrimTypeTokens->extComputation) {
return new HdExtComputation(SdfPath::EmptyPath());
} else if (typeId == HdPrimTypeTokens->light ||
typeId == HdPrimTypeTokens->diskLight ||
typeId == HdPrimTypeTokens->rectLight ||
typeId == HdPrimTypeTokens->sphereLight ||
typeId == HdPrimTypeTokens->cylinderLight) {
return new HdEmbree_Light(SdfPath::EmptyPath(), typeId);
} else {
TF_CODING_ERROR("Unknown Sprim Type %s", typeId.GetText());
}
Expand Down
Loading

0 comments on commit 76c2135

Please sign in to comment.