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