Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qquick3dabstractlight.cpp
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3// Qt-Security score:significant reason:default
4
5
9
10#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
11#include <QtQuick3DUtils/private/qssgutils_p.h>
12
14
15/*!
16 \qmltype Light
17 \inherits Node
18 \inqmlmodule QtQuick3D
19 \brief An uncreatable abstract base type for all lights.
20
21 Light itself is an uncreatable base for all of its subtypes. The subtypes provide multiple
22 options to determine the style of the light.
23
24 For usage examples, see \l{Qt Quick 3D - Lights Example}.
25
26 \sa DirectionalLight, PointLight
27*/
28
29/*!
30 \qmlproperty color Light::color
31 This property defines the color applied to models illuminated by this light.
32 The default value is white, rgb(255, 255, 255).
33 */
34
35/*!
36 \qmlproperty color Light::ambientColor
37 The property defines the ambient color applied to materials before being lit by this light.
38 The default value is black, rgb(0, 0, 0).
39 */
40
41/*!
42 \qmlproperty real Light::brightness
43 This property defines an overall multiplier for this light’s effects.
44 The default value is 1.
45*/
46
47/*!
48 \qmlproperty Node Light::scope
49
50 The property allows the selection of a Node in the scene. Only that node
51 and its children are affected by this light. By default the value is null,
52 which indicates no scope selected.
53
54 \note Scoped lights cannot cast real-time shadows, meaning a Light with a
55 scope set should not set \l castsShadow to true. They can however generate
56 baked shadows when \l bakeMode is set to Light.BakeModeAll.
57*/
58
59/*!
60 \qmlproperty bool Light::castsShadow
61
62 When this property is enabled, the light will cast (real-time) shadows. The
63 default value is false.
64
65 \note When \l bakeMode is set to Light.BakeModeAll, this property has no
66 effect. A fully baked light always has baked shadows, but it will never
67 participate in real-time shadow mapping.
68*/
69
70/*!
71 \qmlproperty real Light::shadowBias
72 This property is used to tweak the shadowing effect when objects
73 are casting shadows on themselves. The value tries to approximate the offset
74 in world space so it needs to be tweaked depending on the size of your scene.
75
76 The default value is \c{10}
77*/
78
79/*!
80 \qmlproperty real Light::shadowFactor
81 This property determines how dark the cast shadows should be. The value range is [0, 100], where
82 0 means no shadows and 100 means the light is fully shadowed.
83
84 The default value is \c{75}.
85*/
86
87/*!
88 \qmlproperty enumeration Light::shadowMapQuality
89 The property sets the quality of the shadow map created for shadow rendering. Lower quality uses
90 less resources, but produces lower quality shadows while higher quality uses more resources, but
91 produces better quality shadows.
92
93 Supported quality values are:
94 \value Light.ShadowMapQualityLow Render shadowmap using a 256x256 texture.
95 \value Light.ShadowMapQualityMedium Render shadowmap using a 512x512 texture.
96 \value Light.ShadowMapQualityHigh Render shadowmap using a 1024x1024 texture.
97 \value Light.ShadowMapQualityVeryHigh Render shadowmap using a 2048x2048 texture.
98 \value Light.ShadowMapQualityUltra Render shadowmap using a 4096x4096 texture.
99
100 The default value is \c Light.ShadowMapQualityLow
101*/
102
103/*!
104 \qmlproperty real Light::shadowMapFar
105 The property determines the maximum distance for the shadow map. Smaller
106 values improve the precision and effects of the map.
107 The default value is 5000. Unit is points in local coordinate space.
108*/
109
110/*!
111 \qmlproperty real Light::shadowFilter
112 This property sets how much blur is applied to the shadows.
113
114 The default value is 5.
115
116 \deprecated [6.8] No longer used for anything, use \l{Light::}{pcfFactor} instead.
117
118 \sa Light::softShadowQuality
119*/
120
121/*!
122 \qmlproperty enumeration Light::bakeMode
123 The property controls if the light is active in baked lighting, such as
124 when generating lightmaps.
125
126 \value Light.BakeModeDisabled The light is not used in baked lighting.
127
128 \value Light.BakeModeIndirect Indirect lighting contribution (for global
129 illumination) is baked for this light. Direct lighting (diffuse, specular,
130 real-time shadow mapping) is calculated normally for the light at run time.
131 At run time, when not in baking mode, the renderer will attempt to sample
132 the lightmap to get the indirect lighting data and combine that with the
133 results of the real-time calculations.
134
135 \value Light.BakeModeAll Both direct (diffuse, shadow) and indirect
136 lighting is baked for this light. The light will not have a specular
137 contribution and will not generate realtime shadow maps, but it will always
138 have baked shadows. At run time, when not in baking mode, the renderer will
139 attempt to sample the lightmap in place of the standard, real-time
140 calculations for diffuse lighting and shadow mapping.
141
142 The default value is \c Light.BakeModeDisabled
143
144 \note Just as with \l Model::usedInBakedLighting, designers and developers
145 must always evaluate on a per-light basis if the light is suitable to take
146 part in baked lighting.
147
148 \warning Lights with dynamically changing properties, for example, animated
149 position, rotation, or other properties, are not suitable for participating
150 in baked lighting.
151
152 This property is relevant both when baking and when using lightmaps. A
153 consistent state between the baking run and the subsequent runs that use
154 the generated data is essential. Changing to a different value will not
155 change the previously generated and persistently stored data in the
156 lightmaps, the engine's rendering behavior will however follow the
157 property's current value.
158
159 For more information on how to bake lightmaps, see the \l {Lightmaps and
160 Global Illumination}.
161
162 \sa Model::usedInBakedLighting, Model::bakedLightmap, Lightmapper, {Lightmaps and Global Illumination}
163*/
164
165/*!
166 \qmlproperty enumeration Light::softShadowQuality
167 \since 6.8
168
169 The property suggests the soft shadow quality that should be used when
170 shadows are enabled. The actual number of samples used when rendering shadows
171 is determined by highest quality set by any light used by a material.
172
173 \value Light.Hard No soft shadows.
174 \value Light.PCF4 Percentage-closer filtering soft shadows with 4 samples.
175 \value Light.PCF8 Percentage-closer filtering soft shadows with 8 samples.
176 \value Light.PCF16 Percentage-closer filtering soft shadows with 16 samples.
177 \value Light.PCF32 Percentage-closer filtering soft shadows with 32 samples.
178 \value Light.PCF64 Percentage-closer filtering soft shadows with 64 samples.
179
180 Default value: \c Light.PCF4
181
182 \note Currently values larger than 16 samples will be treated as 16 samples.
183
184 \sa Light::pcfFactor, Light::shadowFilter
185*/
186
187/*!
188 \qmlproperty real Light::pcfFactor
189 \since 6.8
190
191 The property controls the PCF (percentage-closer filtering) factor. This
192 value tries to approximate the radius of a PCF filtering in world space.
193
194 \note PCF needs to be set in \l{Light::}{softShadowQuality} for this property
195 to have an effect.
196
197 Default value: \c{2.0}
198
199 \sa Light::softShadowQuality
200*/
201
202/*!
203 \qmlproperty bool Light::use32BitShadowmap
204 \since 6.9
205
206 The property controls if a 32-bit shadowmap depth buffer should be used for the light.
207
208 Default value: \c{false}
209
210 \sa Light::castsShadow
211*/
212
213QQuick3DAbstractLight::QQuick3DAbstractLight(QQuick3DNodePrivate &dd, QQuick3DNode *parent)
214 : QQuick3DNode(dd, parent)
215 , m_color(Qt::white)
216 , m_ambientColor(Qt::black) {}
217
218QQuick3DAbstractLight::~QQuick3DAbstractLight() {}
219
220QColor QQuick3DAbstractLight::color() const
221{
222 return m_color;
223}
224
225QColor QQuick3DAbstractLight::ambientColor() const
226{
227 return m_ambientColor;
228}
229
230float QQuick3DAbstractLight::brightness() const
231{
232 return m_brightness;
233}
234
235QQuick3DNode *QQuick3DAbstractLight::scope() const
236{
237 return m_scope;
238}
239
240bool QQuick3DAbstractLight::castsShadow() const
241{
242 return m_castsShadow;
243}
244
245float QQuick3DAbstractLight::shadowBias() const
246{
247 return m_shadowBias;
248}
249
250float QQuick3DAbstractLight::shadowFactor() const
251{
252 return m_shadowFactor;
253}
254
255QQuick3DAbstractLight::QSSGShadowMapQuality QQuick3DAbstractLight::shadowMapQuality() const
256{
257 return m_shadowMapQuality;
258}
259
260QQuick3DAbstractLight::QSSGSoftShadowQuality QQuick3DAbstractLight::softShadowQuality() const
261{
262 return m_softShadowQuality;
263}
264
265float QQuick3DAbstractLight::shadowMapFar() const
266{
267 return m_shadowMapFar;
268}
269
270float QQuick3DAbstractLight::shadowFilter() const
271{
272 return m_shadowFilter;
273}
274
275QQuick3DAbstractLight::QSSGBakeMode QQuick3DAbstractLight::bakeMode() const
276{
277 return m_bakeMode;
278}
279
280float QQuick3DAbstractLight::pcfFactor() const
281{
282 return m_pcfFactor;
283}
284
285bool QQuick3DAbstractLight::use32BitShadowmap() const
286{
287 return m_use32BitShadowmap;
288}
289
290void QQuick3DAbstractLight::markAllDirty()
291{
292 m_dirtyFlags = DirtyFlags(DirtyFlag::ShadowDirty)
293 | DirtyFlags(DirtyFlag::ColorDirty)
294 | DirtyFlags(DirtyFlag::BrightnessDirty)
295 | DirtyFlags(DirtyFlag::FadeDirty)
296 | DirtyFlags(DirtyFlag::AreaDirty)
297 | DirtyFlags(DirtyFlag::BakeModeDirty);
298 QQuick3DNode::markAllDirty();
299}
300
301void QQuick3DAbstractLight::setColor(const QColor &color)
302{
303 if (m_color == color)
304 return;
305
306 m_color = color;
307 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty);
308 emit colorChanged();
309 update();
310}
311
312void QQuick3DAbstractLight::setAmbientColor(const QColor &ambientColor)
313{
314 if (m_ambientColor == ambientColor)
315 return;
316
317 m_ambientColor = ambientColor;
318 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty);
319 emit ambientColorChanged();
320 update();
321}
322
323void QQuick3DAbstractLight::setBrightness(float brightness)
324{
325 if (qFuzzyCompare(m_brightness, brightness))
326 return;
327
328 m_brightness = brightness;
329 m_dirtyFlags.setFlag(DirtyFlag::BrightnessDirty);
330 emit brightnessChanged();
331 update();
332}
333
334void QQuick3DAbstractLight::setScope(QQuick3DNode *scope)
335{
336 if (m_scope == scope)
337 return;
338
339 m_scope = scope;
340 emit scopeChanged();
341 update();
342}
343
344void QQuick3DAbstractLight::setCastsShadow(bool castsShadow)
345{
346 if (m_castsShadow == castsShadow)
347 return;
348
349 m_castsShadow = castsShadow;
350 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
351 emit castsShadowChanged();
352 update();
353}
354
355void QQuick3DAbstractLight::setShadowBias(float shadowBias)
356{
357 if (qFuzzyCompare(m_shadowBias, shadowBias))
358 return;
359
360 m_shadowBias = shadowBias;
361 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
362 emit shadowBiasChanged();
363 update();
364}
365
366void QQuick3DAbstractLight::setShadowFactor(float shadowFactor)
367{
368 shadowFactor = qBound(0.0f, shadowFactor, 100.0f);
369 if (qFuzzyCompare(m_shadowFactor, shadowFactor))
370 return;
371
372 m_shadowFactor = shadowFactor;
373 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
374 emit shadowFactorChanged();
375 update();
376}
377
378void QQuick3DAbstractLight::setShadowMapQuality(
379 QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality)
380{
381 if (m_shadowMapQuality == shadowMapQuality)
382 return;
383
384 m_shadowMapQuality = shadowMapQuality;
385 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
386 emit shadowMapQualityChanged();
387 update();
388}
389
390void QQuick3DAbstractLight::setSoftShadowQuality(QSSGSoftShadowQuality softShadowQuality)
391{
392 if (m_softShadowQuality == softShadowQuality)
393 return;
394
395 m_softShadowQuality = softShadowQuality;
396 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
397 emit softShadowQualityChanged();
398 update();
399}
400
401void QQuick3DAbstractLight::setBakeMode(QQuick3DAbstractLight::QSSGBakeMode bakeMode)
402{
403 if (m_bakeMode == bakeMode)
404 return;
405
406 m_bakeMode = bakeMode;
407 m_dirtyFlags.setFlag(DirtyFlag::BakeModeDirty);
408 emit bakeModeChanged();
409 update();
410}
411
412void QQuick3DAbstractLight::setPcfFactor(float pcfFactor)
413{
414 if (m_pcfFactor == pcfFactor)
415 return;
416
417 m_pcfFactor = pcfFactor;
418 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
419 emit pcfFactorChanged();
420 update();
421}
422
423void QQuick3DAbstractLight::setUse32BitShadowmap(bool use32BitShadowmap)
424{
425 if (m_use32BitShadowmap == use32BitShadowmap)
426 return;
427
428 m_use32BitShadowmap = use32BitShadowmap;
429 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
430 emit use32BitShadowmapChanged();
431 update();
432}
433
434void QQuick3DAbstractLight::setShadowMapFar(float shadowMapFar)
435{
436 if (qFuzzyCompare(m_shadowMapFar, shadowMapFar))
437 return;
438
439 m_shadowMapFar = shadowMapFar;
440 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
441 emit shadowMapFarChanged();
442 update();
443}
444
445void QQuick3DAbstractLight::setShadowFilter(float shadowFilter)
446{
447 if (qFuzzyCompare(m_shadowFilter, shadowFilter))
448 return;
449
450 m_shadowFilter = shadowFilter;
451 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
452 emit shadowFilterChanged();
453 update();
454}
455
456quint32 QQuick3DAbstractLight::mapToShadowResolution(QSSGShadowMapQuality quality)
457{
458 switch (quality) {
459 case QSSGShadowMapQuality::ShadowMapQualityMedium:
460 return 512;
461 case QSSGShadowMapQuality::ShadowMapQualityHigh:
462 return 1024;
463 case QSSGShadowMapQuality::ShadowMapQualityVeryHigh:
464 return 2048;
465 case QSSGShadowMapQuality::ShadowMapQualityUltra:
466 return 4096;
467 default:
468 break;
469 }
470 return 256;
471}
472
473QSSGRenderGraphObject *QQuick3DAbstractLight::updateSpatialNode(QSSGRenderGraphObject *node)
474{
475 Q_ASSERT_X(node, __FUNCTION__, "Node must have been created in parent class.");
476
477 QQuick3DNode::updateSpatialNode(node);
478
479 QSSGRenderLight *light = static_cast<QSSGRenderLight *>(node);
480
481 if (m_dirtyFlags.toInt() != 0) // Some flag was set, so mark the light dirty!
482 light->markDirty(QSSGRenderLight::DirtyFlag::LightDirty);
483
484 if (m_dirtyFlags.testFlag(DirtyFlag::ColorDirty)) {
485 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty, false);
486 light->m_diffuseColor = QSSGUtils::color::sRGBToLinear(m_color).toVector3D();
487 light->m_specularColor = light->m_diffuseColor;
488 light->m_ambientColor = QSSGUtils::color::sRGBToLinear(m_ambientColor).toVector3D();
489 }
490
491 if (m_dirtyFlags.testFlag(DirtyFlag::BrightnessDirty)) {
492 m_dirtyFlags.setFlag(DirtyFlag::BrightnessDirty, false);
493 light->m_brightness = m_brightness;
494 }
495
496 if (m_dirtyFlags.testFlag(DirtyFlag::ShadowDirty)) {
497 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty, false);
498 light->m_castShadow = m_castsShadow;
499 light->m_shadowBias = m_shadowBias;
500 light->m_shadowFactor = m_shadowFactor;
501 light->m_shadowMapRes = mapToShadowResolution(m_shadowMapQuality);
502 light->m_softShadowQuality = static_cast<QSSGRenderLight::SoftShadowQuality>(m_softShadowQuality);
503 light->m_shadowMapFar = m_shadowMapFar;
504 light->m_shadowFilter = m_shadowFilter;
505 light->m_pcfFactor = m_pcfFactor;
506 light->m_use32BitShadowmap = m_use32BitShadowmap;
507 }
508
509 if (m_dirtyFlags.testFlag(DirtyFlag::BakeModeDirty)) {
510 m_dirtyFlags.setFlag(DirtyFlag::BakeModeDirty, false);
511 light->m_bakingEnabled = m_bakeMode != QSSGBakeMode::BakeModeDisabled;
512 light->m_fullyBaked = m_bakeMode == QSSGBakeMode::BakeModeAll;
513 }
514
515 if (m_scope) {
516 // Special case:
517 // If the 'scope' is 'this' and this is the first call, then the spatial node is the one we just created.
518 // This is not unlikely, as it can make sense to put all child nodes that should receive light under the light node...
519 if (m_scope == this)
520 light->m_scope = light;
521 else
522 light->m_scope = static_cast<QSSGRenderNode*>(QQuick3DObjectPrivate::get(m_scope)->spatialNode);
523 } else {
524 light->m_scope = nullptr;
525 }
526
527 return node;
528}
529
530QT_END_NAMESPACE
Combined button and popup list for selecting options.