qgsphongtexturedmaterialsettings: Add support for opacity

Qt3DRender::QTexture2D does not handle opacity. Therefore, it is not
possible to use the default Qt3DExtras::QDiffuseSpecularMaterial
implementation. This problem is solved by using
Qt3DRender::QMaterial and copying the
Qt3DExtras::QDiffuseSpecularMaterial shaders from Qt3D source
code. Then, the texture color needs to be changed to set the correct
opacity. This is achieved in the fragement shader:

```
vec4 diffuseTextureColor = vec4(texture(diffuseTexture, texCoord).rgb,
opacity);
```

instead of the default:

```
vec4 diffuseTextureColor = vec4(texture(diffuseTexture, texCoord));
```

As far as the FrameGraph is concerned, this is already handled in
Qgs3DMapScene::finalizeNewEntity which checks for all material which
have an effect with an opacity parameter.
This commit is contained in:
Jean Felder 2022-07-07 21:12:23 +02:00 committed by Martin Dobias
parent 20325a565b
commit b69feb0817
7 changed files with 129 additions and 19 deletions

View File

@ -88,6 +88,13 @@ during triangulation.quiresTextureCoordinates
float textureRotation() const;
%Docstring
Returns the texture rotation, in degrees.
%End
float opacity() const;
%Docstring
Returns the opacity of the surface
.. versionadded:: 3.28
%End
void setAmbient( const QColor &ambient );
@ -121,6 +128,13 @@ If the texture scale is less than 1 the texture will be stretched
void setTextureRotation( float rotation );
%Docstring
Sets the texture rotation in degrees
%End
void setOpacity( float opacity );
%Docstring
Sets opacity of the surface.
.. versionadded:: 3.28
%End
virtual void readXml( const QDomElement &elem, const QgsReadWriteContext &context );

View File

@ -20,11 +20,12 @@
#include "qgsimagecache.h"
#include "qgsimagetexture.h"
#include "qgsphongmaterialsettings.h"
#include <Qt3DExtras/QDiffuseSpecularMaterial>
#include <Qt3DRender/QPaintedTextureImage>
#include <Qt3DRender/QTexture>
#include <Qt3DRender/QParameter>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QTechnique>
#include <Qt3DRender/QGraphicsApiFilter>
#include <QMap>
@ -71,6 +72,7 @@ void QgsPhongTexturedMaterialSettings::readXml( const QDomElement &elem, const Q
mAmbient = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "ambient" ), QStringLiteral( "25,25,25" ) ) );
mSpecular = QgsSymbolLayerUtils::decodeColor( elem.attribute( QStringLiteral( "specular" ), QStringLiteral( "255,255,255" ) ) );
mShininess = elem.attribute( QStringLiteral( "shininess" ) ).toFloat();
mOpacity = elem.attribute( QStringLiteral( "opacity" ), QStringLiteral( "1.0" ) ).toFloat();
mDiffuseTexturePath = elem.attribute( QStringLiteral( "diffuse_texture_path" ), QString() );
mTextureScale = elem.attribute( QStringLiteral( "texture_scale" ), QString( "1.0" ) ).toFloat();
mTextureRotation = elem.attribute( QStringLiteral( "texture-rotation" ), QString( "0.0" ) ).toFloat();
@ -83,6 +85,7 @@ void QgsPhongTexturedMaterialSettings::writeXml( QDomElement &elem, const QgsRea
elem.setAttribute( QStringLiteral( "ambient" ), QgsSymbolLayerUtils::encodeColor( mAmbient ) );
elem.setAttribute( QStringLiteral( "specular" ), QgsSymbolLayerUtils::encodeColor( mSpecular ) );
elem.setAttribute( QStringLiteral( "shininess" ), mShininess );
elem.setAttribute( QStringLiteral( "opacity" ), mOpacity );
elem.setAttribute( QStringLiteral( "diffuse_texture_path" ), mDiffuseTexturePath );
elem.setAttribute( QStringLiteral( "texture_scale" ), mTextureScale );
elem.setAttribute( QStringLiteral( "texture-rotation" ), mTextureRotation );
@ -113,16 +116,50 @@ Qt3DRender::QMaterial *QgsPhongTexturedMaterialSettings::toMaterial( QgsMaterial
QgsPhongMaterialSettings phongSettings = QgsPhongMaterialSettings();
phongSettings.setAmbient( mAmbient );
phongSettings.setDiffuse( QColor::fromRgbF( 0.7f, 0.7f, 0.7f, 1.0f ) ); // default diffuse color from QDiffuseSpecularMaterial
phongSettings.setOpacity( 1.0 ); // QgsPhongTexturedMaterialSettings does not handle opacity
phongSettings.setOpacity( mOpacity );
phongSettings.setShininess( mShininess );
phongSettings.setSpecular( mSpecular );
Qt3DRender::QMaterial *material = phongSettings.toMaterial( technique, context );
return material;
}
QgsImageTexture *textureImage = new QgsImageTexture( textureSourceImage );
Qt3DExtras::QDiffuseSpecularMaterial *material = new Qt3DExtras::QDiffuseSpecularMaterial;
// Use a custom material because Qt3DRender::QTexture2D does not handle opacity.
Qt3DRender::QMaterial *material = new Qt3DRender::QMaterial;
Qt3DRender::QEffect *effect = new Qt3DRender::QEffect( material );
Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique;
technique->graphicsApiFilter()->setApi( Qt3DRender::QGraphicsApiFilter::OpenGL );
technique->graphicsApiFilter()->setProfile( Qt3DRender::QGraphicsApiFilter::CoreProfile );
technique->graphicsApiFilter()->setMajorVersion( 3 );
technique->graphicsApiFilter()->setMinorVersion( 3 );
Qt3DRender::QFilterKey *filterKey = new Qt3DRender::QFilterKey();
filterKey->setName( QStringLiteral( "renderingStyle" ) );
filterKey->setValue( QStringLiteral( "forward" ) );
technique->addFilterKey( filterKey );
Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass();
Qt3DRender::QShaderProgram *shaderProgram = new Qt3DRender::QShaderProgram();
//Load shader programs
const QUrl urlVert( QStringLiteral( "qrc:/shaders/diffuseSpecular.vert" ) );
shaderProgram->setShaderCode( Qt3DRender::QShaderProgram::Vertex, Qt3DRender::QShaderProgram::loadSource( urlVert ) );
const QUrl urlFrag( QStringLiteral( "qrc:/shaders/diffuseSpecular.frag" ) );
shaderProgram->setShaderCode( Qt3DRender::QShaderProgram::Fragment, Qt3DRender::QShaderProgram::loadSource( urlFrag ) );
renderPass->setShaderProgram( shaderProgram );
technique->addRenderPass( renderPass );
int opacity = mOpacity * 255;
QColor ambient = context.isSelected() ? context.selectionColor().darker() : mAmbient;
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ka" ), QColor( ambient.red(), ambient.green(), ambient.blue(), opacity ) ) );
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "ks" ), QColor( mSpecular.red(), mSpecular.green(), mSpecular.blue(), opacity ) ) );
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "shininess" ), mShininess ) );
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "opacity" ), mOpacity ) );
// TODO : if ( context.isSelected() ) dampen the color of diffuse texture
// with context.map().selectionColor()
QgsImageTexture *textureImage = new QgsImageTexture( textureSourceImage );
Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D();
texture->addTextureImage( textureImage );
@ -136,21 +173,11 @@ Qt3DRender::QMaterial *QgsPhongTexturedMaterialSettings::toMaterial( QgsMaterial
texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
material->setDiffuse( QVariant::fromValue( texture ) );
material->setSpecular( mSpecular );
material->setAmbient( mAmbient );
material->setShininess( mShininess );
material->setTextureScale( mTextureScale );
if ( context.isSelected() )
{
// update the material with selection colors
// TODO : dampen the color of diffuse texture
// mat->setDiffuse( context.map().selectionColor() );
material->setAmbient( context.selectionColor().darker() );
}
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "diffuseTexture" ), QVariant::fromValue( texture ) ) );
effect->addParameter( new Qt3DRender::QParameter( QStringLiteral( "texCoordScale" ), mTextureScale ) );
effect->addTechnique( technique );
material->setEffect( effect );
return material;
}

View File

@ -89,6 +89,12 @@ class _3D_EXPORT QgsPhongTexturedMaterialSettings : public QgsAbstractMaterialSe
*/
float textureRotation() const;
/**
* Returns the opacity of the surface
* \since QGIS 3.28
*/
float opacity() const { return mOpacity; }
//! Sets ambient color component
void setAmbient( const QColor &ambient ) { mAmbient = ambient; }
@ -113,6 +119,12 @@ class _3D_EXPORT QgsPhongTexturedMaterialSettings : public QgsAbstractMaterialSe
//! Sets the texture rotation in degrees
void setTextureRotation( float rotation ) { mTextureRotation = rotation; }
/**
* Sets opacity of the surface.
* \since QGIS 3.28
*/
void setOpacity( float opacity ) { mOpacity = opacity; }
void readXml( const QDomElement &elem, const QgsReadWriteContext &context ) override;
void writeXml( QDomElement &elem, const QgsReadWriteContext &context ) const override;
#ifndef SIP_RUN
@ -126,6 +138,7 @@ class _3D_EXPORT QgsPhongTexturedMaterialSettings : public QgsAbstractMaterialSe
return mAmbient == other.mAmbient &&
mSpecular == other.mSpecular &&
mShininess == other.mShininess &&
mOpacity == other.mOpacity &&
mDiffuseTexturePath == other.mDiffuseTexturePath &&
mTextureScale == other.mTextureScale &&
mTextureRotation == other.mTextureRotation;
@ -135,6 +148,7 @@ class _3D_EXPORT QgsPhongTexturedMaterialSettings : public QgsAbstractMaterialSe
QColor mAmbient{ QColor::fromRgbF( 0.1f, 0.1f, 0.1f, 1.0f ) };
QColor mSpecular{ QColor::fromRgbF( 1.0f, 1.0f, 1.0f, 1.0f ) };
float mShininess = 0.0f;
float mOpacity = 1.0f;
QString mDiffuseTexturePath;
float mTextureScale{ 1.0f };
float mTextureRotation{ 0.0f };

View File

@ -943,7 +943,7 @@ void Qgs3DMapScene::finalizeNewEntity( Qt3DCore::QEntity *newEntity )
}
else
{
// This handles the phong material with data defined properties.
// This handles the phong material with data defined properties and the textured case.
Qt3DRender::QEffect *effect = material->effect();
if ( effect )
{

View File

@ -1,5 +1,7 @@
<RCC>
<qresource prefix="/">
<file>shaders/diffuseSpecular.frag</file>
<file>shaders/diffuseSpecular.vert</file>
<file>shaders/instanced.frag</file>
<file>shaders/instanced.vert</file>
<file>shaders/light.inc.frag</file>

View File

@ -0,0 +1,23 @@
#version 330
in vec3 worldPosition;
in vec3 worldNormal;
in vec2 texCoord;
uniform vec3 eyePosition;
uniform vec4 ka;
uniform vec4 ks;
uniform float shininess;
uniform sampler2D diffuseTexture;
uniform float opacity;
out vec4 fragColor;
#pragma include phong.inc.frag
void main(void)
{
vec4 diffuseTextureColor = vec4(texture(diffuseTexture, texCoord).rgb, opacity);
vec3 worldView = normalize(eyePosition - worldPosition);
fragColor = phongFunction(ka, diffuseTextureColor, ks, shininess, worldPosition, worldView, worldNormal);
}

View File

@ -0,0 +1,30 @@
// copied from qt3d/src/extras/shaders/gl3/default.vert
#version 330
in vec3 vertexPosition;
in vec3 vertexNormal;
in vec2 vertexTexCoord;
out vec3 worldPosition;
out vec3 worldNormal;
out vec2 texCoord;
uniform mat4 modelMatrix;
uniform mat3 modelNormalMatrix;
uniform mat4 modelViewProjection;
uniform float texCoordScale;
void main(void)
{
// Pass through scaled texture coordinates
texCoord = vertexTexCoord * texCoordScale;
// Transform position and normal to world space
worldPosition = vec3(modelMatrix * vec4(vertexPosition, 1.0));
worldNormal = normalize(modelNormalMatrix * vertexNormal);
// Calculate vertex position in clip coordinates
gl_Position = modelViewProjection * vec4(vertexPosition, 1.0);
}