QGIS API Documentation 4.1.0-Master (ca2ac17535b)
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 , mBaseColorMapParameter( new Qt3DRender::QParameter( u"baseColorMap"_s, QVariant(), this ) )
41 , mMetalnessMapParameter( new Qt3DRender::QParameter( u"metalnessMap"_s, QVariant(), this ) )
42 , mRoughnessMapParameter( new Qt3DRender::QParameter( u"roughnessMap"_s, QVariant(), this ) )
43 , mAmbientOcclusionMapParameter( new Qt3DRender::QParameter( u"ambientOcclusionMap"_s, QVariant(), this ) )
44 , mNormalMapParameter( new Qt3DRender::QParameter( u"normalMap"_s, QVariant(), this ) )
45 , mHeightMapParameter( new Qt3DRender::QParameter( u"heightMap"_s, QVariant(), this ) )
46 , mParallaxScaleParameter( new Qt3DRender::QParameter( u"parallaxScale"_s, 0.1f, this ) )
47 , mEmissionMapParameter( new Qt3DRender::QParameter( u"emissionMap"_s, QVariant(), this ) )
48 , mEmissiveColorParameter( new Qt3DRender::QParameter( u"emissiveColor"_s, Qgs3DUtils::srgbToLinear( QColor( 0, 0, 0 ) ), this ) )
49 , mEmissionFactorParameter( new Qt3DRender::QParameter( u"emissiveFactor"_s, 1.0f, this ) )
50 , mTextureScaleParameter( new Qt3DRender::QParameter( u"texCoordScale"_s, 1.0f, this ) )
51 , mTextureRotationParameter( new Qt3DRender::QParameter( u"texCoordRotation"_s, 0.0f, this ) )
52 , mOpacityParameter( new Qt3DRender::QParameter( u"opacity"_s, 1.0f ) )
53 , mMetalRoughEffect( new Qt3DRender::QEffect( this ) )
54 , mMetalRoughGL3Technique( new Qt3DRender::QTechnique( this ) )
55 , mMetalRoughGL3RenderPass( new Qt3DRender::QRenderPass( this ) )
56 , mMetalRoughGL3Shader( new Qt3DRender::QShaderProgram( this ) )
57 , mFilterKey( new Qt3DRender::QFilterKey( this ) )
58{
59 init();
60}
61
62QgsMetalRoughMaterial::~QgsMetalRoughMaterial() = default;
63
64void QgsMetalRoughMaterial::setBaseColor( const QColor &baseColor )
65{
66 mBaseColorParameter->setValue( Qgs3DUtils::srgbToLinear( baseColor ) );
67 bool oldUsingBaseColorMap = mUsingBaseColorMap;
68
69 mUsingBaseColorMap = false;
70 if ( mMetalRoughEffect->parameters().contains( mBaseColorMapParameter ) )
71 mMetalRoughEffect->removeParameter( mBaseColorMapParameter );
72 mMetalRoughEffect->addParameter( mBaseColorParameter );
73
74 if ( oldUsingBaseColorMap != mUsingBaseColorMap )
75 {
76 updateShaders();
77 }
78}
79
80void QgsMetalRoughMaterial::setBaseColorTexture( Qt3DRender::QAbstractTexture *baseColor )
81{
82 mBaseColorMapParameter->setValue( QVariant::fromValue( baseColor ) );
83 bool oldUsingBaseColorMap = mUsingBaseColorMap;
84
85 mUsingBaseColorMap = true;
86 mMetalRoughEffect->addParameter( mBaseColorMapParameter );
87 if ( mMetalRoughEffect->parameters().contains( mBaseColorParameter ) )
88 mMetalRoughEffect->removeParameter( mBaseColorParameter );
89
90 if ( oldUsingBaseColorMap != mUsingBaseColorMap )
91 {
92 updateShaders();
93 }
94}
95
96void QgsMetalRoughMaterial::setMetalness( float metalness )
97{
98 mMetalnessParameter->setValue( metalness );
99 bool oldUsingMetalnessMap = mUsingMetalnessMap;
100
101 mUsingMetalnessMap = false;
102 if ( mMetalRoughEffect->parameters().contains( mMetalnessMapParameter ) )
103 mMetalRoughEffect->removeParameter( mMetalnessMapParameter );
104 mMetalRoughEffect->addParameter( mMetalnessParameter );
105
106 if ( oldUsingMetalnessMap != mUsingMetalnessMap )
107 {
108 updateShaders();
109 }
110}
111
112void QgsMetalRoughMaterial::setMetalnessTexture( Qt3DRender::QAbstractTexture *metalness )
113{
114 mMetalnessMapParameter->setValue( QVariant::fromValue( metalness ) );
115 bool oldUsingMetalnessMap = mUsingMetalnessMap;
116
117 mUsingMetalnessMap = true;
118 mMetalRoughEffect->addParameter( mMetalnessMapParameter );
119 if ( mMetalRoughEffect->parameters().contains( mMetalnessParameter ) )
120 mMetalRoughEffect->removeParameter( mMetalnessParameter );
121
122 if ( oldUsingMetalnessMap != mUsingMetalnessMap )
123 {
124 updateShaders();
125 }
126}
127
128void QgsMetalRoughMaterial::setRoughness( float roughness )
129{
130 mRoughnessParameter->setValue( roughness );
131 bool oldUsingRoughnessMap = mUsingRoughnessMap;
132
133 mUsingRoughnessMap = false;
134 if ( mMetalRoughEffect->parameters().contains( mRoughnessMapParameter ) )
135 mMetalRoughEffect->removeParameter( mRoughnessMapParameter );
136 mMetalRoughEffect->addParameter( mRoughnessParameter );
137
138 if ( oldUsingRoughnessMap != mUsingRoughnessMap )
139 {
140 updateShaders();
141 }
142}
143
144void QgsMetalRoughMaterial::setRoughnessTexture( Qt3DRender::QAbstractTexture *roughness )
145{
146 mRoughnessMapParameter->setValue( QVariant::fromValue( roughness ) );
147 bool oldUsingRoughnessMap = mUsingRoughnessMap;
148
149 mUsingRoughnessMap = true;
150 mMetalRoughEffect->addParameter( mRoughnessMapParameter );
151 if ( mMetalRoughEffect->parameters().contains( mRoughnessParameter ) )
152 mMetalRoughEffect->removeParameter( mRoughnessParameter );
153
154 if ( oldUsingRoughnessMap != mUsingRoughnessMap )
155 {
156 updateShaders();
157 }
158}
159
160void QgsMetalRoughMaterial::setAmbientOcclusionTexture( Qt3DRender::QAbstractTexture *ambientOcclusion )
161{
162 bool oldUsingAmbientOcclusionMap = mUsingAmbientOcclusionMap;
163
164 if ( ambientOcclusion )
165 {
166 mAmbientOcclusionMapParameter->setValue( QVariant::fromValue( ambientOcclusion ) );
167 mUsingAmbientOcclusionMap = true;
168 mMetalRoughEffect->addParameter( mAmbientOcclusionMapParameter );
169 }
170 else
171 {
172 mAmbientOcclusionMapParameter->setValue( QVariant() );
173 mUsingAmbientOcclusionMap = false;
174 if ( mMetalRoughEffect->parameters().contains( mAmbientOcclusionMapParameter ) )
175 mMetalRoughEffect->removeParameter( mAmbientOcclusionMapParameter );
176 }
177
178 if ( oldUsingAmbientOcclusionMap != mUsingAmbientOcclusionMap )
179 {
180 updateShaders();
181 }
182}
183
184void QgsMetalRoughMaterial::setNormalTexture( Qt3DRender::QAbstractTexture *normal )
185{
186 bool oldUsingNormalMap = mUsingNormalMap;
187
188 if ( normal )
189 {
190 mNormalMapParameter->setValue( QVariant::fromValue( normal ) );
191 mUsingNormalMap = true;
192 mMetalRoughEffect->addParameter( mNormalMapParameter );
193 }
194 else
195 {
196 mNormalMapParameter->setValue( QVariant() );
197 mUsingNormalMap = false;
198 if ( mMetalRoughEffect->parameters().contains( mNormalMapParameter ) )
199 mMetalRoughEffect->removeParameter( mNormalMapParameter );
200 }
201
202 if ( oldUsingNormalMap != mUsingNormalMap )
203 {
204 updateShaders();
205 }
206}
207
208void QgsMetalRoughMaterial::setHeightTexture( Qt3DRender::QAbstractTexture *height )
209{
210 bool oldUsingHeightMap = mUsingHeightMap;
211
212 if ( height )
213 {
214 mHeightMapParameter->setValue( QVariant::fromValue( height ) );
215 mUsingHeightMap = true;
216 mMetalRoughEffect->addParameter( mHeightMapParameter );
217 }
218 else
219 {
220 mHeightMapParameter->setValue( QVariant() );
221 mUsingHeightMap = false;
222 if ( mMetalRoughEffect->parameters().contains( mHeightMapParameter ) )
223 mMetalRoughEffect->removeParameter( mHeightMapParameter );
224 }
225
226 if ( oldUsingHeightMap != mUsingHeightMap )
227 {
228 updateShaders();
229 }
230}
231
232void QgsMetalRoughMaterial::setParallaxScale( double scale )
233{
234 mParallaxScaleParameter->setValue( scale );
235}
236
237void QgsMetalRoughMaterial::setEmissionColor( const QColor &color )
238{
239 mEmissiveColorParameter->setValue( Qgs3DUtils::srgbToLinear( color ) );
240 const bool oldUsingEmissionMap = mUsingEmissionMap;
241
242 mUsingEmissionMap = false;
243 if ( mMetalRoughEffect->parameters().contains( mEmissionMapParameter ) )
244 mMetalRoughEffect->removeParameter( mEmissionMapParameter );
245 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
246
247 if ( oldUsingEmissionMap != mUsingEmissionMap )
248 {
249 updateShaders();
250 }
251}
252
253void QgsMetalRoughMaterial::setEmissionTexture( Qt3DRender::QAbstractTexture *emission )
254{
255 const bool oldUsingEmissionMap = mUsingEmissionMap;
256
257 if ( emission )
258 {
259 mEmissionMapParameter->setValue( QVariant::fromValue( emission ) );
260 mUsingEmissionMap = true;
261 mMetalRoughEffect->addParameter( mEmissionMapParameter );
262 if ( mMetalRoughEffect->parameters().contains( mEmissiveColorParameter ) )
263 mMetalRoughEffect->removeParameter( mEmissiveColorParameter );
264 }
265 else
266 {
267 mEmissionMapParameter->setValue( QVariant() );
268 mUsingEmissionMap = false;
269 if ( mMetalRoughEffect->parameters().contains( mEmissionMapParameter ) )
270 mMetalRoughEffect->removeParameter( mEmissionMapParameter );
271 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
272 }
273
274 if ( oldUsingEmissionMap != mUsingEmissionMap )
275 {
276 updateShaders();
277 }
278}
279
280void QgsMetalRoughMaterial::setEmissionFactor( double factor )
281{
282 mEmissionFactorParameter->setValue( factor );
283}
284
285void QgsMetalRoughMaterial::setTextureScale( float textureScale )
286{
287 mTextureScaleParameter->setValue( textureScale );
288}
289
290void QgsMetalRoughMaterial::setTextureRotation( float textureRotation )
291{
292 mTextureRotationParameter->setValue( textureRotation );
293}
294
295void QgsMetalRoughMaterial::init()
296{
297 mMetalRoughGL3Technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
298 mMetalRoughGL3Technique->graphicsApiFilter()->setMajorVersion( 3 );
299 mMetalRoughGL3Technique->graphicsApiFilter()->setMinorVersion( 3 );
300 mMetalRoughGL3Technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
301
302 mFilterKey->setParent( this );
303 mFilterKey->setName( u"renderingStyle"_s );
304 mFilterKey->setValue( u"forward"_s );
305
306 mMetalRoughGL3Technique->addFilterKey( mFilterKey );
307 mMetalRoughGL3RenderPass->setShaderProgram( mMetalRoughGL3Shader );
308 mMetalRoughGL3Technique->addRenderPass( mMetalRoughGL3RenderPass );
309 mMetalRoughEffect->addTechnique( mMetalRoughGL3Technique );
310
311 // ensure IBL cubemaps are seamless -- this should be safe to do here, the only cubemap
312 // lookups happening in the metalrough shader is for IBL
313 mMetalRoughGL3RenderPass->addRenderState( new Qt3DRender::QSeamlessCubemap( this ) );
314
315 // Given parameters a parent
316 mBaseColorMapParameter->setParent( mMetalRoughEffect );
317 mMetalnessMapParameter->setParent( mMetalRoughEffect );
318 mRoughnessMapParameter->setParent( mMetalRoughEffect );
319 mNormalMapParameter->setParent( mMetalRoughEffect );
320 mHeightMapParameter->setParent( mMetalRoughEffect );
321 mAmbientOcclusionMapParameter->setParent( mMetalRoughEffect );
322 mEmissionMapParameter->setParent( mMetalRoughEffect );
323
324 mMetalRoughEffect->addParameter( mBaseColorParameter );
325 mMetalRoughEffect->addParameter( mMetalnessParameter );
326 mMetalRoughEffect->addParameter( mRoughnessParameter );
327 mMetalRoughEffect->addParameter( mParallaxScaleParameter );
328 mMetalRoughEffect->addParameter( mEmissiveColorParameter );
329 mMetalRoughEffect->addParameter( mEmissionFactorParameter );
330 mMetalRoughEffect->addParameter( mTextureScaleParameter );
331 mMetalRoughEffect->addParameter( mTextureRotationParameter );
332 mMetalRoughEffect->addParameter( mOpacityParameter );
333
334 setEffect( mMetalRoughEffect );
335
336 updateShaders();
337}
338
339void QgsMetalRoughMaterial::updateShaders()
340{
341 QByteArray fragmentShaderCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/metalrough.frag"_s ) );
342
343 // pre-process fragment shader and add #defines based on whether using maps for some properties
344 QStringList fragShaderDefines;
345 if ( mUsingBaseColorMap )
346 fragShaderDefines += "BASE_COLOR_MAP";
347 if ( mUsingMetalnessMap )
348 fragShaderDefines += "METALNESS_MAP";
349 if ( mUsingRoughnessMap )
350 fragShaderDefines += "ROUGHNESS_MAP";
351 if ( mUsingAmbientOcclusionMap )
352 fragShaderDefines += "AMBIENT_OCCLUSION_MAP";
353 if ( mUsingNormalMap )
354 fragShaderDefines += "NORMAL_MAP";
355 if ( mUsingHeightMap )
356 fragShaderDefines += "HEIGHT_MAP";
357 if ( mUsingEmissionMap )
358 fragShaderDefines += "EMISSION_MAP";
359 if ( mFlatShading )
360 fragShaderDefines += "FLAT_SHADING";
361 if ( mEnableEnvironmentalLighting )
362 fragShaderDefines += "ENABLE_IBL";
363
364 if ( mInstanced )
365 {
366 QStringList defines = { u"HAS_TEXTURE"_s, u"HAS_TANGENT"_s };
367 if ( mInstanceFlags.testFlag( Qgis::InstancedMaterialFlag::DataDefinedScale ) )
368 defines << u"USE_INSTANCE_SCALE"_s;
369 if ( mInstanceFlags.testFlag( Qgis::InstancedMaterialFlag::DataDefinedRotation ) )
370 defines << u"USE_INSTANCE_ROTATION"_s;
371 const QByteArray vertCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/instanced.vert"_s ) );
372 mMetalRoughGL3Shader->setVertexShaderCode( Qgs3DUtils::addDefinesToShaderCode( vertCode, defines ) );
373 }
374 else if ( mDataDefinedEnabled )
375 {
376 fragShaderDefines += "DATA_DEFINED";
377 mMetalRoughGL3Shader->setShaderCode( Qt3DRender::QShaderProgram::Vertex, Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/metalroughDataDefined.vert"_s ) ) );
378 }
379 else
380 {
381 const QByteArray vertexShaderCode = Qt3DRender::QShaderProgram::loadSource( QUrl( u"qrc:/shaders/default.vert"_s ) );
382 const QByteArray finalVertexShaderCode = Qgs3DUtils::addDefinesToShaderCode( vertexShaderCode, { "TEXTURE_ROTATION" } );
383 mMetalRoughGL3Shader->setVertexShaderCode( finalVertexShaderCode );
384 }
385
386 const QByteArray finalShaderCode = Qgs3DUtils::addDefinesToShaderCode( fragmentShaderCode, fragShaderDefines );
387 mMetalRoughGL3Shader->setFragmentShaderCode( finalShaderCode );
388}
389
390void QgsMetalRoughMaterial::setFlatShadingEnabled( bool enabled )
391{
392 if ( enabled != mFlatShading )
393 {
394 mFlatShading = enabled;
395 updateShaders();
396 }
397}
398
399void QgsMetalRoughMaterial::setOpacity( float opacity )
400{
401 mOpacityParameter->setValue( opacity );
402}
403
404void QgsMetalRoughMaterial::setDataDefinedEnabled( bool enabled )
405{
406 if ( enabled != mDataDefinedEnabled )
407 {
408 mDataDefinedEnabled = enabled;
409 updateShaders();
410 }
411}
412
413void QgsMetalRoughMaterial::setEnvironmentalLightingEnabled( bool enabled )
414{
415 if ( enabled != mEnableEnvironmentalLighting )
416 {
417 mEnableEnvironmentalLighting = enabled;
418 updateShaders();
419 }
420}
421
422void QgsMetalRoughMaterial::setInstancingEnabled( bool enabled, Qgis::InstancedMaterialFlags flags )
423{
424 mInstanced = enabled;
425 mInstanceFlags = flags;
426 updateShaders();
427}
428
QFlags< InstancedMaterialFlag > InstancedMaterialFlags
Definition qgis.h:4365
@ DataDefinedRotation
Per-instance data-defined rotation.
Definition qgis.h:4362
@ DataDefinedScale
Per-instance data-defined scale.
Definition qgis.h:4361
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