1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include "qquick3dspotlight_p.h"
5#include "qquick3dobject_p.h"
6
7#include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
8
9#include "qquick3dnode_p_p.h"
10
11QT_BEGIN_NAMESPACE
12
13/*!
14 \qmltype SpotLight
15 \inherits Light
16 \inqmlmodule QtQuick3D
17 \brief Defines a spot light in the scene.
18 \since 5.15
19
20 The spot light emits light towards one direction in a cone shape, which is defined by the
21 \l {coneAngle} property. The light intensity diminishes when approaching the \l {coneAngle}.
22 The angle at which the light intensity starts to diminish is defined by \l {innerConeAngle}.
23 Both angles are defined in degrees.
24
25 Inside the \l {innerConeAngle}, the spot light behaves similarly to the point light.
26 There the light intensity diminishes according to inverse-square-law. However, the fade-off
27 (and range) can be controlled with the \l {constantFade}, \l {linearFade}, and
28 \l quadraticFade properties. Light attenuation is calculated using the formula:
29 \l {constantFade} + \c distance * (\l {linearFade} * 0.01) + \c distance * (\l {quadraticFade} * 0.0001)^2
30
31 Let's look at a simple example. Here a SpotLight is placed at 300 on the Z
32 axis, so halfway between the camera and the scene center. By default the
33 light is emitting in the direction of the Z axis. The \l {Light::}{brightness} is
34 increased to 10 to make it look more like a typical spot light.
35
36 \qml
37 import QtQuick
38 import QtQuick3D
39 View3D {
40 anchors.fill: parent
41
42 PerspectiveCamera { z: 600 }
43
44 SpotLight {
45 z: 300
46 brightness: 10
47 ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
48 }
49
50 Model {
51 source: "#Rectangle"
52 scale: Qt.vector3d(10, 10, 10)
53 z: -100
54 materials: PrincipledMaterial { }
55 }
56
57 Model {
58 source: "#Sphere"
59 scale: Qt.vector3d(2, 2, 2)
60 materials: PrincipledMaterial {
61 baseColor: "#40c060"
62 roughness: 0.1
63 }
64 }
65 }
66 \endqml
67
68 \image spotlight-1.png
69
70 Rotations happens similarly to \l DirectionalLight. Here we want to light to
71 emit more to the right, so we rotate around the Y axis by -20 degrees. The
72 cone is reduced by setting coneAngle to 30 instead of the default 40. We
73 also make the intensity start diminish earlier, by changing innerConeAngle
74 to 10.
75
76 \qml
77 SpotLight {
78 z: 300
79 brightness: 10
80 ambientColor: Qt.rgba(0.1, 0.1, 0.1, 1.0)
81 eulerRotation.y: -20
82 coneAngle: 30
83 innerConeAngle: 10
84 }
85 \endqml
86
87 \image spotlight-2.png
88
89 For further usage examples, see \l{Qt Quick 3D - Lights Example}.
90
91 \sa DirectionalLight, PointLight, {Shadow Mapping}
92*/
93
94/*!
95 \qmlproperty real SpotLight::constantFade
96
97 This property is constant factor of the attenuation term of the light.
98 The default value is 1.0.
99 */
100
101/*!
102 \qmlproperty real SpotLight::linearFade
103
104 This property increases the rate at which the lighting effect dims the light
105 in proportion to the distance to the light. The default value is \c 0.0, which means the light
106 doesn't have linear fade. The value used here is multiplied by \c 0.01 before being used to
107 calculate light attenuation.
108*/
109
110/*!
111 \qmlproperty real SpotLight::quadraticFade
112
113 This property increases the rate at which the lighting effect dims the light
114 in proportion to the inverse square law. The default value is 1.0, which means the spot light
115 fade exactly follows the inverse square law, i.e. when distance to an object doubles the
116 light intensity decreases to 1/4th. The value used here is multiplied by \c 0.0001 before
117 being used to calculate light attenuation.
118*/
119
120/*!
121 \qmlproperty real SpotLight::coneAngle
122
123 This property defines the cut-off angle (from edge to edge) beyond which the light doesn't affect the scene.
124 Defined in degrees between \c{0} and \c{180}. The default value is \c{40}.
125
126 \note When the cone angle approaches \c{180} degrees the shadow quality will start to deteriorate. A value
127 under \c{170} is therefore recommended.
128*/
129
130/*!
131 \qmlproperty real SpotLight::innerConeAngle
132
133 This property defines the angle (from edge to edge) at which the light intensity starts to gradually diminish
134 as it approaches \l {coneAngle}. Defined in degrees between 0 and 180. If the value is set
135 larger than \l {coneAngle}, it'll behave as if it had the same value as \l {coneAngle}.
136 The default value is 30.
137*/
138
139QQuick3DSpotLight::QQuick3DSpotLight(QQuick3DNode *parent)
140 : QQuick3DAbstractLight(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::SpotLight)), parent) {}
141
142float QQuick3DSpotLight::constantFade() const
143{
144 return m_constantFade;
145}
146
147float QQuick3DSpotLight::linearFade() const
148{
149 return m_linearFade;
150}
151
152float QQuick3DSpotLight::quadraticFade() const
153{
154 return m_quadraticFade;
155}
156
157float QQuick3DSpotLight::coneAngle() const
158{
159 return m_coneAngle;
160}
161
162float QQuick3DSpotLight::innerConeAngle() const
163{
164 return m_innerConeAngle;
165}
166
167void QQuick3DSpotLight::setConstantFade(float constantFade)
168{
169 if (qFuzzyCompare(p1: m_constantFade, p2: constantFade))
170 return;
171
172 m_constantFade = constantFade;
173 m_dirtyFlags.setFlag(flag: DirtyFlag::FadeDirty);
174 emit constantFadeChanged();
175 update();
176}
177
178void QQuick3DSpotLight::setLinearFade(float linearFade)
179{
180 if (qFuzzyCompare(p1: m_linearFade, p2: linearFade))
181 return;
182
183 m_linearFade = linearFade;
184 m_dirtyFlags.setFlag(flag: DirtyFlag::FadeDirty);
185 emit linearFadeChanged();
186 update();
187}
188
189void QQuick3DSpotLight::setQuadraticFade(float quadraticFade)
190{
191 if (qFuzzyCompare(p1: m_quadraticFade, p2: quadraticFade))
192 return;
193
194 m_quadraticFade = quadraticFade;
195 m_dirtyFlags.setFlag(flag: DirtyFlag::FadeDirty);
196 emit quadraticFadeChanged();
197 update();
198}
199
200void QQuick3DSpotLight::setConeAngle(float coneAngle)
201{
202 if (coneAngle < 0.f)
203 coneAngle = 0.f;
204 else if (coneAngle > 180.f)
205 coneAngle = 180.f;
206
207 if (qFuzzyCompare(p1: m_coneAngle, p2: coneAngle))
208 return;
209
210 m_coneAngle = coneAngle;
211 m_dirtyFlags.setFlag(flag: DirtyFlag::AreaDirty);
212 emit coneAngleChanged();
213 update();
214}
215
216void QQuick3DSpotLight::setInnerConeAngle(float innerConeAngle)
217{
218 if (innerConeAngle < 0.f)
219 innerConeAngle = 0.f;
220 else if (innerConeAngle > 180.f)
221 innerConeAngle = 180.f;
222
223 if (qFuzzyCompare(p1: m_innerConeAngle, p2: innerConeAngle))
224 return;
225
226 m_innerConeAngle = innerConeAngle;
227 m_dirtyFlags.setFlag(flag: DirtyFlag::AreaDirty);
228 emit innerConeAngleChanged();
229 update();
230}
231
232QSSGRenderGraphObject *QQuick3DSpotLight::updateSpatialNode(QSSGRenderGraphObject *node)
233{
234 if (!node) {
235 markAllDirty();
236 node = new QSSGRenderLight(QSSGRenderLight::Type::SpotLight);
237 }
238
239 QQuick3DAbstractLight::updateSpatialNode(node); // Marks the light node dirty if m_dirtyFlags != 0
240
241 QSSGRenderLight *light = static_cast<QSSGRenderLight *>(node);
242
243 if (m_dirtyFlags.testFlag(flag: DirtyFlag::FadeDirty)) {
244 m_dirtyFlags.setFlag(flag: DirtyFlag::FadeDirty, on: false);
245 light->m_constantFade = m_constantFade;
246 light->m_linearFade = m_linearFade;
247 light->m_quadraticFade = m_quadraticFade;
248 }
249
250 if (m_dirtyFlags.testFlag(flag: DirtyFlag::AreaDirty)) {
251 m_dirtyFlags.setFlag(flag: DirtyFlag::AreaDirty, on: false);
252 light->m_coneAngle = qBound(min: 0.0f, val: m_coneAngle * 0.5, max: 90.0f);
253 light->m_innerConeAngle = qBound(min: 0.0f, val: m_innerConeAngle * 0.5, max: 90.0f);
254 }
255
256 return node;
257}
258
259QT_END_NAMESPACE
260

Provided by KDAB

Privacy Policy
Start learning QML with our Intro Training
Find out more

source code of qtquick3d/src/quick3d/qquick3dspotlight.cpp