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
struct Light Light
@ NODE_VECTOR_MATH_MULTIPLY
struct Scene Scene
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 Brightness Control the brightness and contrast of the input color Vector Map input vector components with curves 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 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
~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)
bool has_surface_spatial_varying
void set_graph(ShaderGraph *graph)
void tag_update(Scene *scene)
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
ustring name
Definition graph/node.h:177
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
CCL_NAMESPACE_BEGIN struct Transform Transform