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