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