Skip to content

Commit 163813b

Browse files
committed
[hdEmbree] add direct camera visibility support for rect lights
1 parent 98807ac commit 163813b

File tree

5 files changed

+200
-2
lines changed

5 files changed

+200
-2
lines changed

pxr/imaging/plugin/hdEmbree/context.h

+4
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,14 @@
1313

1414
#include "pxr/base/gf/matrix4f.h"
1515
#include "pxr/base/vt/array.h"
16+
#include "pxr/base/vt/types.h"
1617

1718
#include <embree3/rtcore.h>
1819

1920
PXR_NAMESPACE_OPEN_SCOPE
2021

2122
class HdRprim;
23+
class HdEmbree_Light;
2224

2325
/// \class HdEmbreePrototypeContext
2426
///
@@ -51,6 +53,8 @@ struct HdEmbreeInstanceContext
5153
RTCScene rootScene;
5254
/// The instance id of this instance.
5355
int32_t instanceId;
56+
/// The light (if this is a light)
57+
HdEmbree_Light *light = nullptr;
5458
};
5559

5660

pxr/imaging/plugin/hdEmbree/light.cpp

+87-2
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include "pxr/imaging/hio/image.h"
1616

1717
#include <embree3/rtcore_buffer.h>
18+
#include <embree3/rtcore_device.h>
1819
#include <embree3/rtcore_scene.h>
1920

2021
#include <fstream>
@@ -78,6 +79,11 @@ _SyncLightTexture(const SdfPath& id, HdEmbree_LightData& light, HdSceneDelegate
7879
} // anonymous namespace
7980
PXR_NAMESPACE_OPEN_SCOPE
8081

82+
TF_DEFINE_PRIVATE_TOKENS(_tokens,
83+
((inputsVisibilityCamera, "inputs:visibility:camera"))
84+
((inputsVisibilityShadow, "inputs:visibility:shadow"))
85+
);
86+
8187
HdEmbree_Light::HdEmbree_Light(SdfPath const& id, TfToken const& lightType)
8288
: HdLight(id) {
8389
if (id.IsEmpty()) {
@@ -117,7 +123,8 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate,
117123
static_cast<HdEmbreeRenderParam*>(renderParam);
118124

119125
// calling this bumps the scene version and causes a re-render
120-
embreeRenderParam->AcquireSceneForEdit();
126+
RTCScene scene = embreeRenderParam->AcquireSceneForEdit();
127+
RTCDevice device = embreeRenderParam->GetEmbreeDevice();
121128

122129
SdfPath const& id = GetId();
123130

@@ -147,6 +154,13 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate,
147154

148155
// Get visibility
149156
_lightData.visible = sceneDelegate->GetVisible(id);
157+
_lightData.visible_camera = sceneDelegate->GetLightParamValue(
158+
id, _tokens->inputsVisibilityCamera).GetWithDefault(false);
159+
// XXX: Don't think we can get this to work in Embree unless it's built with
160+
// masking only solution would be to use rtcIntersect instead of rtcOccluded
161+
// for shadow rays, which maybe isn't the worst for a reference renderer
162+
_lightData.visible_shadow = sceneDelegate->GetLightParamValue(
163+
id, _tokens->inputsVisibilityShadow).GetWithDefault(false);
150164

151165
// Switch on the _lightData type and pull the relevant attributes from the scene
152166
// delegate
@@ -211,12 +225,71 @@ HdEmbree_Light::Sync(HdSceneDelegate *sceneDelegate,
211225
_lightData.shaping.coneSoftness = value.UncheckedGet<float>();
212226
}
213227

228+
_PopulateRtcLight(device, scene);
229+
214230
HdEmbreeRenderer *renderer = embreeRenderParam->GetRenderer();
215231
renderer->AddLight(id, this);
216232

217233
*dirtyBits &= ~HdLight::AllDirty;
218234
}
219235

236+
void
237+
HdEmbree_Light::_PopulateRtcLight(RTCDevice device, RTCScene scene)
238+
{
239+
_lightData.rtcMeshId = RTC_INVALID_GEOMETRY_ID;
240+
241+
// create the light geometry, if required
242+
if (_lightData.visible) {
243+
if (auto* rect = std::get_if<HdEmbree_Rect>(&_lightData.lightVariant))
244+
{
245+
// create _lightData mesh
246+
GfVec3f v0(-rect->width/2.0f, -rect->height/2.0f, 0.0f);
247+
GfVec3f v1( rect->width/2.0f, -rect->height/2.0f, 0.0f);
248+
GfVec3f v2( rect->width/2.0f, rect->height/2.0f, 0.0f);
249+
GfVec3f v3(-rect->width/2.0f, rect->height/2.0f, 0.0f);
250+
251+
v0 = _lightData.xformLightToWorld.Transform(v0);
252+
v1 = _lightData.xformLightToWorld.Transform(v1);
253+
v2 = _lightData.xformLightToWorld.Transform(v2);
254+
v3 = _lightData.xformLightToWorld.Transform(v3);
255+
256+
_lightData.rtcGeometry = rtcNewGeometry(device,
257+
RTC_GEOMETRY_TYPE_QUAD);
258+
GfVec3f* vertices = static_cast<GfVec3f*>(
259+
rtcSetNewGeometryBuffer(_lightData.rtcGeometry,
260+
RTC_BUFFER_TYPE_VERTEX,
261+
0,
262+
RTC_FORMAT_FLOAT3,
263+
sizeof(GfVec3f),
264+
4));
265+
vertices[0] = v0;
266+
vertices[1] = v1;
267+
vertices[2] = v2;
268+
vertices[3] = v3;
269+
270+
unsigned* index = static_cast<unsigned*>(
271+
rtcSetNewGeometryBuffer(_lightData.rtcGeometry,
272+
RTC_BUFFER_TYPE_INDEX,
273+
0,
274+
RTC_FORMAT_UINT4,
275+
sizeof(unsigned)*4,
276+
1));
277+
index[0] = 0; index[1] = 1; index[2] = 2; index[3] = 3;
278+
279+
auto ctx = std::make_unique<HdEmbreeInstanceContext>();
280+
ctx->light = this;
281+
rtcSetGeometryTimeStepCount(_lightData.rtcGeometry, 1);
282+
rtcCommitGeometry(_lightData.rtcGeometry);
283+
_lightData.rtcMeshId = rtcAttachGeometry(scene, _lightData.rtcGeometry);
284+
if (_lightData.rtcMeshId == RTC_INVALID_GEOMETRY_ID) {
285+
TF_WARN("could not create rect mesh for %s", GetId().GetAsString().c_str());
286+
} else {
287+
rtcSetGeometryUserData(_lightData.rtcGeometry, ctx.release());
288+
}
289+
}
290+
}
291+
}
292+
220293
HdDirtyBits
221294
HdEmbree_Light::GetInitialDirtyBitsMask() const
222295
{
@@ -227,10 +300,22 @@ void
227300
HdEmbree_Light::Finalize(HdRenderParam *renderParam)
228301
{
229302
auto* embreeParam = static_cast<HdEmbreeRenderParam*>(renderParam);
303+
RTCScene scene = embreeParam->AcquireSceneForEdit();
230304

231-
// Remove from renderer's light map
305+
// First, remove from renderer's light map
232306
HdEmbreeRenderer *renderer = embreeParam->GetRenderer();
233307
renderer->RemoveLight(GetId(), this);
308+
309+
// Then clean up the associated embree objects
310+
if (_lightData.rtcMeshId != RTC_INVALID_GEOMETRY_ID) {
311+
delete static_cast<HdEmbreeInstanceContext*>(
312+
rtcGetGeometryUserData(_lightData.rtcGeometry));
313+
314+
rtcDetachGeometry(scene, _lightData.rtcMeshId);
315+
rtcReleaseGeometry(_lightData.rtcGeometry);
316+
_lightData.rtcMeshId = RTC_INVALID_GEOMETRY_ID;
317+
_lightData.rtcGeometry = nullptr;
318+
}
234319
}
235320

236321
PXR_NAMESPACE_CLOSE_SCOPE

pxr/imaging/plugin/hdEmbree/light.h

+6
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,11 @@ struct HdEmbree_LightData
8888
HdEmbree_LightVariant lightVariant;
8989
bool normalize = false;
9090
bool visible = true;
91+
bool visible_camera = true;
92+
bool visible_shadow = true;
9193
HdEmbree_Shaping shaping;
94+
unsigned rtcMeshId = RTC_INVALID_GEOMETRY_ID;
95+
RTCGeometry rtcGeometry = nullptr;
9296
};
9397

9498
class HdEmbree_Light final : public HdLight
@@ -118,6 +122,8 @@ class HdEmbree_Light final : public HdLight
118122
}
119123

120124
private:
125+
void _PopulateRtcLight(RTCDevice device, RTCScene scene);
126+
121127
HdEmbree_LightData _lightData;
122128
};
123129

pxr/imaging/plugin/hdEmbree/mesh.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -894,6 +894,7 @@ HdEmbreeMesh::_PopulateRtMesh(HdSceneDelegate* sceneDelegate,
894894
HdEmbreeInstanceContext *ctx = new HdEmbreeInstanceContext;
895895
ctx->rootScene = _rtcMeshScene;
896896
ctx->instanceId = i;
897+
ctx->light = nullptr;
897898
rtcSetGeometryUserData(geom,ctx);
898899
_rtcInstanceGeometries[i] = geom;
899900
}

pxr/imaging/plugin/hdEmbree/renderer.cpp

+102
Original file line numberDiff line numberDiff line change
@@ -422,6 +422,20 @@ _SampleCylinder(GfMatrix4f const& xf, GfMatrix3f const& normalXform,
422422
};
423423
}
424424

425+
_ShapeSample
426+
_IntersectAreaLight(HdEmbree_LightData const& light, RTCRayHit const& rayHit)
427+
{
428+
// XXX: just rect lights at the moment, need to do the others
429+
auto const& rect = std::get<HdEmbree_Rect>(light.lightVariant);
430+
431+
return _ShapeSample {
432+
_CalculateHitPosition(rayHit),
433+
GfVec3f(rayHit.hit.Ng_x, rayHit.hit.Ng_y, rayHit.hit.Ng_z),
434+
GfVec2f(1.0f - rayHit.hit.u, rayHit.hit.v),
435+
_AreaRect(light.xformLightToWorld, rect.width, rect.height)
436+
};
437+
}
438+
425439
GfVec3f
426440
_EvalLightBasic(HdEmbree_LightData const& light)
427441
{
@@ -1275,6 +1289,43 @@ _CosineWeightedDirection(GfVec2f const& uniform_float)
12751289
return dir;
12761290
}
12771291

1292+
bool
1293+
HdEmbreeRenderer::_RayShouldContinue(RTCRayHit const& rayHit) const {
1294+
if (rayHit.hit.geomID == RTC_INVALID_GEOMETRY_ID) {
1295+
// missed, don't continue
1296+
return false;
1297+
}
1298+
1299+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1300+
// not hit an instance, but a "raw" geometry. This should be a light
1301+
const HdEmbreeInstanceContext *instanceContext =
1302+
static_cast<HdEmbreeInstanceContext*>(
1303+
rtcGetGeometryUserData(rtcGetGeometry(_scene,
1304+
rayHit.hit.geomID)));
1305+
1306+
if (instanceContext->light == nullptr) {
1307+
// if this isn't a light, don't know what this is
1308+
return false;
1309+
}
1310+
1311+
auto const& light = instanceContext->light->LightData();
1312+
1313+
if ((rayHit.ray.mask & HdEmbree_RayMask::Camera)
1314+
&& !light.visible_camera) {
1315+
return true;
1316+
} else if ((rayHit.ray.mask & HdEmbree_RayMask::Shadow)
1317+
&& !light.visible_shadow) {
1318+
return true;
1319+
} else {
1320+
return false;
1321+
}
1322+
}
1323+
1324+
// XXX: otherwise this is a regular geo. we should handle visibility here
1325+
// too eventually
1326+
return false;
1327+
}
1328+
12781329
void
12791330
HdEmbreeRenderer::_TraceRay(unsigned int x, unsigned int y,
12801331
GfVec3f const &origin, GfVec3f const &dir,
@@ -1306,6 +1357,13 @@ HdEmbreeRenderer::_TraceRay(unsigned int x, unsigned int y,
13061357
rayHit.hit.Ng_z = -rayHit.hit.Ng_z;
13071358
}
13081359

1360+
if (_RayShouldContinue(rayHit)) {
1361+
GfVec3f hitPos = _CalculateHitPosition(rayHit);
1362+
1363+
_TraceRay(x, y, hitPos + dir * _rayHitContinueBias, dir, random);
1364+
return;
1365+
}
1366+
13091367
// Write AOVs to attachments that aren't converged.
13101368
for (size_t i = 0; i < _aovBindings.size(); ++i) {
13111369
HdEmbreeRenderBuffer *renderBuffer =
@@ -1361,6 +1419,11 @@ HdEmbreeRenderer::_ComputeId(RTCRayHit const& rayHit, TfToken const& idType,
13611419
return false;
13621420
}
13631421

1422+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1423+
// not hit an instance, but a "raw" geometry. This should be a light
1424+
return false;
1425+
}
1426+
13641427
// Get the instance and prototype context structures for the hit prim.
13651428
// We don't use embree's multi-level instancing; we
13661429
// flatten everything in hydra. So instID[0] should always be correct.
@@ -1401,6 +1464,11 @@ HdEmbreeRenderer::_ComputeDepth(RTCRayHit const& rayHit,
14011464
return false;
14021465
}
14031466

1467+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1468+
// not hit an instance, but a "raw" geometry. This should be a light
1469+
return false;
1470+
}
1471+
14041472
if (clip) {
14051473
GfVec3f hitPos = _CalculateHitPosition(rayHit);
14061474

@@ -1424,6 +1492,11 @@ HdEmbreeRenderer::_ComputeNormal(RTCRayHit const& rayHit,
14241492
return false;
14251493
}
14261494

1495+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1496+
// not hit an instance, but a "raw" geometry. This should be a light
1497+
return false;
1498+
}
1499+
14271500
// We don't use embree's multi-level instancing; we
14281501
// flatten everything in hydra. So instID[0] should always be correct.
14291502
const HdEmbreeInstanceContext *instanceContext =
@@ -1462,6 +1535,11 @@ HdEmbreeRenderer::_ComputePrimvar(RTCRayHit const& rayHit,
14621535
return false;
14631536
}
14641537

1538+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1539+
// not hit an instance, but a "raw" geometry. This should be a light
1540+
return false;
1541+
}
1542+
14651543
// We don't use embree's multi-level instancing; we
14661544
// flatten everything in hydra. So instID[0] should always be correct.
14671545
const HdEmbreeInstanceContext *instanceContext =
@@ -1548,6 +1626,30 @@ HdEmbreeRenderer::_ComputeColor(RTCRayHit const& rayHit,
15481626
return domeColor;
15491627
}
15501628

1629+
if (rayHit.hit.instID[0] == RTC_INVALID_GEOMETRY_ID) {
1630+
// if it's not an instance then it's almost certainly a light
1631+
const HdEmbreeInstanceContext *instanceContext =
1632+
static_cast<HdEmbreeInstanceContext*>(
1633+
rtcGetGeometryUserData(rtcGetGeometry(_scene,
1634+
rayHit.hit.geomID)));
1635+
1636+
// if we hit a light, just evaluate the light directly
1637+
if (instanceContext->light != nullptr) {
1638+
auto const& light = instanceContext->light->LightData();
1639+
_ShapeSample ss = _IntersectAreaLight(light, rayHit);
1640+
_LightSample ls = _EvalAreaLight(light, ss,
1641+
GfVec3f(rayHit.ray.org_x, rayHit.ray.org_y, rayHit.ray.org_z));
1642+
1643+
return GfVec4f(ls.Li[0], ls.Li[1], ls.Li[2], 1.0f);
1644+
} else {
1645+
// should never get here. magenta warning!
1646+
TF_WARN("Unexpected runtime state - hit an an embree instance "
1647+
"that wasn't a geo or light");
1648+
return GfVec4f(1.0f, 0.0f, 1.0f, 1.0f);
1649+
}
1650+
1651+
}
1652+
15511653
// Get the instance and prototype context structures for the hit prim.
15521654
// We don't use embree's multi-level instancing; we
15531655
// flatten everything in hydra. So instID[0] should always be correct.

0 commit comments

Comments
 (0)