QGIS API Documentation 4.1.0-Master (497c8e4c39a)
Loading...
Searching...
No Matches
qgsphongtexturedmaterialsettings.cpp
Go to the documentation of this file.
1/***************************************************************************
2 qgsphongtexturedmaterialsettings.cpp
3 --------------------------------------
4 Date : August 2020
5 Copyright : (C) 2020 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 "qgsapplication.h"
19#include "qgscolorutils.h"
20#include "qgsimagecache.h"
22#include "qgssymbollayerutils.h"
23
24#include <QImage>
25#include <QMap>
26#include <QString>
27
28using namespace Qt::StringLiterals;
29
31{
32 return u"phongtextured"_s;
33}
34
53
58
63
65{
66 const QgsPhongTexturedMaterialSettings *otherPhong = dynamic_cast<const QgsPhongTexturedMaterialSettings *>( other );
67 if ( !otherPhong )
68 return false;
69
70 return *this == *otherPhong;
71}
72
77
79{
80 return !mDiffuseTexturePath.isEmpty();
81}
82
84{
85 const double avgDiffuseFactor = 0.3;
86 const double avgSpecularFactor = 0.2;
87
88 const double kAmbient = 0.2;
89 const double kDiffuse = 0.6;
90 const double kSpecular = 0.2;
91
92 const QColor diffuse = textureAverageColor();
93
94 double red = kAmbient * mAmbient.redF() + kDiffuse * avgDiffuseFactor * diffuse.redF() + kSpecular * avgSpecularFactor * mSpecular.redF();
95
96 double green = kAmbient * mAmbient.greenF() + kDiffuse * avgDiffuseFactor * diffuse.greenF() + kSpecular * avgSpecularFactor * mSpecular.greenF();
97
98 double blue = kAmbient * mAmbient.blueF() + kDiffuse * avgDiffuseFactor * diffuse.blueF() + kSpecular * avgSpecularFactor * mSpecular.blueF();
99
100 red = std::clamp( red, 0.0, 1.0 );
101 green = std::clamp( green, 0.0, 1.0 );
102 blue = std::clamp( blue, 0.0, 1.0 );
103
104 return QColor::fromRgbF( static_cast<float>( red ), static_cast<float>( green ), static_cast<float>( blue ), static_cast<float>( mOpacity ) );
105}
106
107
108QColor QgsPhongTexturedMaterialSettings::textureAverageColor() const
109{
110 if ( mTextureAverageColor.has_value() )
111 {
112 return *mTextureAverageColor;
113 }
114
115 bool fitsInCache = false;
116 QImage texture = QgsApplication::imageCache()->pathAsImage( mDiffuseTexturePath, QSize(), true, 1.0, fitsInCache );
117 if ( texture.isNull() )
118 {
119 mTextureAverageColor = QColor( 127, 127, 127 );
120 return *mTextureAverageColor;
121 }
122
123 if ( texture.format() != QImage::Format_ARGB32 )
124 {
125 texture = texture.convertToFormat( QImage::Format_ARGB32 );
126 }
127
128 unsigned long long red = 0;
129 unsigned long long green = 0;
130 unsigned long long blue = 0;
131 unsigned long long pixelCount = 0;
132
133 // downsampling to ensure a fast computation
134 const int sampleStep = std::min( texture.width() / 5, texture.height() / 5 );
135 const int width = texture.width();
136 const int height = texture.height();
137 for ( int y = 0; y < height; y += sampleStep )
138 {
139 const QRgb *line = reinterpret_cast< const QRgb * >( texture.constScanLine( y ) );
140 for ( int x = 0; x < width; x += sampleStep )
141 {
142 const QRgb pixel = line[x];
143 red += qRed( pixel );
144 green += qGreen( pixel );
145 blue += qBlue( pixel );
146 pixelCount++;
147 }
148 }
149
150 mTextureAverageColor = QColor( static_cast<int>( red / pixelCount ), static_cast<int>( green / pixelCount ), static_cast<int>( blue / pixelCount ) );
151 return *mTextureAverageColor;
152}
153
154void QgsPhongTexturedMaterialSettings::setColorsFromBase( const QColor &baseColor, float metallic )
155{
156 metallic = std::clamp( metallic, 0.0f, 1.0f );
157
158 const float baseR = baseColor.redF();
159 const float baseG = baseColor.greenF();
160 const float baseB = baseColor.blueF();
161
162 // ambient: stable, non-directional lighting
163 constexpr float AMBIENT_FACTOR = 0.2f;
164 mAmbient = QColor::fromRgbF( baseR * AMBIENT_FACTOR, baseG * AMBIENT_FACTOR, baseB * AMBIENT_FACTOR );
165
166 // F0: Fresnel reflectance at normal incidence
167 constexpr float F0_DIELECTRIC = 0.04f;
168
169 // specular
170 // * Non-metallic surfaces: Independent of base color
171 // * Metallic surfaces:
172 // - Reflect their own color
173 // - Linear interpolation from white to base color as metallic increases
174 mSpecular = QColor::fromRgbF( ( 1.0f - metallic ) * F0_DIELECTRIC + metallic * baseR, ( 1.0f - metallic ) * F0_DIELECTRIC + metallic * baseG, ( 1.0f - metallic ) * F0_DIELECTRIC + metallic * baseB );
175
176 constexpr float MIN_SHININESS = 32.0f;
177 constexpr float MAX_SHININESS = 200.0f;
178 mShininess = MIN_SHININESS + metallic * ( MAX_SHININESS - MIN_SHININESS );
179}
180
182{
183 setColorsFromBase( baseColor, 0.0f );
184}
185
186void QgsPhongTexturedMaterialSettings::readXml( const QDomElement &elem, const QgsReadWriteContext &context )
187{
188 mAmbient = QgsColorUtils::colorFromString( elem.attribute( u"ambient"_s, u"25,25,25"_s ) );
189 mSpecular = QgsColorUtils::colorFromString( elem.attribute( u"specular"_s, u"255,255,255"_s ) );
190 mShininess = elem.attribute( u"shininess"_s ).toDouble();
191 mOpacity = elem.attribute( u"opacity"_s, u"1.0"_s ).toDouble();
192 mDiffuseTexturePath = elem.attribute( u"diffuse_texture_path"_s, QString() );
193 mTextureScale = elem.attribute( u"texture_scale"_s, QString( "1.0" ) ).toDouble();
194 mTextureRotation = elem.attribute( u"texture-rotation"_s, QString( "0.0" ) ).toDouble();
195 mTextureOffset = QgsSymbolLayerUtils::decodePoint( elem.attribute( u"texture_offset"_s ) );
196
198}
199
200void QgsPhongTexturedMaterialSettings::writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const
201{
202 elem.setAttribute( u"ambient"_s, QgsColorUtils::colorToString( mAmbient ) );
203 elem.setAttribute( u"specular"_s, QgsColorUtils::colorToString( mSpecular ) );
204 elem.setAttribute( u"shininess"_s, mShininess );
205 elem.setAttribute( u"opacity"_s, mOpacity );
206 elem.setAttribute( u"diffuse_texture_path"_s, mDiffuseTexturePath );
207 elem.setAttribute( u"texture_scale"_s, mTextureScale );
208 elem.setAttribute( u"texture-rotation"_s, mTextureRotation );
209 if ( !qgsDoubleNear( mTextureOffset.x(), 0 ) || !qgsDoubleNear( mTextureOffset.y(), 0 ) )
210 elem.setAttribute( u"texture_offset"_s, QgsSymbolLayerUtils::encodePoint( mTextureOffset ) );
211
213}
MaterialRenderingTechnique
Material rendering techniques.
Definition qgis.h:4362
@ Points
Point based rendering, requires point data.
Definition qgis.h:4366
@ Triangles
Triangle based rendering (default).
Definition qgis.h:4363
@ TrianglesFromModel
Triangle based rendering, using a model object source.
Definition qgis.h:4368
@ Lines
Line based rendering, requires line data.
Definition qgis.h:4364
@ Billboards
Flat billboard rendering.
Definition qgis.h:4370
@ TrianglesDataDefined
Triangle based rendering with possibility of datadefined color.
Definition qgis.h:4369
@ InstancedPoints
Instanced based rendering, requiring triangles and point data.
Definition qgis.h:4365
@ TrianglesWithFixedTexture
Triangle based rendering, using a fixed, non-user-configurable texture (e.g. for terrain rendering).
Definition qgis.h:4367
Abstract base class for material settings.
virtual void writeXml(QDomElement &element, const QgsReadWriteContext &) const
Writes settings to a DOM element.
virtual void readXml(const QDomElement &element, const QgsReadWriteContext &)
Reads settings from a DOM element.
static QgsImageCache * imageCache()
Returns the application's image cache, used for caching resampled versions of raster images.
static QColor colorFromString(const QString &string)
Decodes a string into a color value.
static QString colorToString(const QColor &color)
Encodes a color into a string value.
QImage pathAsImage(const QString &path, const QSize size, const bool keepAspectRatio, const double opacity, bool &fitsInCache, bool blocking=false, double targetDpi=96, int frameNumber=-1, bool *isMissing=nullptr)
Returns the specified path rendered as an image.
QSet< QgsAbstractMaterialSettings::Property > supportedProperties() const override
Returns the set of data-defined properties supported by this material.
bool requiresTextureCoordinates() const override
Returns true if the material requires texture coordinates to be generated during triangulation.
static bool supportsTechnique(Qgis::MaterialRenderingTechnique technique)
Returns true if the specified technique is supported by the Phong material.
bool equals(const QgsAbstractMaterialSettings *other) const override
Returns true if this settings exactly matches an other settings.
QColor averageColor() const override
Returns an approximate color representing the blended material color.
void setColorsFromBase(const QColor &baseColor, float metallic)
Decompose an average color into Phong material components, and sets the material's colors accordingly...
void writeXml(QDomElement &elem, const QgsReadWriteContext &context) const override
Writes settings to a DOM element.
QString type() const override
Returns the unique type name for the material.
void readXml(const QDomElement &elem, const QgsReadWriteContext &context) override
Reads settings from a DOM element.
QgsPhongTexturedMaterialSettings * clone() const override
Clones the material settings.
static QgsAbstractMaterialSettings * create()
Returns a new instance of QgsPhongTexturedMaterialSettings.
A container for the context for various read/write operations on objects.
static QString encodePoint(QPointF point)
Encodes a QPointF to a string.
static QPointF decodePoint(const QString &string)
Decodes a QSizeF from a string.
bool qgsDoubleNear(double a, double b, double epsilon=4 *std::numeric_limits< double >::epsilon())
Compare two doubles (but allow some difference).
Definition qgis.h:7327