1// Copyright (C) 2017 Klaralvdalens Datakonsult AB (KDAB).
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qmetalroughmaterial.h"
5#include "qmetalroughmaterial_p.h"
6#include <Qt3DRender/qfilterkey.h>
7#include <Qt3DRender/qmaterial.h>
8#include <Qt3DRender/qeffect.h>
9#include <Qt3DRender/qtexture.h>
10#include <Qt3DRender/qtechnique.h>
11#include <Qt3DRender/qshaderprogram.h>
12#include <Qt3DRender/qshaderprogrambuilder.h>
13#include <Qt3DRender/qparameter.h>
14#include <Qt3DRender/qrenderpass.h>
15#include <Qt3DRender/qgraphicsapifilter.h>
16#include <QUrl>
17#include <QVector3D>
18#include <QVector4D>
19
20QT_BEGIN_NAMESPACE
21
22using namespace Qt3DRender;
23
24namespace Qt3DExtras {
25
26QMetalRoughMaterialPrivate::QMetalRoughMaterialPrivate()
27 : QMaterialPrivate()
28 , m_baseColorParameter(new QParameter(QStringLiteral("baseColor"), QColor::fromString(name: "grey")))
29 , m_metalnessParameter(new QParameter(QStringLiteral("metalness"), 0.0f))
30 , m_roughnessParameter(new QParameter(QStringLiteral("roughness"), 0.0f))
31 , m_baseColorMapParameter(new QParameter(QStringLiteral("baseColorMap"), QVariant()))
32 , m_metalnessMapParameter(new QParameter(QStringLiteral("metalnessMap"), QVariant()))
33 , m_roughnessMapParameter(new QParameter(QStringLiteral("roughnessMap"), QVariant()))
34 , m_ambientOcclusionMapParameter(new QParameter(QStringLiteral("ambientOcclusionMap"), QVariant()))
35 , m_normalMapParameter(new QParameter(QStringLiteral("normalMap"), QVariant()))
36 , m_textureScaleParameter(new QParameter(QStringLiteral("texCoordScale"), 1.0f))
37 , m_metalRoughEffect(new QEffect())
38 , m_metalRoughGL3Technique(new QTechnique())
39 , m_metalRoughGL3RenderPass(new QRenderPass())
40 , m_metalRoughGL3Shader(new QShaderProgram())
41 , m_metalRoughGL3ShaderBuilder(new QShaderProgramBuilder())
42 , m_metalRoughES3Technique(new QTechnique())
43 , m_metalRoughES3RenderPass(new QRenderPass())
44 , m_metalRoughES3Shader(new QShaderProgram())
45 , m_metalRoughES3ShaderBuilder(new QShaderProgramBuilder())
46 , m_metalRoughRHITechnique(new QTechnique())
47 , m_metalRoughRHIRenderPass(new QRenderPass())
48 , m_metalRoughRHIShader(new QShaderProgram())
49 , m_metalRoughRHIShaderBuilder(new QShaderProgramBuilder())
50 , m_filterKey(new QFilterKey)
51{
52}
53
54void QMetalRoughMaterialPrivate::init()
55{
56 Q_Q(QMetalRoughMaterial);
57
58 QObject::connect(sender: m_baseColorParameter, signal: &Qt3DRender::QParameter::valueChanged,
59 context: q, slot: &QMetalRoughMaterial::baseColorChanged);
60 QObject::connect(sender: m_metalnessParameter, signal: &Qt3DRender::QParameter::valueChanged,
61 context: q, slot: &QMetalRoughMaterial::metalnessChanged);
62 QObject::connect(sender: m_roughnessParameter, signal: &Qt3DRender::QParameter::valueChanged,
63 context: q, slot: &QMetalRoughMaterial::roughnessChanged);
64 QObject::connect(sender: m_ambientOcclusionMapParameter, signal: &Qt3DRender::QParameter::valueChanged,
65 context: q, slot: &QMetalRoughMaterial::roughnessChanged);
66 QObject::connect(sender: m_normalMapParameter, signal: &Qt3DRender::QParameter::valueChanged,
67 context: q, slot: &QMetalRoughMaterial::normalChanged);
68 connect(sender: m_textureScaleParameter, signal: &Qt3DRender::QParameter::valueChanged,
69 receiverPrivate: this, slot: &QMetalRoughMaterialPrivate::handleTextureScaleChanged);
70
71 m_metalRoughGL3Shader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/gl3/default.vert"))));
72 m_metalRoughGL3ShaderBuilder->setParent(q);
73 m_metalRoughGL3ShaderBuilder->setShaderProgram(m_metalRoughGL3Shader);
74 m_metalRoughGL3ShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/metalrough.frag.json")));
75 m_metalRoughGL3ShaderBuilder->setEnabledLayers({QStringLiteral("baseColor"),
76 QStringLiteral("metalness"),
77 QStringLiteral("roughness"),
78 QStringLiteral("ambientOcclusion"),
79 QStringLiteral("normal")});
80
81 m_metalRoughES3Shader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/es3/default.vert"))));
82 m_metalRoughES3ShaderBuilder->setParent(q);
83 m_metalRoughES3ShaderBuilder->setShaderProgram(m_metalRoughES3Shader);
84 m_metalRoughES3ShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/metalrough.frag.json")));
85 m_metalRoughES3ShaderBuilder->setEnabledLayers({QStringLiteral("baseColor"),
86 QStringLiteral("metalness"),
87 QStringLiteral("roughness"),
88 QStringLiteral("ambientOcclusion"),
89 QStringLiteral("normal")});
90
91 m_metalRoughRHIShader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QStringLiteral("qrc:/shaders/rhi/default_pos_norm.vert"))));
92 m_metalRoughRHIShaderBuilder->setParent(q);
93 m_metalRoughRHIShaderBuilder->setShaderProgram(m_metalRoughRHIShader);
94 m_metalRoughRHIShaderBuilder->setFragmentShaderGraph(QUrl(QStringLiteral("qrc:/shaders/graphs/metalrough.frag.json")));
95 m_metalRoughRHIShaderBuilder->setEnabledLayers({QStringLiteral("baseColor"),
96 QStringLiteral("metalness"),
97 QStringLiteral("roughness"),
98 QStringLiteral("ambientOcclusion"),
99 QStringLiteral("normal")});
100
101 m_metalRoughGL3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGL);
102 m_metalRoughGL3Technique->graphicsApiFilter()->setMajorVersion(3);
103 m_metalRoughGL3Technique->graphicsApiFilter()->setMinorVersion(1);
104 m_metalRoughGL3Technique->graphicsApiFilter()->setProfile(QGraphicsApiFilter::CoreProfile);
105
106 m_metalRoughES3Technique->graphicsApiFilter()->setApi(QGraphicsApiFilter::OpenGLES);
107 m_metalRoughES3Technique->graphicsApiFilter()->setMajorVersion(3);
108 m_metalRoughES3Technique->graphicsApiFilter()->setMinorVersion(0);
109
110 m_metalRoughRHITechnique->graphicsApiFilter()->setApi(QGraphicsApiFilter::RHI);
111 m_metalRoughRHITechnique->graphicsApiFilter()->setMajorVersion(1);
112 m_metalRoughRHITechnique->graphicsApiFilter()->setMinorVersion(0);
113
114 m_filterKey->setParent(q);
115 m_filterKey->setName(QStringLiteral("renderingStyle"));
116 m_filterKey->setValue(QStringLiteral("forward"));
117
118 m_metalRoughGL3Technique->addFilterKey(filterKey: m_filterKey);
119 m_metalRoughGL3RenderPass->setShaderProgram(m_metalRoughGL3Shader);
120 m_metalRoughGL3Technique->addRenderPass(pass: m_metalRoughGL3RenderPass);
121 m_metalRoughEffect->addTechnique(t: m_metalRoughGL3Technique);
122
123 m_metalRoughES3Technique->addFilterKey(filterKey: m_filterKey);
124 m_metalRoughES3RenderPass->setShaderProgram(m_metalRoughES3Shader);
125 m_metalRoughES3Technique->addRenderPass(pass: m_metalRoughES3RenderPass);
126 m_metalRoughEffect->addTechnique(t: m_metalRoughES3Technique);
127
128 m_metalRoughRHITechnique->addFilterKey(filterKey: m_filterKey);
129 m_metalRoughRHIRenderPass->setShaderProgram(m_metalRoughRHIShader);
130 m_metalRoughRHITechnique->addRenderPass(pass: m_metalRoughRHIRenderPass);
131 m_metalRoughEffect->addTechnique(t: m_metalRoughRHITechnique);
132
133 // Given parameters a parent
134 m_baseColorMapParameter->setParent(m_metalRoughEffect);
135 m_metalnessMapParameter->setParent(m_metalRoughEffect);
136 m_roughnessMapParameter->setParent(m_metalRoughEffect);
137
138 m_metalRoughEffect->addParameter(parameter: m_baseColorParameter);
139 m_metalRoughEffect->addParameter(parameter: m_metalnessParameter);
140 m_metalRoughEffect->addParameter(parameter: m_roughnessParameter);
141 m_metalRoughEffect->addParameter(parameter: m_textureScaleParameter);
142
143 q->setEffect(m_metalRoughEffect);
144}
145
146void QMetalRoughMaterialPrivate::handleTextureScaleChanged(const QVariant &var)
147{
148 Q_Q(QMetalRoughMaterial);
149 emit q->textureScaleChanged(textureScale: var.toFloat());
150}
151
152void QMetalRoughMaterialPrivate::updateLayersOnTechnique(const QStringList &layers)
153{
154 m_metalRoughGL3ShaderBuilder->setEnabledLayers(layers);
155 m_metalRoughES3ShaderBuilder->setEnabledLayers(layers);
156 m_metalRoughRHIShaderBuilder->setEnabledLayers(layers);
157 updateVertexShaderBasedOnLayers(layers);
158}
159
160void QMetalRoughMaterialPrivate::updateVertexShaderBasedOnLayers(const QStringList &layers)
161{
162 const QString mapsLayers[] = {
163 QStringLiteral("baseColorMap"),
164 QStringLiteral("metalnessMap"),
165 QStringLiteral("roughnessMap"),
166 QStringLiteral("ambientOcclusionMap"),
167 QStringLiteral("normalMap")
168 };
169
170 const bool needsTexCoords = std::any_of(first: std::cbegin(cont: mapsLayers),
171 last: std::cend(cont: mapsLayers),
172 pred: [&] (const QString &mapLayers) { return layers.contains(str: mapLayers); });
173 const bool needsTangents = layers.contains(QStringLiteral("normalMap"));
174
175 QString vertexShader = QLatin1String("default_pos_norm");
176
177 if (needsTexCoords)
178 vertexShader += QLatin1String("_tex");
179 if (needsTangents)
180 vertexShader += QLatin1String("_tan");
181
182 m_metalRoughRHIShader->setVertexShaderCode(QShaderProgram::loadSource(sourceUrl: QUrl(QLatin1String("qrc:/shaders/rhi/%1.vert").arg(args&: vertexShader))));
183}
184
185
186/*!
187 \class Qt3DExtras::QMetalRoughMaterial
188 \ingroup qt3d-extras-materials
189 \brief The QMetalRoughMaterial provides a default implementation of PBR
190 lighting.
191 \inmodule Qt3DExtras
192 \since 5.9
193 \inherits Qt3DRender::QMaterial
194
195 This material uses an effect with a single render pass approach and performs per fragment
196 lighting. Techniques are provided for OpenGL 3 and OpenGL ES 3.
197*/
198
199/*!
200 \qmltype MetalRoughMaterial
201 \instantiates Qt3DExtras::QMetalRoughMaterial
202 \inqmlmodule Qt3D.Extras
203
204 \brief This material uses an effect with a single render pass approach and
205 performs per fragment lighting. Techniques are provided for OpenGL 3
206 and OpenGL ES 3.
207*/
208
209/*!
210 Constructs a new QMetalRoughMaterial instance with parent object \a parent.
211*/
212QMetalRoughMaterial::QMetalRoughMaterial(QNode *parent)
213 : QMaterial(*new QMetalRoughMaterialPrivate, parent)
214{
215 Q_D(QMetalRoughMaterial);
216 d->init();
217}
218
219/*! \internal */
220QMetalRoughMaterial::QMetalRoughMaterial(QMetalRoughMaterialPrivate &dd, QNode *parent)
221 : QMaterial(dd, parent)
222{
223 Q_D(QMetalRoughMaterial);
224 d->init();
225}
226
227/*!
228 Destroys the QMetalRoughMaterial instance.
229*/
230QMetalRoughMaterial::~QMetalRoughMaterial()
231{
232}
233
234/*!
235 \property QMetalRoughMaterial::baseColor
236
237 Holds the current base color of the material. This can be either a plain
238 color value or a texture. By default the value of this property is "grey".
239*/
240/*!
241 \qmlproperty variant Qt3D.Extras::MetalRoughMaterial::baseColor
242
243 Holds the current base color of the material. This can be either a plain
244 color value or a texture. By default the value of this property is "grey".
245*/
246QVariant QMetalRoughMaterial::baseColor() const
247{
248 Q_D(const QMetalRoughMaterial);
249 return d->m_baseColorParameter->value();
250}
251
252/*!
253 \property QMetalRoughMaterial::metalness
254
255 Holds the current metalness level of the material, as a value between
256 0 (purely dielectric, the default) and 1 (purely metallic). This can be
257 either a plain uniform value or a texture. By default the value of this
258 property is 0.
259*/
260/*!
261 \qmlproperty variant Qt3D.Extras::MetalRoughMaterial::metalness
262
263 Holds the current metalness level of the material, as a value between
264 0 (purely dielectric, the default) and 1 (purely metallic). This can be
265 either a plain uniform value or a texture. By default the value of this
266 property is 0.
267*/
268QVariant QMetalRoughMaterial::metalness() const
269{
270 Q_D(const QMetalRoughMaterial);
271 return d->m_metalnessParameter->value();
272}
273
274/*!
275 \property QMetalRoughMaterial::roughness
276
277 Holds the current roughness level of the material. This can be either a
278 plain uniform value or a texture. By default the value of this property is
279 0.
280*/
281/*!
282 \qmlproperty variant Qt3D.Extras::MetalRoughMaterial::roughness
283
284 Holds the current roughness level of the material. This can be either a
285 plain uniform value or a texture. By default the value of this property is
286 0.
287*/
288QVariant QMetalRoughMaterial::roughness() const
289{
290 Q_D(const QMetalRoughMaterial);
291 return d->m_roughnessParameter->value();
292}
293
294/*!
295 \property QMetalRoughMaterial::ambientOcclusion
296
297 Holds the current ambient occlusion map texture of the material. This can
298 only be a texture, otherwise it is ignored. By default this map is not set.
299*/
300/*!
301 \qmlproperty Texture Qt3D.Extras::MetalRoughMaterial::ambientOcclusion
302
303 Holds the current ambient occlusion map texture of the material. This can
304 only be a texture, otherwise it is ignored. By default this map is not set.
305*/
306QVariant QMetalRoughMaterial::ambientOcclusion() const
307{
308 Q_D(const QMetalRoughMaterial);
309 return d->m_ambientOcclusionMapParameter->value();
310}
311
312/*!
313 \property QMetalRoughMaterial::normal
314
315 Holds the current normal map texture of the material. This can only be a
316 texture, otherwise it is ignored. By default this map is not set.
317*/
318/*!
319 \qmlproperty Texture Qt3D.Extras::MetalRoughMaterial::normal
320
321 Holds the current normal map texture of the material. This can only be a
322 texture, otherwise it is ignored. By default this map is not set.
323*/
324QVariant QMetalRoughMaterial::normal() const
325{
326 Q_D(const QMetalRoughMaterial);
327 return d->m_normalMapParameter->value();
328}
329
330/*!
331 \property QMetalRoughMaterial::textureScale
332
333 Holds the current texture scale. It is applied as a multiplier to texture
334 coordinates at render time. Defaults to 1.0.
335
336 When used in conjunction with QTextureWrapMode::Repeat, textureScale provides a simple
337 way to tile a texture across a surface. For example, a texture scale of \c 4.0
338 would result in 16 (4x4) tiles.
339*/
340/*!
341 \qmlproperty real Qt3D.Extras::MetalRoughMaterial::textureScale
342
343 Holds the current texture scale. It is applied as a multiplier to texture
344 coordinates at render time. Defaults to 1.0.
345
346 When used in conjunction with WrapMode.Repeat, textureScale provides a simple
347 way to tile a texture across a surface. For example, a texture scale of \c 4.0
348 would result in 16 (4x4) tiles.
349*/
350float QMetalRoughMaterial::textureScale() const
351{
352 Q_D(const QMetalRoughMaterial);
353 return d->m_textureScaleParameter->value().toFloat();
354}
355
356void QMetalRoughMaterial::setBaseColor(const QVariant &baseColor)
357{
358 Q_D(QMetalRoughMaterial);
359 d->m_baseColorParameter->setValue(baseColor);
360 d->m_baseColorMapParameter->setValue(baseColor);
361
362 auto layers = d->m_metalRoughGL3ShaderBuilder->enabledLayers();
363 if (baseColor.value<QAbstractTexture *>()) {
364 layers.removeAll(QStringLiteral("baseColor"));
365 layers.append(QStringLiteral("baseColorMap"));
366 d->m_metalRoughEffect->addParameter(parameter: d->m_baseColorMapParameter);
367 if (d->m_metalRoughEffect->parameters().contains(t: d->m_baseColorParameter))
368 d->m_metalRoughEffect->removeParameter(parameter: d->m_baseColorParameter);
369 } else {
370 layers.removeAll(QStringLiteral("baseColorMap"));
371 layers.append(QStringLiteral("baseColor"));
372 if (d->m_metalRoughEffect->parameters().contains(t: d->m_baseColorMapParameter))
373 d->m_metalRoughEffect->removeParameter(parameter: d->m_baseColorMapParameter);
374 d->m_metalRoughEffect->addParameter(parameter: d->m_baseColorParameter);
375 }
376 d->updateLayersOnTechnique(layers);
377}
378
379void QMetalRoughMaterial::setMetalness(const QVariant &metalness)
380{
381 Q_D(QMetalRoughMaterial);
382 d->m_metalnessParameter->setValue(metalness);
383 d->m_metalnessMapParameter->setValue(metalness);
384
385 auto layers = d->m_metalRoughGL3ShaderBuilder->enabledLayers();
386 if (metalness.value<QAbstractTexture *>()) {
387 layers.removeAll(QStringLiteral("metalness"));
388 layers.append(QStringLiteral("metalnessMap"));
389 d->m_metalRoughEffect->addParameter(parameter: d->m_metalnessMapParameter);
390 if (d->m_metalRoughEffect->parameters().contains(t: d->m_metalnessParameter))
391 d->m_metalRoughEffect->removeParameter(parameter: d->m_metalnessParameter);
392 } else {
393 layers.removeAll(QStringLiteral("metalnessMap"));
394 layers.append(QStringLiteral("metalness"));
395 if (d->m_metalRoughEffect->parameters().contains(t: d->m_metalnessMapParameter))
396 d->m_metalRoughEffect->removeParameter(parameter: d->m_metalnessMapParameter);
397 d->m_metalRoughEffect->addParameter(parameter: d->m_metalnessParameter);
398 }
399 d->updateLayersOnTechnique(layers);
400}
401
402void QMetalRoughMaterial::setRoughness(const QVariant &roughness)
403{
404 Q_D(QMetalRoughMaterial);
405 d->m_roughnessParameter->setValue(roughness);
406 d->m_roughnessMapParameter->setValue(roughness);
407
408 auto layers = d->m_metalRoughGL3ShaderBuilder->enabledLayers();
409 if (roughness.value<QAbstractTexture *>()) {
410 layers.removeAll(QStringLiteral("roughness"));
411 layers.append(QStringLiteral("roughnessMap"));
412 d->m_metalRoughEffect->addParameter(parameter: d->m_roughnessMapParameter);
413 if (d->m_metalRoughEffect->parameters().contains(t: d->m_roughnessParameter))
414 d->m_metalRoughEffect->removeParameter(parameter: d->m_roughnessParameter);
415 } else {
416 layers.removeAll(QStringLiteral("roughnessMap"));
417 layers.append(QStringLiteral("roughness"));
418 if (d->m_metalRoughEffect->parameters().contains(t: d->m_roughnessMapParameter))
419 d->m_metalRoughEffect->removeParameter(parameter: d->m_roughnessMapParameter);
420 d->m_metalRoughEffect->addParameter(parameter: d->m_roughnessParameter);
421 }
422 d->updateLayersOnTechnique(layers);
423}
424
425void QMetalRoughMaterial::setAmbientOcclusion(const QVariant &ambientOcclusion)
426{
427 Q_D(QMetalRoughMaterial);
428 d->m_ambientOcclusionMapParameter->setValue(ambientOcclusion);
429
430 auto layers = d->m_metalRoughGL3ShaderBuilder->enabledLayers();
431 if (ambientOcclusion.value<QAbstractTexture *>()) {
432 layers.removeAll(QStringLiteral("ambientOcclusion"));
433 layers.append(QStringLiteral("ambientOcclusionMap"));
434 d->m_metalRoughEffect->addParameter(parameter: d->m_ambientOcclusionMapParameter);
435 } else {
436 layers.removeAll(QStringLiteral("ambientOcclusionMap"));
437 layers.append(QStringLiteral("ambientOcclusion"));
438 if (d->m_metalRoughEffect->parameters().contains(t: d->m_ambientOcclusionMapParameter))
439 d->m_metalRoughEffect->removeParameter(parameter: d->m_ambientOcclusionMapParameter);
440 }
441 d->updateLayersOnTechnique(layers);
442}
443
444void QMetalRoughMaterial::setNormal(const QVariant &normal)
445{
446 Q_D(QMetalRoughMaterial);
447 d->m_normalMapParameter->setValue(normal);
448
449 auto layers = d->m_metalRoughGL3ShaderBuilder->enabledLayers();
450 if (normal.value<QAbstractTexture *>()) {
451 layers.removeAll(QStringLiteral("normal"));
452 layers.append(QStringLiteral("normalMap"));
453 d->m_metalRoughEffect->addParameter(parameter: d->m_normalMapParameter);
454 } else {
455 layers.removeAll(QStringLiteral("normalMap"));
456 layers.append(QStringLiteral("normal"));
457 if (d->m_metalRoughEffect->parameters().contains(t: d->m_normalMapParameter))
458 d->m_metalRoughEffect->removeParameter(parameter: d->m_normalMapParameter);
459 }
460 d->updateLayersOnTechnique(layers);
461}
462
463void QMetalRoughMaterial::setTextureScale(float textureScale)
464{
465 Q_D(QMetalRoughMaterial);
466 d->m_textureScaleParameter->setValue(textureScale);
467}
468
469} // namespace Qt3DExtras
470
471QT_END_NAMESPACE
472
473#include "moc_qmetalroughmaterial.cpp"
474

source code of qt3d/src/extras/defaults/qmetalroughmaterial.cpp