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