diff --git a/python/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in b/python/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in index 6cacee4ea96..3c028fecb9b 100644 --- a/python/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in +++ b/python/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in @@ -72,6 +72,45 @@ Returns shininess of the surface Returns the opacity of the surface .. versionadded:: 3.26 +%End + + float ambientCoefficient() const; +%Docstring +Returns the coefficient for the ambient color contribution (ie strength factor of the ambient color). + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`specularCoefficient` + +.. versionadded:: 3.36 +%End + + float diffuseCoefficient() const; +%Docstring +Returns the coefficient for the diffuse color contribution (ie strength factor of the diffuse color). + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`ambientCoefficient` + +.. seealso:: :py:func:`specularCoefficient` + +.. versionadded:: 3.36 +%End + + float specularCoefficient() const; +%Docstring +Returns the coefficient for the specular color contribution (ie strength factor of the specular color). + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`ambientCoefficient` + +.. versionadded:: 3.36 %End virtual QMap toExportParameters() const; @@ -101,7 +140,44 @@ Sets opacity of the surface .. versionadded:: 3.26 %End + void setAmbientCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the ambient color contribution (ie strength factor of the ambient color). +.. seealso:: :py:func:`ambientCoefficient` + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. versionadded:: 3.36 +%End + + void setDiffuseCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the diffuse color contribution (ie strength factor of the diffuse color). + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. versionadded:: 3.36 +%End + + void setSpecularCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the specular color contribution (ie strength factor of the specular color). + +.. seealso:: :py:func:`specularCoefficient` + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. versionadded:: 3.36 +%End virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ); diff --git a/python/PyQt6/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in b/python/PyQt6/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in index 6cacee4ea96..3c028fecb9b 100644 --- a/python/PyQt6/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in +++ b/python/PyQt6/3d/auto_generated/materials/qgsphongmaterialsettings.sip.in @@ -72,6 +72,45 @@ Returns shininess of the surface Returns the opacity of the surface .. versionadded:: 3.26 +%End + + float ambientCoefficient() const; +%Docstring +Returns the coefficient for the ambient color contribution (ie strength factor of the ambient color). + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`specularCoefficient` + +.. versionadded:: 3.36 +%End + + float diffuseCoefficient() const; +%Docstring +Returns the coefficient for the diffuse color contribution (ie strength factor of the diffuse color). + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`ambientCoefficient` + +.. seealso:: :py:func:`specularCoefficient` + +.. versionadded:: 3.36 +%End + + float specularCoefficient() const; +%Docstring +Returns the coefficient for the specular color contribution (ie strength factor of the specular color). + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`ambientCoefficient` + +.. versionadded:: 3.36 %End virtual QMap toExportParameters() const; @@ -101,7 +140,44 @@ Sets opacity of the surface .. versionadded:: 3.26 %End + void setAmbientCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the ambient color contribution (ie strength factor of the ambient color). +.. seealso:: :py:func:`ambientCoefficient` + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. versionadded:: 3.36 +%End + + void setDiffuseCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the diffuse color contribution (ie strength factor of the diffuse color). + +.. seealso:: :py:func:`diffuseCoefficient` + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. seealso:: :py:func:`setSpecularCoefficient` + +.. versionadded:: 3.36 +%End + + void setSpecularCoefficient( float coefficient ); +%Docstring +Sets the ``coefficient`` for the specular color contribution (ie strength factor of the specular color). + +.. seealso:: :py:func:`specularCoefficient` + +.. seealso:: :py:func:`setDiffuseCoefficient` + +.. seealso:: :py:func:`setAmbientCoefficient` + +.. versionadded:: 3.36 +%End virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context ); diff --git a/src/3d/materials/qgsphongmaterialsettings.cpp b/src/3d/materials/qgsphongmaterialsettings.cpp index 22d9192184a..7b8f18a9527 100644 --- a/src/3d/materials/qgsphongmaterialsettings.cpp +++ b/src/3d/materials/qgsphongmaterialsettings.cpp @@ -83,6 +83,9 @@ void QgsPhongMaterialSettings::readXml( const QDomElement &elem, const QgsReadWr mSpecular = QgsColorUtils::colorFromString( elem.attribute( QStringLiteral( "specular" ), QStringLiteral( "255,255,255" ) ) ); mShininess = elem.attribute( QStringLiteral( "shininess" ) ).toFloat(); mOpacity = elem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1.0" ) ).toFloat(); + mAmbientCoefficient = elem.attribute( QStringLiteral( "ka" ), QStringLiteral( "1.0" ) ).toFloat(); + mDiffuseCoefficient = elem.attribute( QStringLiteral( "kd" ), QStringLiteral( "1.0" ) ).toFloat(); + mSpecularCoefficient = elem.attribute( QStringLiteral( "ks" ), QStringLiteral( "1.0" ) ).toFloat(); QgsAbstractMaterialSettings::readXml( elem, context ); } @@ -94,6 +97,9 @@ void QgsPhongMaterialSettings::writeXml( QDomElement &elem, const QgsReadWriteCo elem.setAttribute( QStringLiteral( "specular" ), QgsColorUtils::colorToString( mSpecular ) ); elem.setAttribute( QStringLiteral( "shininess" ), mShininess ); elem.setAttribute( QStringLiteral( "opacity" ), mOpacity ); + elem.setAttribute( QStringLiteral( "ka" ), mAmbientCoefficient ); + elem.setAttribute( QStringLiteral( "kd" ), mDiffuseCoefficient ); + elem.setAttribute( QStringLiteral( "ks" ), mSpecularCoefficient ); QgsAbstractMaterialSettings::writeXml( elem, context ); } @@ -139,12 +145,18 @@ void QgsPhongMaterialSettings::addParametersToEffect( Qt3DRender::QEffect *effec Qt3DRender::QParameter *specularParameter = new Qt3DRender::QParameter( QStringLiteral( "specularColor" ), mSpecular ); Qt3DRender::QParameter *shininessParameter = new Qt3DRender::QParameter( QStringLiteral( "shininess" ), mShininess ); Qt3DRender::QParameter *opacityParameter = new Qt3DRender::QParameter( QStringLiteral( "opacity" ), mOpacity ); + Qt3DRender::QParameter *kaParameter = new Qt3DRender::QParameter( QStringLiteral( "ka" ), mAmbientCoefficient ); + Qt3DRender::QParameter *kdParameter = new Qt3DRender::QParameter( QStringLiteral( "kd" ), mDiffuseCoefficient ); + Qt3DRender::QParameter *ksParameter = new Qt3DRender::QParameter( QStringLiteral( "ks" ), mSpecularCoefficient ); effect->addParameter( ambientParameter ); effect->addParameter( diffuseParameter ); effect->addParameter( specularParameter ); effect->addParameter( shininessParameter ); effect->addParameter( opacityParameter ); + effect->addParameter( kaParameter ); + effect->addParameter( kdParameter ); + effect->addParameter( ksParameter ); } QByteArray QgsPhongMaterialSettings::dataDefinedVertexColorsAsByte( const QgsExpressionContext &expressionContext ) const @@ -247,6 +259,9 @@ Qt3DRender::QMaterial *QgsPhongMaterialSettings::constantColorMaterial( const Qg eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ambientColor" ), context.isSelected() ? context.selectionColor().darker() : mAmbient ) ); eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "diffuseColor" ), context.isSelected() ? context.selectionColor() : mDiffuse ) ); eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "specularColor" ), mSpecular ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ka" ), mAmbientCoefficient ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "kd" ), mDiffuseCoefficient ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ks" ), mSpecularCoefficient ) ); if ( mOpacity < 1.0f ) { @@ -300,6 +315,9 @@ Qt3DRender::QMaterial *QgsPhongMaterialSettings::dataDefinedMaterial() const eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "shininess" ), mShininess ) ); eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "opacity" ), mOpacity ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ka" ), mAmbientCoefficient ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "kd" ), mDiffuseCoefficient ) ); + eff->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ks" ), mSpecularCoefficient ) ); if ( mOpacity < 1.0f ) { diff --git a/src/3d/materials/qgsphongmaterialsettings.h b/src/3d/materials/qgsphongmaterialsettings.h index 43192700e83..7242fbc6db7 100644 --- a/src/3d/materials/qgsphongmaterialsettings.h +++ b/src/3d/materials/qgsphongmaterialsettings.h @@ -71,6 +71,39 @@ class _3D_EXPORT QgsPhongMaterialSettings : public QgsAbstractMaterialSettings */ float opacity() const { return mOpacity; } + /** + * Returns the coefficient for the ambient color contribution (ie strength factor of the ambient color). + * + * \see setAmbientCoefficient() + * \see diffuseCoefficient() + * \see specularCoefficient() + * + * \since QGIS 3.36 + */ + float ambientCoefficient() const { return mAmbientCoefficient; } + + /** + * Returns the coefficient for the diffuse color contribution (ie strength factor of the diffuse color). + * + * \see setDiffuseCoefficient() + * \see ambientCoefficient() + * \see specularCoefficient() + * + * \since QGIS 3.36 + */ + float diffuseCoefficient() const { return mDiffuseCoefficient; } + + /** + * Returns the coefficient for the specular color contribution (ie strength factor of the specular color). + * + * \see setSpecularCoefficient() + * \see diffuseCoefficient() + * \see ambientCoefficient() + * + * \since QGIS 3.36 + */ + float specularCoefficient() const { return mSpecularCoefficient; } + QMap toExportParameters() const override; //! Sets ambient color component @@ -88,7 +121,38 @@ class _3D_EXPORT QgsPhongMaterialSettings : public QgsAbstractMaterialSettings */ void setOpacity( float opacity ) { mOpacity = opacity; } + /** + * Sets the \a coefficient for the ambient color contribution (ie strength factor of the ambient color). + * + * \see ambientCoefficient() + * \see setDiffuseCoefficient() + * \see setSpecularCoefficient() + * + * \since QGIS 3.36 + */ + void setAmbientCoefficient( float coefficient ) { mAmbientCoefficient = coefficient; } + /** + * Sets the \a coefficient for the diffuse color contribution (ie strength factor of the diffuse color). + * + * \see diffuseCoefficient() + * \see setAmbientCoefficient() + * \see setSpecularCoefficient() + * + * \since QGIS 3.36 + */ + void setDiffuseCoefficient( float coefficient ) { mDiffuseCoefficient = coefficient; } + + /** + * Sets the \a coefficient for the specular color contribution (ie strength factor of the specular color). + * + * \see specularCoefficient() + * \see setDiffuseCoefficient() + * \see setAmbientCoefficient() + * + * \since QGIS 3.36 + */ + void setSpecularCoefficient( float coefficient ) { mSpecularCoefficient = coefficient; } void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override; void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override; @@ -113,7 +177,10 @@ class _3D_EXPORT QgsPhongMaterialSettings : public QgsAbstractMaterialSettings mDiffuse == other.mDiffuse && mOpacity == other.mOpacity && mSpecular == other.mSpecular && - mShininess == other.mShininess; + mShininess == other.mShininess && + mAmbientCoefficient == other.mAmbientCoefficient && + mDiffuseCoefficient == other.mDiffuseCoefficient && + mSpecularCoefficient == other.mSpecularCoefficient; } private: @@ -121,6 +188,11 @@ class _3D_EXPORT QgsPhongMaterialSettings : public QgsAbstractMaterialSettings QColor mDiffuse{ QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) }; QColor mSpecular{ QColor::fromRgbF( 1.0f, 1.0f, 1.0f, 1.0f ) }; float mShininess = 0.0f; + + float mAmbientCoefficient = 1.0f; + float mDiffuseCoefficient = 1.0f; + float mSpecularCoefficient = 1.0f; + float mOpacity = 1.0f; //! Constructs a material from shader files diff --git a/src/3d/shaders/diffuseSpecular.frag b/src/3d/shaders/diffuseSpecular.frag index a21c9f108d6..e990d19b4ac 100644 --- a/src/3d/shaders/diffuseSpecular.frag +++ b/src/3d/shaders/diffuseSpecular.frag @@ -21,5 +21,6 @@ void main(void) vec3 worldView = normalize(eyePosition - worldPosition); fragColor = phongFunction(ambientColor, diffuseTextureColor, specularColor, shininess, + 1.0, 1.0, 1.0, worldPosition, worldView, worldNormal); } diff --git a/src/3d/shaders/phong.inc.frag b/src/3d/shaders/phong.inc.frag index 40536b0dd73..23629cd417b 100644 --- a/src/3d/shaders/phong.inc.frag +++ b/src/3d/shaders/phong.inc.frag @@ -6,6 +6,9 @@ vec4 phongFunction(const in vec4 ambient, const in vec4 diffuse, const in vec4 specular, const in float shin, + const in float ka, + const in float kd, + const in float ks, const in vec3 worldPosition, const in vec3 worldView, const in vec3 worldNormal) @@ -15,9 +18,9 @@ vec4 phongFunction(const in vec4 ambient, adsModel(worldPosition, worldNormal, worldView, shin, diffuseColor, specularColor); // Combine spec with ambient+diffuse for final fragment color - vec3 color = ambient.rgb - + diffuseColor * diffuse.rgb - + specularColor * specular.rgb; + vec3 color = ka * ambient.rgb + + kd * diffuseColor * diffuse.rgb + + ks * specularColor * specular.rgb; return vec4(color, diffuse.a); } diff --git a/src/3d/shaders/phongConstant.frag b/src/3d/shaders/phongConstant.frag index 49f4ee3aa43..96475d01e33 100644 --- a/src/3d/shaders/phongConstant.frag +++ b/src/3d/shaders/phongConstant.frag @@ -7,6 +7,9 @@ in vec3 worldNormal; uniform float shininess; uniform float opacity; +uniform float ka; +uniform float kd; +uniform float ks; uniform vec3 ambientColor; uniform vec3 diffuseColor; uniform vec3 specularColor; @@ -23,6 +26,7 @@ void main(void) vec4(diffuseColor, opacity), vec4(specularColor, opacity), shininess, + ka, kd, ks, worldPosition, worldView, worldNormal); diff --git a/src/3d/shaders/phongDataDefined.frag b/src/3d/shaders/phongDataDefined.frag index af2b6395d25..43719f58f94 100644 --- a/src/3d/shaders/phongDataDefined.frag +++ b/src/3d/shaders/phongDataDefined.frag @@ -2,6 +2,9 @@ uniform vec3 eyePosition; uniform float shininess; +uniform float ka; +uniform float kd; +uniform float ks; in vec3 worldPosition; in vec3 worldNormal; @@ -19,5 +22,13 @@ out vec4 fragColor; void main(void) { vec3 worldView = normalize(eyePosition - worldPosition); - fragColor = phongFunction(vs_in.ambient,vs_in.diffuse,vs_in.specular, shininess, worldPosition, worldView, worldNormal); + fragColor = phongFunction( + vs_in.ambient, + vs_in.diffuse, + vs_in.specular, + shininess, + ka, kd, ks, + worldPosition, + worldView, + worldNormal); } diff --git a/tests/src/3d/testqgs3drendering.cpp b/tests/src/3d/testqgs3drendering.cpp index 334b30053e7..2428bbdb93e 100644 --- a/tests/src/3d/testqgs3drendering.cpp +++ b/tests/src/3d/testqgs3drendering.cpp @@ -75,6 +75,7 @@ class TestQgs3DRendering : public QgsTest void testTerrainShading(); void testEpsg4978LineRendering(); void testExtrudedPolygons(); + void testPhongShading(); void testExtrudedPolygonsDataDefined(); void testExtrudedPolygonsGoochShading(); void testExtrudedPolygonsMetalRoughShading(); @@ -452,6 +453,55 @@ void TestQgs3DRendering::testExtrudedPolygons() QGSVERIFYIMAGECHECK( "polygon3d_extrusion_opacity", "polygon3d_extrusion_opacity", img2, QString(), 40, QSize( 0, 0 ), 2 ); } +void TestQgs3DRendering::testPhongShading() +{ + const QgsRectangle fullExtent = mLayerDtm->extent(); + + std::unique_ptr< QgsVectorLayer > buildings = std::make_unique< QgsVectorLayer >( testDataPath( "/3d/buildings.shp" ), "buildings", "ogr" ); + QVERIFY( buildings->isValid() ); + + QgsPhongMaterialSettings materialSettings; + materialSettings.setAmbient( QColor( 0, 100, 0 ) ); + materialSettings.setDiffuse( QColor( 255, 0, 255 ) ); + materialSettings.setSpecular( QColor( 0, 255, 255 ) ); + materialSettings.setShininess( 2 ); + materialSettings.setAmbientCoefficient( 0.3 ); + materialSettings.setDiffuseCoefficient( 0.8 ); + materialSettings.setSpecularCoefficient( 0.7 ); + QgsPolygon3DSymbol *symbol3d = new QgsPolygon3DSymbol; + symbol3d->setMaterialSettings( materialSettings.clone() ); + symbol3d->setExtrusionHeight( 10.f ); + QgsVectorLayer3DRenderer *renderer3d = new QgsVectorLayer3DRenderer( symbol3d ); + buildings->setRenderer3D( renderer3d ); + + Qgs3DMapSettings *map = new Qgs3DMapSettings; + map->setCrs( mProject->crs() ); + map->setExtent( fullExtent ); + map->setLayers( QList() << buildings.get() ); + QgsPointLightSettings defaultLight; + defaultLight.setIntensity( 0.5 ); + defaultLight.setPosition( QgsVector3D( 0, 1000, 0 ) ); + map->setLightSources( {defaultLight.clone() } ); + + QgsFlatTerrainGenerator *flatTerrain = new QgsFlatTerrainGenerator; + flatTerrain->setCrs( map->crs() ); + map->setTerrainGenerator( flatTerrain ); + + QgsOffscreen3DEngine engine; + Qgs3DMapScene *scene = new Qgs3DMapScene( *map, &engine ); + engine.setRootEntity( scene ); + + scene->cameraController()->setLookingAtPoint( QgsVector3D( 0, 0, 250 ), 300, 45, 0 ); + + // When running the test on Travis, it would initially return empty rendered image. + // Capturing the initial image and throwing it away fixes that. Hopefully we will + // find a better fix in the future. + Qgs3DUtils::captureSceneImage( engine, scene ); + QImage img = Qgs3DUtils::captureSceneImage( engine, scene ); + + QGSVERIFYIMAGECHECK( "phong_shading", "phong_shading", img, QString(), 40, QSize( 0, 0 ), 2 ); +} + void TestQgs3DRendering::testExtrudedPolygonsDataDefined() { QgsPropertyCollection propertyColection; diff --git a/tests/testdata/control_images/3d/expected_phong_shading/expected_phong_shading.png b/tests/testdata/control_images/3d/expected_phong_shading/expected_phong_shading.png new file mode 100644 index 00000000000..df6a3d91339 Binary files /dev/null and b/tests/testdata/control_images/3d/expected_phong_shading/expected_phong_shading.png differ