Blender  V3.3
hydra/light.cpp
Go to the documentation of this file.
1 /* SPDX-License-Identifier: Apache-2.0
2  * Copyright 2022 NVIDIA Corporation
3  * Copyright 2022 Blender Foundation */
4 
5 #include "hydra/light.h"
6 #include "hydra/session.h"
7 #include "scene/light.h"
8 #include "scene/scene.h"
9 #include "scene/shader.h"
10 #include "scene/shader_graph.h"
11 #include "scene/shader_nodes.h"
12 #include "util/hash.h"
13 
14 #include <pxr/imaging/hd/sceneDelegate.h>
15 #include <pxr/usd/sdf/assetPath.h>
16 
18 
19 extern Transform convert_transform(const GfMatrix4d &matrix);
20 
21 // clang-format off
23  (visibleInPrimaryRay)
24 );
25 // clang-format on
26 
27 HdCyclesLight::HdCyclesLight(const SdfPath &sprimId, const TfToken &lightType)
28  : HdLight(sprimId), _lightType(lightType)
29 {
30 }
31 
33 {
34 }
35 
37 {
38  return DirtyBits::DirtyTransform | DirtyBits::DirtyParams;
39 }
40 
41 void HdCyclesLight::Sync(HdSceneDelegate *sceneDelegate,
42  HdRenderParam *renderParam,
43  HdDirtyBits *dirtyBits)
44 {
45  if (*dirtyBits == DirtyBits::Clean) {
46  return;
47  }
48 
49  Initialize(renderParam);
50 
51  const SceneLock lock(renderParam);
52 
53  VtValue value;
54  const SdfPath &id = GetId();
55 
56  if (*dirtyBits & DirtyBits::DirtyTransform) {
57  const float metersPerUnit =
58  static_cast<HdCyclesSession *>(renderParam)->GetStageMetersPerUnit();
59 
60  const Transform tfm = transform_scale(make_float3(metersPerUnit)) *
61 #if PXR_VERSION >= 2011
62  convert_transform(sceneDelegate->GetTransform(id));
63 #else
65  sceneDelegate->GetLightParamValue(id, HdTokens->transform)
66  .Get<GfMatrix4d>());
67 #endif
68  _light->set_tfm(tfm);
69 
70  _light->set_co(transform_get_column(&tfm, 3));
71  _light->set_dir(-transform_get_column(&tfm, 2));
72 
73  if (_lightType == HdPrimTypeTokens->diskLight || _lightType == HdPrimTypeTokens->rectLight) {
74  _light->set_axisu(transform_get_column(&tfm, 0));
75  _light->set_axisv(transform_get_column(&tfm, 1));
76  }
77  }
78 
79  if (*dirtyBits & DirtyBits::DirtyParams) {
80  float3 strength = make_float3(1.0f, 1.0f, 1.0f);
81 
82  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->color);
83  if (!value.IsEmpty()) {
84  const auto color = value.Get<GfVec3f>();
85  strength = make_float3(color[0], color[1], color[2]);
86  }
87 
88  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->exposure);
89  if (!value.IsEmpty()) {
90  strength *= exp2(value.Get<float>());
91  }
92 
93  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->intensity);
94  if (!value.IsEmpty()) {
95  strength *= value.Get<float>();
96  }
97 
98  // Cycles lights are normalized by default, so need to scale intensity if Hydra light is not
99  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->normalize);
100  const bool normalize = value.IsHolding<bool>() && value.UncheckedGet<bool>();
101 
102  value = sceneDelegate->GetLightParamValue(id, _tokens->visibleInPrimaryRay);
103  if (!value.IsEmpty()) {
104  _light->set_use_camera(value.Get<bool>());
105  }
106 
107  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shadowEnable);
108  if (!value.IsEmpty()) {
109  _light->set_cast_shadow(value.Get<bool>());
110  }
111 
112  if (_lightType == HdPrimTypeTokens->distantLight) {
113  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->angle);
114  if (!value.IsEmpty()) {
115  _light->set_angle(GfDegreesToRadians(value.Get<float>()));
116  }
117  }
118  else if (_lightType == HdPrimTypeTokens->diskLight) {
119  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius);
120  if (!value.IsEmpty()) {
121  const float size = value.Get<float>() * 2.0f;
122  _light->set_sizeu(size);
123  _light->set_sizev(size);
124  }
125 
126  if (!normalize) {
127  const float radius = _light->get_sizeu() * 0.5f;
128  strength *= M_PI * radius * radius;
129  }
130  }
131  else if (_lightType == HdPrimTypeTokens->rectLight) {
132  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->width);
133  if (!value.IsEmpty()) {
134  _light->set_sizeu(value.Get<float>());
135  }
136 
137  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->height);
138  if (!value.IsEmpty()) {
139  _light->set_sizev(value.Get<float>());
140  }
141 
142  if (!normalize) {
143  strength *= _light->get_sizeu() * _light->get_sizeu();
144  }
145  }
146  else if (_lightType == HdPrimTypeTokens->sphereLight) {
147  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->radius);
148  if (!value.IsEmpty()) {
149  _light->set_size(value.Get<float>());
150  }
151 
152  bool shaping = false;
153 
154  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeAngle);
155  if (!value.IsEmpty()) {
156  _light->set_spot_angle(GfDegreesToRadians(value.Get<float>()) * 2.0f);
157  shaping = true;
158  }
159 
160  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingConeSoftness);
161  if (!value.IsEmpty()) {
162  _light->set_spot_smooth(value.Get<float>());
163  shaping = true;
164  }
165 
166  _light->set_light_type(shaping ? LIGHT_SPOT : LIGHT_POINT);
167 
168  if (!normalize) {
169  const float radius = _light->get_size();
170  strength *= M_PI * radius * radius * 4.0f;
171  }
172  }
173 
174  const bool visible = sceneDelegate->GetVisible(id);
175  // Disable invisible lights by zeroing the strength
176  // So 'LightManager::test_enabled_lights' updates the enabled flag correctly
177  if (!visible) {
178  strength = zero_float3();
179  }
180 
181  _light->set_strength(strength);
182  _light->set_is_enabled(visible);
183 
184  PopulateShaderGraph(sceneDelegate);
185  }
186  // Need to update shader graph when transform changes in case transform was baked into it
187  else if (_light->tfm_is_modified() && (_lightType == HdPrimTypeTokens->domeLight ||
188  _light->get_shader()->has_surface_spatial_varying)) {
189  PopulateShaderGraph(sceneDelegate);
190  }
191 
192  if (_light->is_modified()) {
193  _light->tag_update(lock.scene);
194  }
195 
196  *dirtyBits = DirtyBits::Clean;
197 }
198 
199 void HdCyclesLight::PopulateShaderGraph(HdSceneDelegate *sceneDelegate)
200 {
201  auto graph = new ShaderGraph();
202  ShaderNode *outputNode = nullptr;
203 
204  if (_lightType == HdPrimTypeTokens->domeLight) {
205  BackgroundNode *bgNode = graph->create_node<BackgroundNode>();
206  // Bake strength into shader graph, since only the shader is used for background lights
207  bgNode->set_color(_light->get_strength());
208  graph->add(bgNode);
209 
210  graph->connect(bgNode->output("Background"), graph->output()->input("Surface"));
211 
212  outputNode = bgNode;
213  }
214  else {
215  EmissionNode *emissionNode = graph->create_node<EmissionNode>();
216  emissionNode->set_color(one_float3());
217  emissionNode->set_strength(1.0f);
218  graph->add(emissionNode);
219 
220  graph->connect(emissionNode->output("Emission"), graph->output()->input("Surface"));
221 
222  outputNode = emissionNode;
223  }
224 
225  VtValue value;
226  const SdfPath &id = GetId();
227  bool hasSpatialVarying = false;
228  bool hasColorTemperature = false;
229 
230  if (sceneDelegate != nullptr) {
231  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->enableColorTemperature);
232  const bool enableColorTemperature = value.IsHolding<bool>() && value.UncheckedGet<bool>();
233 
234  if (enableColorTemperature) {
235  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->colorTemperature);
236  if (value.IsHolding<float>()) {
237  BlackbodyNode *blackbodyNode = graph->create_node<BlackbodyNode>();
238  blackbodyNode->set_temperature(value.UncheckedGet<float>());
239  graph->add(blackbodyNode);
240 
241  if (_lightType == HdPrimTypeTokens->domeLight) {
242  VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
243  mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
244  mathNode->set_vector2(_light->get_strength());
245  graph->add(mathNode);
246 
247  graph->connect(blackbodyNode->output("Color"), mathNode->input("Vector1"));
248  graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
249  }
250  else {
251  graph->connect(blackbodyNode->output("Color"), outputNode->input("Color"));
252  }
253 
254  hasColorTemperature = true;
255  }
256  }
257 
258  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->shapingIesFile);
259  if (value.IsHolding<SdfAssetPath>()) {
260  std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath();
261  if (filename.empty()) {
262  filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath();
263  }
264 
265  TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
266  coordNode->set_ob_tfm(_light->get_tfm());
267  coordNode->set_use_transform(true);
268  graph->add(coordNode);
269 
270  IESLightNode *iesNode = graph->create_node<IESLightNode>();
271  iesNode->set_filename(ustring(filename));
272 
273  graph->connect(coordNode->output("Normal"), iesNode->input("Vector"));
274  graph->connect(iesNode->output("Fac"), outputNode->input("Strength"));
275 
276  hasSpatialVarying = true;
277  }
278 
279  value = sceneDelegate->GetLightParamValue(id, HdLightTokens->textureFile);
280  if (value.IsHolding<SdfAssetPath>()) {
281  std::string filename = value.UncheckedGet<SdfAssetPath>().GetResolvedPath();
282  if (filename.empty()) {
283  filename = value.UncheckedGet<SdfAssetPath>().GetAssetPath();
284  }
285 
286  ImageSlotTextureNode *textureNode = nullptr;
287  if (_lightType == HdPrimTypeTokens->domeLight) {
288  Transform tfm = _light->get_tfm();
289  transform_set_column(&tfm, 3, zero_float3()); // Remove translation
290 
291  TextureCoordinateNode *coordNode = graph->create_node<TextureCoordinateNode>();
292  coordNode->set_ob_tfm(tfm);
293  coordNode->set_use_transform(true);
294  graph->add(coordNode);
295 
296  textureNode = graph->create_node<EnvironmentTextureNode>();
297  static_cast<EnvironmentTextureNode *>(textureNode)->set_filename(ustring(filename));
298  graph->add(textureNode);
299 
300  graph->connect(coordNode->output("Object"), textureNode->input("Vector"));
301 
302  hasSpatialVarying = true;
303  }
304  else {
305  GeometryNode *coordNode = graph->create_node<GeometryNode>();
306  graph->add(coordNode);
307 
308  textureNode = graph->create_node<ImageTextureNode>();
309  static_cast<ImageTextureNode *>(textureNode)->set_filename(ustring(filename));
310  graph->add(textureNode);
311 
312  graph->connect(coordNode->output("Parametric"), textureNode->input("Vector"));
313  }
314 
315  if (hasColorTemperature) {
316  VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
317  mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
318  graph->add(mathNode);
319 
320  graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
321  ShaderInput *const outputNodeInput = outputNode->input("Color");
322  graph->connect(outputNodeInput->link, mathNode->input("Vector2"));
323  graph->disconnect(outputNodeInput);
324  graph->connect(mathNode->output("Vector"), outputNodeInput);
325  }
326  else if (_lightType == HdPrimTypeTokens->domeLight) {
327  VectorMathNode *mathNode = graph->create_node<VectorMathNode>();
328  mathNode->set_math_type(NODE_VECTOR_MATH_MULTIPLY);
329  mathNode->set_vector2(_light->get_strength());
330  graph->add(mathNode);
331 
332  graph->connect(textureNode->output("Color"), mathNode->input("Vector1"));
333  graph->connect(mathNode->output("Vector"), outputNode->input("Color"));
334  }
335  else {
336  graph->connect(textureNode->output("Color"), outputNode->input("Color"));
337  }
338  }
339  }
340 
341  Shader *const shader = _light->get_shader();
342  shader->set_graph(graph);
343  shader->tag_update((Scene *)_light->get_owner());
344 
345  shader->has_surface_spatial_varying = hasSpatialVarying;
346 }
347 
348 void HdCyclesLight::Finalize(HdRenderParam *renderParam)
349 {
350  if (!_light) {
351  return;
352  }
353 
354  const SceneLock lock(renderParam);
355  const bool keep_nodes = static_cast<const HdCyclesSession *>(renderParam)->keep_nodes;
356 
357  if (!keep_nodes) {
358  lock.scene->delete_node(_light);
359  }
360 
361  _light = nullptr;
362 }
363 
364 void HdCyclesLight::Initialize(HdRenderParam *renderParam)
365 {
366  if (_light) {
367  return;
368  }
369 
370  const SceneLock lock(renderParam);
371 
372  _light = lock.scene->create_node<Light>();
373  _light->name = GetId().GetString();
374 
375  _light->set_random_id(hash_uint2(hash_string(_light->name.c_str()), 0));
376 
377  if (_lightType == HdPrimTypeTokens->domeLight) {
378  _light->set_light_type(LIGHT_BACKGROUND);
379  }
380  else if (_lightType == HdPrimTypeTokens->distantLight) {
381  _light->set_light_type(LIGHT_DISTANT);
382  }
383  else if (_lightType == HdPrimTypeTokens->diskLight) {
384  _light->set_light_type(LIGHT_AREA);
385  _light->set_round(true);
386  _light->set_size(1.0f);
387  }
388  else if (_lightType == HdPrimTypeTokens->rectLight) {
389  _light->set_light_type(LIGHT_AREA);
390  _light->set_round(false);
391  _light->set_size(1.0f);
392  }
393  else if (_lightType == HdPrimTypeTokens->sphereLight) {
394  _light->set_light_type(LIGHT_POINT);
395  _light->set_size(1.0f);
396  }
397 
398  _light->set_use_mis(true);
399  _light->set_use_camera(false);
400 
401  Shader *const shader = lock.scene->create_node<Shader>();
402  _light->set_shader(shader);
403 
404  // Create default shader graph
405  PopulateShaderGraph(nullptr);
406 }
407 
#define M_PI
Definition: BLI_math_base.h:20
@ NODE_VECTOR_MATH_MULTIPLY
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
volatile int lock
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
void Finalize(PXR_NS::HdRenderParam *renderParam) override
void Sync(PXR_NS::HdSceneDelegate *sceneDelegate, PXR_NS::HdRenderParam *renderParam, PXR_NS::HdDirtyBits *dirtyBits) override
Definition: hydra/light.cpp:41
~HdCyclesLight() override
Definition: hydra/light.cpp:32
HdCyclesLight(const PXR_NS::SdfPath &sprimId, const PXR_NS::TfToken &lightType)
Definition: hydra/light.cpp:27
PXR_NS::HdDirtyBits GetInitialDirtyBitsMask() const override
Definition: hydra/light.cpp:36
ShaderOutput * link
Definition: shader_graph.h:103
ShaderInput * input(const char *name)
ShaderOutput * output(const char *name)
bool has_surface_spatial_varying
Definition: scene/shader.h:112
void set_graph(ShaderGraph *graph)
void tag_update(Scene *scene)
Depsgraph * graph
static uint hash_string(const char *str)
Definition: hash.h:363
ccl_device_inline uint hash_uint2(uint kx, uint ky)
Definition: hash.h:70
#define HDCYCLES_NAMESPACE_CLOSE_SCOPE
Definition: hydra/config.h:17
TF_DEFINE_PRIVATE_TOKENS(_tokens,(visibleInPrimaryRay))
HDCYCLES_NAMESPACE_OPEN_SCOPE Transform convert_transform(const GfMatrix4d &matrix)
Definition: hydra/mesh.cpp:65
ccl_device_inline void transform_set_column(Transform *t, int column, float3 value)
ccl_device_inline float3 transform_get_column(const Transform *t, int column)
ccl_device_inline Transform transform_scale(float3 s)
@ LIGHT_AREA
Definition: kernel/types.h:459
@ LIGHT_DISTANT
Definition: kernel/types.h:457
@ LIGHT_SPOT
Definition: kernel/types.h:460
@ LIGHT_BACKGROUND
Definition: kernel/types.h:458
@ LIGHT_POINT
Definition: kernel/types.h:456
ccl_device_inline float3 one_float3()
Definition: math_float3.h:89
ccl_device_inline float3 zero_float3()
Definition: math_float3.h:80
#define make_float3(x, y, z)
Definition: metal/compat.h:204
vec_base< T, Size > normalize(const vec_base< T, Size > &v)