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 controls the soft shadow quality.
168
169 \value Light.Hard No soft shadows.
170 \value Light.PCF4 Percentage-closer filtering soft shadows with 4 samples.
171 \value Light.PCF8 Percentage-closer filtering soft shadows with 8 samples.
172 \value Light.PCF16 Percentage-closer filtering soft shadows with 16 samples.
173 \value Light.PCF32 Percentage-closer filtering soft shadows with 32 samples.
174 \value Light.PCF64 Percentage-closer filtering soft shadows with 64 samples.
175
176 Default value: \c Light.PCF4
177
178 \note Currently values larger than 16 samples will be treated as 16 samples.
179
180 \sa Light::pcfFactor, Light::shadowFilter
181*/
182
183/*!
184 \qmlproperty real Light::pcfFactor
185 \since 6.8
186
187 The property controls the PCF (percentage-closer filtering) factor. This
188 value tries to approximate the radius of a PCF filtering in world space.
189
190 \note PCF needs to be set in \l{Light::}{softShadowQuality} for this property
191 to have an effect.
192
193 Default value: \c{2.0}
194
195 \sa Light::softShadowQuality
196*/
197
198/*!
199 \qmlproperty bool Light::use32BitShadowmap
200 \since 6.9
201
202 The property controls if a 32-bit shadowmap depth buffer should be used for the light.
203
204 Default value: \c{false}
205
206 \sa Light::castsShadow
207*/
208
209QQuick3DAbstractLight::QQuick3DAbstractLight(QQuick3DNodePrivate &dd, QQuick3DNode *parent)
210 : QQuick3DNode(dd, parent)
211 , m_color(Qt::white)
212 , m_ambientColor(Qt::black) {}
213
214QQuick3DAbstractLight::~QQuick3DAbstractLight() {}
215
216QColor QQuick3DAbstractLight::color() const
217{
218 return m_color;
219}
220
221QColor QQuick3DAbstractLight::ambientColor() const
222{
223 return m_ambientColor;
224}
225
226float QQuick3DAbstractLight::brightness() const
227{
228 return m_brightness;
229}
230
231QQuick3DNode *QQuick3DAbstractLight::scope() const
232{
233 return m_scope;
234}
235
236bool QQuick3DAbstractLight::castsShadow() const
237{
238 return m_castsShadow;
239}
240
241float QQuick3DAbstractLight::shadowBias() const
242{
243 return m_shadowBias;
244}
245
246float QQuick3DAbstractLight::shadowFactor() const
247{
248 return m_shadowFactor;
249}
250
251QQuick3DAbstractLight::QSSGShadowMapQuality QQuick3DAbstractLight::shadowMapQuality() const
252{
253 return m_shadowMapQuality;
254}
255
256QQuick3DAbstractLight::QSSGSoftShadowQuality QQuick3DAbstractLight::softShadowQuality() const
257{
258 return m_softShadowQuality;
259}
260
261float QQuick3DAbstractLight::shadowMapFar() const
262{
263 return m_shadowMapFar;
264}
265
266float QQuick3DAbstractLight::shadowFilter() const
267{
268 return m_shadowFilter;
269}
270
271QQuick3DAbstractLight::QSSGBakeMode QQuick3DAbstractLight::bakeMode() const
272{
273 return m_bakeMode;
274}
275
276float QQuick3DAbstractLight::pcfFactor() const
277{
278 return m_pcfFactor;
279}
280
281bool QQuick3DAbstractLight::use32BitShadowmap() const
282{
283 return m_use32BitShadowmap;
284}
285
286void QQuick3DAbstractLight::markAllDirty()
287{
288 m_dirtyFlags = DirtyFlags(DirtyFlag::ShadowDirty)
289 | DirtyFlags(DirtyFlag::ColorDirty)
290 | DirtyFlags(DirtyFlag::BrightnessDirty)
291 | DirtyFlags(DirtyFlag::FadeDirty)
292 | DirtyFlags(DirtyFlag::AreaDirty)
293 | DirtyFlags(DirtyFlag::BakeModeDirty);
294 QQuick3DNode::markAllDirty();
295}
296
297void QQuick3DAbstractLight::setColor(const QColor &color)
298{
299 if (m_color == color)
300 return;
301
302 m_color = color;
303 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty);
304 emit colorChanged();
305 update();
306}
307
308void QQuick3DAbstractLight::setAmbientColor(const QColor &ambientColor)
309{
310 if (m_ambientColor == ambientColor)
311 return;
312
313 m_ambientColor = ambientColor;
314 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty);
315 emit ambientColorChanged();
316 update();
317}
318
319void QQuick3DAbstractLight::setBrightness(float brightness)
320{
321 if (qFuzzyCompare(m_brightness, brightness))
322 return;
323
324 m_brightness = brightness;
325 m_dirtyFlags.setFlag(DirtyFlag::BrightnessDirty);
326 emit brightnessChanged();
327 update();
328}
329
330void QQuick3DAbstractLight::setScope(QQuick3DNode *scope)
331{
332 if (m_scope == scope)
333 return;
334
335 m_scope = scope;
336 emit scopeChanged();
337 update();
338}
339
340void QQuick3DAbstractLight::setCastsShadow(bool castsShadow)
341{
342 if (m_castsShadow == castsShadow)
343 return;
344
345 m_castsShadow = castsShadow;
346 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
347 emit castsShadowChanged();
348 update();
349}
350
351void QQuick3DAbstractLight::setShadowBias(float shadowBias)
352{
353 if (qFuzzyCompare(m_shadowBias, shadowBias))
354 return;
355
356 m_shadowBias = shadowBias;
357 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
358 emit shadowBiasChanged();
359 update();
360}
361
362void QQuick3DAbstractLight::setShadowFactor(float shadowFactor)
363{
364 shadowFactor = qBound(0.0f, shadowFactor, 100.0f);
365 if (qFuzzyCompare(m_shadowFactor, shadowFactor))
366 return;
367
368 m_shadowFactor = shadowFactor;
369 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
370 emit shadowFactorChanged();
371 update();
372}
373
374void QQuick3DAbstractLight::setShadowMapQuality(
375 QQuick3DAbstractLight::QSSGShadowMapQuality shadowMapQuality)
376{
377 if (m_shadowMapQuality == shadowMapQuality)
378 return;
379
380 m_shadowMapQuality = shadowMapQuality;
381 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
382 emit shadowMapQualityChanged();
383 update();
384}
385
386void QQuick3DAbstractLight::setSoftShadowQuality(QSSGSoftShadowQuality softShadowQuality)
387{
388 if (m_softShadowQuality == softShadowQuality)
389 return;
390
391 m_softShadowQuality = softShadowQuality;
392 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
393 emit softShadowQualityChanged();
394 update();
395}
396
397void QQuick3DAbstractLight::setBakeMode(QQuick3DAbstractLight::QSSGBakeMode bakeMode)
398{
399 if (m_bakeMode == bakeMode)
400 return;
401
402 m_bakeMode = bakeMode;
403 m_dirtyFlags.setFlag(DirtyFlag::BakeModeDirty);
404 emit bakeModeChanged();
405 update();
406}
407
408void QQuick3DAbstractLight::setPcfFactor(float pcfFactor)
409{
410 if (m_pcfFactor == pcfFactor)
411 return;
412
413 m_pcfFactor = pcfFactor;
414 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
415 emit pcfFactorChanged();
416 update();
417}
418
419void QQuick3DAbstractLight::setUse32BitShadowmap(bool use32BitShadowmap)
420{
421 if (m_use32BitShadowmap == use32BitShadowmap)
422 return;
423
424 m_use32BitShadowmap = use32BitShadowmap;
425 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
426 emit use32BitShadowmapChanged();
427 update();
428}
429
430void QQuick3DAbstractLight::setShadowMapFar(float shadowMapFar)
431{
432 if (qFuzzyCompare(m_shadowMapFar, shadowMapFar))
433 return;
434
435 m_shadowMapFar = shadowMapFar;
436 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
437 emit shadowMapFarChanged();
438 update();
439}
440
441void QQuick3DAbstractLight::setShadowFilter(float shadowFilter)
442{
443 if (qFuzzyCompare(m_shadowFilter, shadowFilter))
444 return;
445
446 m_shadowFilter = shadowFilter;
447 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty);
448 emit shadowFilterChanged();
449 update();
450}
451
452quint32 QQuick3DAbstractLight::mapToShadowResolution(QSSGShadowMapQuality quality)
453{
454 switch (quality) {
455 case QSSGShadowMapQuality::ShadowMapQualityMedium:
456 return 512;
457 case QSSGShadowMapQuality::ShadowMapQualityHigh:
458 return 1024;
459 case QSSGShadowMapQuality::ShadowMapQualityVeryHigh:
460 return 2048;
461 case QSSGShadowMapQuality::ShadowMapQualityUltra:
462 return 4096;
463 default:
464 break;
465 }
466 return 256;
467}
468
469QSSGRenderGraphObject *QQuick3DAbstractLight::updateSpatialNode(QSSGRenderGraphObject *node)
470{
471 Q_ASSERT_X(node, __FUNCTION__, "Node must have been created in parent class.");
472
473 QQuick3DNode::updateSpatialNode(node);
474
475 QSSGRenderLight *light = static_cast<QSSGRenderLight *>(node);
476
477 if (m_dirtyFlags.toInt() != 0) // Some flag was set, so mark the light dirty!
478 light->markDirty(QSSGRenderLight::DirtyFlag::LightDirty);
479
480 if (m_dirtyFlags.testFlag(DirtyFlag::ColorDirty)) {
481 m_dirtyFlags.setFlag(DirtyFlag::ColorDirty, false);
482 light->m_diffuseColor = QSSGUtils::color::sRGBToLinear(m_color).toVector3D();
483 light->m_specularColor = light->m_diffuseColor;
484 light->m_ambientColor = QSSGUtils::color::sRGBToLinear(m_ambientColor).toVector3D();
485 }
486
487 if (m_dirtyFlags.testFlag(DirtyFlag::BrightnessDirty)) {
488 m_dirtyFlags.setFlag(DirtyFlag::BrightnessDirty, false);
489 light->m_brightness = m_brightness;
490 }
491
492 if (m_dirtyFlags.testFlag(DirtyFlag::ShadowDirty)) {
493 m_dirtyFlags.setFlag(DirtyFlag::ShadowDirty, false);
494 light->m_castShadow = m_castsShadow;
495 light->m_shadowBias = m_shadowBias;
496 light->m_shadowFactor = m_shadowFactor;
497 light->m_shadowMapRes = mapToShadowResolution(m_shadowMapQuality);
498 light->m_softShadowQuality = static_cast<QSSGRenderLight::SoftShadowQuality>(m_softShadowQuality);
499 light->m_shadowMapFar = m_shadowMapFar;
500 light->m_shadowFilter = m_shadowFilter;
501 light->m_pcfFactor = m_pcfFactor;
502 light->m_use32BitShadowmap = m_use32BitShadowmap;
503 }
504
505 if (m_dirtyFlags.testFlag(DirtyFlag::BakeModeDirty)) {
506 m_dirtyFlags.setFlag(DirtyFlag::BakeModeDirty, false);
507 light->m_bakingEnabled = m_bakeMode != QSSGBakeMode::BakeModeDisabled;
508 light->m_fullyBaked = m_bakeMode == QSSGBakeMode::BakeModeAll;
509 }
510
511 if (m_scope) {
512 // Special case:
513 // If the 'scope' is 'this' and this is the first call, then the spatial node is the one we just created.
514 // This is not unlikely, as it can make sense to put all child nodes that should receive light under the light node...
515 if (m_scope == this)
516 light->m_scope = light;
517 else
518 light->m_scope = static_cast<QSSGRenderNode*>(QQuick3DObjectPrivate::get(m_scope)->spatialNode);
519 } else {
520 light->m_scope = nullptr;
521 }
522
523 return node;
524}
525
526QT_END_NAMESPACE