1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only |
3 | |
4 | #include "qquick3dpointlight_p.h" |
5 | #include "qquick3dobject_p.h" |
6 | |
7 | #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h> |
8 | |
9 | #include "qquick3dnode_p_p.h" |
10 | |
11 | QT_BEGIN_NAMESPACE |
12 | |
13 | /*! |
14 | \qmltype PointLight |
15 | \inherits Light |
16 | \inqmlmodule QtQuick3D |
17 | \brief Defines a point light in the scene. |
18 | |
19 | The point light can be described as a sphere, emitting light with equal strength in all |
20 | directions from the center of the light. This is similar to the way a light bulb emits light. |
21 | |
22 | Rotating or scaling a point light does not have any effect. Moving a point light will change |
23 | the position from where the light is emitted. |
24 | |
25 | By default, a point light intensity diminishes according to inverse-square-law. However, the fade-off |
26 | (and range) can be controlled with the \l {constantFade}, \l {linearFade}, and |
27 | \l quadraticFade properties. Light attenuation is calculated using the formula: |
28 | \l {constantFade} + \c distance * (\l {linearFade} * 0.01) + \c distance^2 * (\l {quadraticFade} * 0.0001) |
29 | |
30 | \section2 A simple example: shading a sphere in three different ways |
31 | |
32 | Take a scene containing a sphere in front of a scaled up rectangle in the |
33 | background. The default base color of the PrincipledMaterial of the |
34 | rectangle is white. |
35 | |
36 | Without any lights and disabling light-related shading for the two meshes, |
37 | we get the following: |
38 | |
39 | \qml |
40 | import QtQuick |
41 | import QtQuick3D |
42 | View3D { |
43 | anchors.fill: parent |
44 | |
45 | PerspectiveCamera { z: 600 } |
46 | |
47 | Model { |
48 | source: "#Rectangle" |
49 | scale: Qt.vector3d(10, 10, 10) |
50 | z: -100 |
51 | materials: PrincipledMaterial { |
52 | lighting: PrincipledMaterial.NoLighting |
53 | } |
54 | } |
55 | |
56 | Model { |
57 | source: "#Sphere" |
58 | scale: Qt.vector3d(2, 2, 2) |
59 | materials: PrincipledMaterial { |
60 | lighting: PrincipledMaterial.NoLighting |
61 | baseColor: "#40c060" |
62 | roughness: 0.1 |
63 | } |
64 | } |
65 | } |
66 | \endqml |
67 | |
68 | \image pointlight-1.png |
69 | |
70 | Adding a directional light, emitting down the Z axis by default, leads to the following: |
71 | |
72 | \qml |
73 | import QtQuick |
74 | import QtQuick3D |
75 | View3D { |
76 | anchors.fill: parent |
77 | |
78 | PerspectiveCamera { z: 600 } |
79 | |
80 | DirectionalLight { } |
81 | |
82 | Model { |
83 | source: "#Rectangle" |
84 | scale: Qt.vector3d(10, 10, 10) |
85 | z: -100 |
86 | materials: PrincipledMaterial { } |
87 | } |
88 | |
89 | Model { |
90 | source: "#Sphere" |
91 | scale: Qt.vector3d(2, 2, 2) |
92 | materials: PrincipledMaterial { |
93 | baseColor: "#40c060" |
94 | roughness: 0.1 |
95 | } |
96 | } |
97 | } |
98 | \endqml |
99 | |
100 | \image pointlight-2.png |
101 | |
102 | What if we now replace DirectionalLight with: |
103 | |
104 | \qml |
105 | PointLight { |
106 | z: 300 |
107 | } |
108 | \endqml |
109 | |
110 | The white colored PointLight here is moved on the Z axis so that it is |
111 | halfway between the camera and the center of the scene. Unlike |
112 | DirectionalLight, the rotation of the PointLight does not matter, whereas |
113 | its position is significant. The diminishing intensity is visible here, |
114 | especially on the rectangle mesh in the background. |
115 | |
116 | \image pointlight-3.png |
117 | |
118 | For more usage examples, see \l{Qt Quick 3D - Lights Example}. |
119 | |
120 | \sa DirectionalLight, SpotLight |
121 | */ |
122 | |
123 | /*! |
124 | \qmlproperty real PointLight::constantFade |
125 | |
126 | This property is constant factor of the attenuation term of the light. |
127 | The default value is 1.0. |
128 | */ |
129 | |
130 | /*! |
131 | \qmlproperty real PointLight::linearFade |
132 | |
133 | This property increases the rate at which the lighting effect dims the light |
134 | in proportion to the distance to the light. The default value is \c 0.0, meaning the light doesn't |
135 | have linear fade. The value used here is multiplied by \c 0.01 before being used to |
136 | calculate light attenuation. |
137 | */ |
138 | |
139 | /*! |
140 | \qmlproperty real PointLight::quadraticFade |
141 | |
142 | This property increases the rate at which the lighting effect dims the light |
143 | in proportion to the inverse square law. The default value is 1.0 meaning the point light |
144 | fade exactly follows the inverse square law i.e. when distance to an object doubles the |
145 | light intensity decreases to 1/4th. The value used here is multiplied by \c 0.0001 before |
146 | being used to calculate light attenuation. |
147 | */ |
148 | |
149 | QQuick3DPointLight::QQuick3DPointLight(QQuick3DNode *parent) |
150 | : QQuick3DAbstractLight(*(new QQuick3DNodePrivate(QQuick3DNodePrivate::Type::PointLight)), parent) {} |
151 | |
152 | float QQuick3DPointLight::constantFade() const |
153 | { |
154 | return m_constantFade; |
155 | } |
156 | |
157 | float QQuick3DPointLight::linearFade() const |
158 | { |
159 | return m_linearFade; |
160 | } |
161 | |
162 | float QQuick3DPointLight::quadraticFade() const |
163 | { |
164 | return m_quadraticFade; |
165 | } |
166 | |
167 | void QQuick3DPointLight::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 | |
178 | void QQuick3DPointLight::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 | |
189 | void QQuick3DPointLight::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 | |
200 | QSSGRenderGraphObject *QQuick3DPointLight::updateSpatialNode(QSSGRenderGraphObject *node) |
201 | { |
202 | if (!node) { |
203 | markAllDirty(); |
204 | node = new QSSGRenderLight(QSSGRenderLight::Type::PointLight); |
205 | } |
206 | |
207 | QQuick3DAbstractLight::updateSpatialNode(node); // Marks the light node dirty if m_dirtyFlags != 0 |
208 | |
209 | QSSGRenderLight *light = static_cast<QSSGRenderLight *>(node); |
210 | |
211 | if (m_dirtyFlags.testFlag(flag: DirtyFlag::FadeDirty)) { |
212 | m_dirtyFlags.setFlag(flag: DirtyFlag::FadeDirty, on: false); |
213 | light->m_constantFade = m_constantFade; |
214 | light->m_linearFade = m_linearFade; |
215 | light->m_quadraticFade = m_quadraticFade; |
216 | } |
217 | |
218 | return node; |
219 | } |
220 | |
221 | QT_END_NAMESPACE |
222 | |