QGIS API Documentation 4.1.0-Master (26185ffb827)
Loading...
Searching...
No Matches
qgsmetalroughmaterial.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsmetalroughmaterial.cpp
3 --------------------------------------
4 Date : December 2023
5 Copyright : (C) 2023 by Nyall Dawson
6 Email : nyall dot dawson at gmail dot com
7 ***************************************************************************
8 * *
9 * This program is free software; you can redistribute it and/or modify *
10 * it under the terms of the GNU General Public License as published by *
11 * the Free Software Foundation; either version 2 of the License, or *
12 * (at your option) any later version. *
13 * *
14 ***************************************************************************/
15
17
18#include "qgs3dutils.h"
19
20#include <QString>
21#include <Qt3DRender/QAbstractTexture>
22#include <Qt3DRender/QGraphicsApiFilter>
23#include <Qt3DRender/QParameter>
24#include <Qt3DRender/QRenderPass>
25#include <Qt3DRender/QSeamlessCubemap>
26#include <Qt3DRender/QShaderProgramBuilder>
27#include <Qt3DRender/QTechnique>
28#include <Qt3DRender/QTexture>
29
30#include "moc_qgsmetalroughmaterial.cpp"
31
32using namespace Qt::StringLiterals;
33
35QgsMetalRoughMaterial::QgsMetalRoughMaterial( QNode *parent )
36 : QgsMaterial( parent )
37 , mBaseColorParameter( new Qt3DRender::QParameter( u"baseColor"_s, Qgs3DUtils::srgbToLinear( QColor( "grey" ) ), this ) )
38 , mMetalnessParameter( new Qt3DRender::QParameter( u"metalness"_s, 0.0f, this ) )
39 , mRoughnessParameter( new Qt3DRender::QParameter( u"roughness"_s, 0.0f, this ) )
40 , mReflectanceParameter( new Qt3DRender::QParameter( u"reflectance"_s, 0.5f, this ) )
41 , mAnisotropyParameter( new Qt3DRender::QParameter( u"anisotropy"_s, 0.0f, this ) )
42 , mAnisotropyRotationParameter( new Qt3DRender::QParameter( u"anisotropyRotation"_s, 0.0f, this ) )
43 , mBaseColorMapParameter( new Qt3DRender::QParameter( u"baseColorMap"_s, QVariant(), this ) )
44 , mMetalnessMapParameter( new Qt3DRender::QParameter( u"metalnessMap"_s, QVariant(), this ) )
45 , mRoughnessMapParameter( new Qt3DRender::QParameter( u"roughnessMap"_s, QVariant(), this ) )
46 , mAmbientOcclusionMapParameter( new Qt3DRender::QParameter( u"ambientOcclusionMap"_s, QVariant(), this ) )
47 , mNormalMapParameter( new Qt3DRender::QParameter( u"normalMap"_s, QVariant(), this ) )
48 , mHeightMapParameter( new Qt3DRender::QParameter( u"heightMap"_s, QVariant(), this ) )
49 , mParallaxScaleParameter( new Qt3DRender::QParameter( u"parallaxScale"_s, 0.1f, this ) )
50 , mEmissionMapParameter( new Qt3DRender::QParameter( u"emissionMap"_s, QVariant(), this ) )
51 , mEmissiveColorParameter( new Qt3DRender::QParameter( u"emissiveColor"_s, Qgs3DUtils::srgbToLinear( QColor( 0, 0, 0 ) ), this ) )
52 , mEmissionFactorParameter( new Qt3DRender::QParameter( u"emissiveFactor"_s, 1.0f, this ) )
53 , mClearCoatFactorParameter( new Qt3DRender::QParameter( u"clearCoatFactor"_s, 0.0f, this ) )
54 , mClearCoatRoughnessParameter( new Qt3DRender::QParameter( u"clearCoatRoughness"_s, 0.0f, this ) )
55 , mTextureScaleParameter( new Qt3DRender::QParameter( u"texCoordScale"_s, 1.0f, this ) )
56 , mTextureRotationParameter( new Qt3DRender::QParameter( u"texCoordRotation"_s, 0.0f, this ) )
57 , mOpacityParameter( new Qt3DRender::QParameter( u"opacity"_s, 1.0f ) )
58 , mMetalRoughEffect( new Qt3DRender::QEffect( this ) )
59 , mMetalRoughGL3Technique( new Qt3DRender::QTechnique( this ) )
60 , mMetalRoughGL3RenderPass( new Qt3DRender::QRenderPass( this ) )
61 , mMetalRoughGL3Shader( new Qt3DRender::QShaderProgram( this ) )
62 , mFilterKey( new Qt3DRender::QFilterKey( this ) )
63{
64 init();
65}
66
67QgsMetalRoughMaterial::~QgsMetalRoughMaterial() = default;
68
69void QgsMetalRoughMaterial::setBaseColor( const QColor &baseColor )
70{
71 mBaseColorParameter->setValue( Qgs3DUtils::srgbToLinear( baseColor ) );
72 bool oldUsingBaseColorMap = mUsingBaseColorMap;
73
74 mUsingBaseColorMap = false;
75 if ( mMetalRoughEffect->parameters().contains( mBaseColorMapParameter ) )
76 mMetalRoughEffect->removeParameter( mBaseColorMapParameter );
77 mMetalRoughEffect->addParameter( mBaseColorParameter );
78
79 if ( oldUsingBaseColorMap != mUsingBaseColorMap )
80 {
81 updateShaders();
82 }
83}
84
85void QgsMetalRoughMaterial::setBaseColorTexture( Qt3DRender::QAbstractTexture *baseColor )
86{
87 mBaseColorMapParameter->setValue( QVariant::fromValue( baseColor ) );
88 bool oldUsingBaseColorMap = mUsingBaseColorMap;
89
90 mUsingBaseColorMap = true;
91 mMetalRoughEffect->addParameter( mBaseColorMapParameter );
92 if ( mMetalRoughEffect->parameters().contains( mBaseColorParameter ) )
93 mMetalRoughEffect->removeParameter( mBaseColorParameter );
94
95 if ( oldUsingBaseColorMap != mUsingBaseColorMap )
96 {
97 updateShaders();
98 }
99}
100
101void QgsMetalRoughMaterial::setMetalness( float metalness )
102{
103 mMetalnessParameter->setValue( metalness );
104 bool oldUsingMetalnessMap = mUsingMetalnessMap;
105
106 mUsingMetalnessMap = false;
107 if ( mMetalRoughEffect->parameters().contains( mMetalnessMapParameter ) )
108 mMetalRoughEffect->removeParameter( mMetalnessMapParameter );
109 mMetalRoughEffect->addParameter( mMetalnessParameter );
110
111 if ( oldUsingMetalnessMap != mUsingMetalnessMap )
112 {
113 updateShaders();
114 }
115}
116
117void QgsMetalRoughMaterial::setMetalnessTexture( Qt3DRender::QAbstractTexture *metalness )
118{
119 mMetalnessMapParameter->setValue( QVariant::fromValue( metalness ) );
120 bool oldUsingMetalnessMap = mUsingMetalnessMap;
121
122 mUsingMetalnessMap = true;
123 mMetalRoughEffect->addParameter( mMetalnessMapParameter );
124 if ( mMetalRoughEffect->parameters().contains( mMetalnessParameter ) )
125 mMetalRoughEffect->removeParameter( mMetalnessParameter );
126
127 if ( oldUsingMetalnessMap != mUsingMetalnessMap )
128 {
129 updateShaders();
130 }
131}
132
133void QgsMetalRoughMaterial::setRoughness( float roughness )
134{
135 mRoughnessParameter->setValue( roughness );
136 bool oldUsingRoughnessMap = mUsingRoughnessMap;
137
138 mUsingRoughnessMap = false;
139 if ( mMetalRoughEffect->parameters().contains( mRoughnessMapParameter ) )
140 mMetalRoughEffect->removeParameter( mRoughnessMapParameter );
141 mMetalRoughEffect->addParameter( mRoughnessParameter );
142
143 if ( oldUsingRoughnessMap != mUsingRoughnessMap )
144 {
145 updateShaders();
146 }
147}
148
149void QgsMetalRoughMaterial::setRoughnessTexture( Qt3DRender::QAbstractTexture *roughness )
150{
151 mRoughnessMapParameter->setValue( QVariant::fromValue( roughness ) );
152 bool oldUsingRoughnessMap = mUsingRoughnessMap;
153
154 mUsingRoughnessMap = true;
155 mMetalRoughEffect->addParameter( mRoughnessMapParameter );
156 if ( mMetalRoughEffect->parameters().contains( mRoughnessParameter ) )
157 mMetalRoughEffect->removeParameter( mRoughnessParameter );
158
159 if ( oldUsingRoughnessMap != mUsingRoughnessMap )
160 {
161 updateShaders();
162 }
163}
164
165void QgsMetalRoughMaterial::setReflectance( float reflectance )
166{
167 mReflectanceParameter->setValue( QVariant::fromValue( reflectance ) );
168}
169
170void QgsMetalRoughMaterial::setAnisotropy( float anisotropy )
171{
172 const bool oldUsingAnisotropy = mMetalRoughEffect->parameters().contains( mAnisotropyParameter );
173 mAnisotropyParameter->setValue( anisotropy );
174 const bool newUsingAnisotropy = anisotropy > 0;
175 if ( newUsingAnisotropy )
176 {
177 if ( !oldUsingAnisotropy )
178 {
179 mMetalRoughEffect->addParameter( mAnisotropyParameter );
180 mMetalRoughEffect->addParameter( mAnisotropyRotationParameter );
181 }
182 }
183 else if ( oldUsingAnisotropy )
184 {
185 mMetalRoughEffect->removeParameter( mAnisotropyParameter );
186 mMetalRoughEffect->removeParameter( mAnisotropyRotationParameter );
187 }
188
189 if ( oldUsingAnisotropy != newUsingAnisotropy )
190 {
191 updateShaders();
192 }
193}
194
195void QgsMetalRoughMaterial::setAnisotropyRotation( float rotation )
196{
197 mAnisotropyRotationParameter->setValue( M_PI * rotation / 180.0 );
198}
199
200void QgsMetalRoughMaterial::setAmbientOcclusionTexture( Qt3DRender::QAbstractTexture *ambientOcclusion )
201{
202 bool oldUsingAmbientOcclusionMap = mUsingAmbientOcclusionMap;
203
204 if ( ambientOcclusion )
205 {
206 mAmbientOcclusionMapParameter->setValue( QVariant::fromValue( ambientOcclusion ) );
207 mUsingAmbientOcclusionMap = true;
208 mMetalRoughEffect->addParameter( mAmbientOcclusionMapParameter );
209 }
210 else
211 {
212 mAmbientOcclusionMapParameter->setValue( QVariant() );
213 mUsingAmbientOcclusionMap = false;
214 if ( mMetalRoughEffect->parameters().contains( mAmbientOcclusionMapParameter ) )
215 mMetalRoughEffect->removeParameter( mAmbientOcclusionMapParameter );
216 }
217
218 if ( oldUsingAmbientOcclusionMap != mUsingAmbientOcclusionMap )
219 {
220 updateShaders();
221 }
222}
223
224void QgsMetalRoughMaterial::setNormalTexture( Qt3DRender::QAbstractTexture *normal )
225{
226 bool oldUsingNormalMap = mUsingNormalMap;
227
228 if ( normal )
229 {
230 mNormalMapParameter->setValue( QVariant::fromValue( normal ) );
231 mUsingNormalMap = true;
232 mMetalRoughEffect->addParameter( mNormalMapParameter );
233 }
234 else
235 {
236 mNormalMapParameter->setValue( QVariant() );
237 mUsingNormalMap = false;
238 if ( mMetalRoughEffect->parameters().contains( mNormalMapParameter ) )
239 mMetalRoughEffect->removeParameter( mNormalMapParameter );
240 }
241
242 if ( oldUsingNormalMap != mUsingNormalMap )
243 {
244 updateShaders();
245 }
246}
247
248void QgsMetalRoughMaterial::setHeightTexture( Qt3DRender::QAbstractTexture *height )
249{
250 bool oldUsingHeightMap = mUsingHeightMap;
251
252 if ( height )
253 {
254 mHeightMapParameter->setValue( QVariant::fromValue( height ) );
255 mUsingHeightMap = true;
256 mMetalRoughEffect->addParameter( mHeightMapParameter );
257 }
258 else
259 {
260 mHeightMapParameter->setValue( QVariant() );
261 mUsingHeightMap = false;
262 if ( mMetalRoughEffect->parameters().contains( mHeightMapParameter ) )
263 mMetalRoughEffect->removeParameter( mHeightMapParameter );
264 }
265
266 if ( oldUsingHeightMap != mUsingHeightMap )
267 {
268 updateShaders();
269 }
270}
271
272void QgsMetalRoughMaterial::setParallaxScale( double scale )
273{
274 mParallaxScaleParameter->setValue( scale );
275}
276
277void QgsMetalRoughMaterial::setEmissionColor( const QColor &color )
278{
279 mEmissiveColorParameter->setValue( Qgs3DUtils::srgbToLinear( color ) );
280 const bool oldUsingEmissionMap = mUsingEmissionMap;
281
282 mUsingEmissionMap = false;
283 if ( mMetalRoughEffect->parameters().contains( mEmissionMapParameter ) )
284 mMetalRoughEffect->removeParameter( mEmissionMapParameter );
285 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
286
287 if ( oldUsingEmissionMap != mUsingEmissionMap )
288 {
289 updateShaders();
290 }
291}
292
293void QgsMetalRoughMaterial::setEmissionTexture( Qt3DRender::QAbstractTexture *emission )
294{
295 const bool oldUsingEmissionMap = mUsingEmissionMap;
296
297 if ( emission )
298 {
299 mEmissionMapParameter->setValue( QVariant::fromValue( emission ) );
300 mUsingEmissionMap = true;
301 mMetalRoughEffect->addParameter( mEmissionMapParameter );
302 if ( mMetalRoughEffect->parameters().contains( mEmissiveColorParameter ) )
303 mMetalRoughEffect->removeParameter( mEmissiveColorParameter );
304 }
305 else
306 {
307 mEmissionMapParameter->setValue( QVariant() );
308 mUsingEmissionMap = false;
309 if ( mMetalRoughEffect->parameters().contains( mEmissionMapParameter ) )
310 mMetalRoughEffect->removeParameter( mEmissionMapParameter );
311 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
312 }
313
314 if ( oldUsingEmissionMap != mUsingEmissionMap )
315 {
316 updateShaders();
317 }
318}
319
320void QgsMetalRoughMaterial::setEmissionFactor( double factor )
321{
322 mEmissionFactorParameter->setValue( factor );
323}
324
325void QgsMetalRoughMaterial::setClearCoatFactor( float factor )
326{
327 mClearCoatFactorParameter->setValue( factor );
328 const bool oldUsingClearCoat = mMetalRoughEffect->parameters().contains( mClearCoatFactorParameter );
329 const bool newUsingClearCoat = factor > 0;
330 if ( newUsingClearCoat )
331 {
332 if ( !oldUsingClearCoat )
333 {
334 mMetalRoughEffect->addParameter( mClearCoatFactorParameter );
335 mMetalRoughEffect->addParameter( mClearCoatRoughnessParameter );
336 }
337 }
338 else if ( oldUsingClearCoat )
339 {
340 mMetalRoughEffect->removeParameter( mClearCoatFactorParameter );
341 mMetalRoughEffect->removeParameter( mClearCoatRoughnessParameter );
342 }
343
344 if ( oldUsingClearCoat != newUsingClearCoat )
345 {
346 updateShaders();
347 }
348}
349
350void QgsMetalRoughMaterial::setClearCoatRoughness( float roughness )
351{
352 mClearCoatRoughnessParameter->setValue( roughness );
353}
354
355void QgsMetalRoughMaterial::setTextureScale( float textureScale )
356{
357 mTextureScaleParameter->setValue( textureScale );
358}
359
360void QgsMetalRoughMaterial::setTextureRotation( float textureRotation )
361{
362 mTextureRotationParameter->setValue( textureRotation );
363}
364
365void QgsMetalRoughMaterial::init()
366{
367 mMetalRoughGL3Technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
368 mMetalRoughGL3Technique->graphicsApiFilter()->setMajorVersion( 3 );
369 mMetalRoughGL3Technique->graphicsApiFilter()->setMinorVersion( 3 );
370 mMetalRoughGL3Technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
371
372 mFilterKey->setParent( this );
373 mFilterKey->setName( u"renderingStyle"_s );
374 mFilterKey->setValue( u"forward"_s );
375
376 mMetalRoughGL3Technique->addFilterKey( mFilterKey );
377 mMetalRoughGL3RenderPass->setShaderProgram( mMetalRoughGL3Shader );
378 mMetalRoughGL3Technique->addRenderPass( mMetalRoughGL3RenderPass );
379 mMetalRoughEffect->addTechnique( mMetalRoughGL3Technique );
380
381 // ensure IBL cubemaps are seamless -- this should be safe to do here, the only cubemap
382 // lookups happening in the metalrough shader is for IBL
383 mMetalRoughGL3RenderPass->addRenderState( new Qt3DRender::QSeamlessCubemap( this ) );
384
385 // Given parameters a parent
386 mBaseColorMapParameter->setParent( mMetalRoughEffect );
387 mMetalnessMapParameter->setParent( mMetalRoughEffect );
388 mRoughnessMapParameter->setParent( mMetalRoughEffect );
389 mNormalMapParameter->setParent( mMetalRoughEffect );
390 mHeightMapParameter->setParent( mMetalRoughEffect );
391 mAmbientOcclusionMapParameter->setParent( mMetalRoughEffect );
392 mEmissionMapParameter->setParent( mMetalRoughEffect );
393
394 mMetalRoughEffect->addParameter( mBaseColorParameter );
395 mMetalRoughEffect->addParameter( mMetalnessParameter );
396 mMetalRoughEffect->addParameter( mRoughnessParameter );
397 mMetalRoughEffect->addParameter( mReflectanceParameter );
398 mMetalRoughEffect->addParameter( mParallaxScaleParameter );
399 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
400 mMetalRoughEffect->addParameter( mEmissionFactorParameter );
401 mMetalRoughEffect->addParameter( mTextureScaleParameter );
402 mMetalRoughEffect->addParameter( mTextureRotationParameter );
403 mMetalRoughEffect->addParameter( mOpacityParameter );
404
405 setEffect( mMetalRoughEffect );
406
407 updateShaders();
408}
409
410void QgsMetalRoughMaterial::updateShaders()
411{
412 QByteArray fragmentShaderCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/metalrough.frag"_s ) );
413
414 // pre-process fragment shader and add #defines based on whether using maps for some properties
415 QStringList fragShaderDefines;
416 if ( mUsingBaseColorMap )
417 fragShaderDefines += "BASE_COLOR_MAP";
418 if ( mUsingMetalnessMap )
419 fragShaderDefines += "METALNESS_MAP";
420 if ( mUsingRoughnessMap )
421 fragShaderDefines += "ROUGHNESS_MAP";
422 if ( mUsingAmbientOcclusionMap )
423 fragShaderDefines += "AMBIENT_OCCLUSION_MAP";
424 if ( mUsingNormalMap )
425 fragShaderDefines += "NORMAL_MAP";
426 if ( mUsingHeightMap )
427 fragShaderDefines += "HEIGHT_MAP";
428 if ( mUsingEmissionMap )
429 fragShaderDefines += "EMISSION_MAP";
430 if ( mFlatShading )
431 fragShaderDefines += "FLAT_SHADING";
432 if ( mMetalRoughEffect->parameters().contains( mAnisotropyParameter ) )
433 fragShaderDefines += "ANISOTROPY";
434 if ( mMetalRoughEffect->parameters().contains( mClearCoatFactorParameter ) )
435 fragShaderDefines += "CLEAR_COAT";
436 if ( mEnableEnvironmentalLighting )
437 fragShaderDefines += "ENABLE_IBL";
438
439 if ( mInstanced )
440 {
441 QStringList defines = { u"HAS_TEXTURE"_s, u"HAS_TANGENT"_s };
442 if ( mInstanceFlags.testFlag( Qgis::InstancedMaterialFlag::DataDefinedScale ) )
443 defines << u"USE_INSTANCE_SCALE"_s;
444 if ( mInstanceFlags.testFlag( Qgis::InstancedMaterialFlag::DataDefinedRotation ) )
445 defines << u"USE_INSTANCE_ROTATION"_s;
446 const QByteArray vertCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/instanced.vert"_s ) );
447 mMetalRoughGL3Shader->setVertexShaderCode( Qgs3DUtils::addDefinesToShaderCode( vertCode, defines ) );
448 }
449 else if ( mDataDefinedEnabled )
450 {
451 fragShaderDefines += "DATA_DEFINED";
452 mMetalRoughGL3Shader->setShaderCode( Qt3DRender::QShaderProgram::Vertex, Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/metalroughDataDefined.vert"_s ) ) );
453 }
454 else
455 {
456 const QByteArray vertexShaderCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/default.vert"_s ) );
457 const QByteArray finalVertexShaderCode = Qgs3DUtils::addDefinesToShaderCode( vertexShaderCode, { "TEXTURE_ROTATION" } );
458 mMetalRoughGL3Shader->setVertexShaderCode( finalVertexShaderCode );
459 }
460
461 const QByteArray finalShaderCode = Qgs3DUtils::addDefinesToShaderCode( fragmentShaderCode, fragShaderDefines );
462 mMetalRoughGL3Shader->setFragmentShaderCode( finalShaderCode );
463}
464
465void QgsMetalRoughMaterial::setFlatShadingEnabled( bool enabled )
466{
467 if ( enabled != mFlatShading )
468 {
469 mFlatShading = enabled;
470 updateShaders();
471 }
472}
473
474void QgsMetalRoughMaterial::setOpacity( float opacity )
475{
476 mOpacityParameter->setValue( opacity );
477}
478
479void QgsMetalRoughMaterial::setDataDefinedEnabled( bool enabled )
480{
481 if ( enabled != mDataDefinedEnabled )
482 {
483 mDataDefinedEnabled = enabled;
484 updateShaders();
485 }
486}
487
488void QgsMetalRoughMaterial::setEnvironmentalLightingEnabled( bool enabled )
489{
490 if ( enabled != mEnableEnvironmentalLighting )
491 {
492 mEnableEnvironmentalLighting = enabled;
493 updateShaders();
494 }
495}
496
497void QgsMetalRoughMaterial::setInstancingEnabled( bool enabled, Qgis::InstancedMaterialFlags flags )
498{
499 mInstanced = enabled;
500 mInstanceFlags = flags;
501 updateShaders();
502}
503
QFlags< InstancedMaterialFlag > InstancedMaterialFlags
Definition qgis.h:4385
@ DataDefinedRotation
Per-instance data-defined rotation.
Definition qgis.h:4382
@ DataDefinedScale
Per-instance data-defined scale.
Definition qgis.h:4381
Miscellaneous utility functions used from 3D code.
Definition qgs3dutils.h:66
static QByteArray addDefinesToShaderCode(const QByteArray &shaderCode, const QStringList &defines)
Inserts some define macros into a shader source code.
static QColor srgbToLinear(const QColor &color)
Converts a SRGB color to a linear color.
Base class for all materials used within QGIS 3D views.
Definition qgsmaterial.h:40