Skip to content
Permalink
Browse files

[FEATURE] Add support for shading of terrain

This adds new options for user to choose how the terrain should be rendered:
- shading disabled - color of terrain is determined only from map texture
- shading enabled - color of terrain is determined using Phong's shading model,
  taking into account map texture, terrain normal vector, scene light(s) and
  terrain material's ambient+specular colors and shininess

(configuration of lights is not there yet - now we always have a single point
light with white color at the middle of the scene at 1km above the zero elevation)
  • Loading branch information
wonder-sk committed Nov 11, 2018
1 parent cf24e65 commit d3e81f6d18dd229074efefbaabd209dcf46d448f
@@ -73,6 +73,8 @@ Reads settings from a DOM element
Writes settings to a DOM element
%End

bool operator==( const QgsPhongMaterialSettings &other ) const;

};


@@ -92,6 +92,7 @@ Qgs3DMapScene::Qgs3DMapScene( const Qgs3DMapSettings &map, QgsAbstract3DEngine *
connect( &map, &Qgs3DMapSettings::mapTileResolutionChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::maxTerrainScreenErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::maxTerrainGroundErrorChanged, this, &Qgs3DMapScene::createTerrain );
connect( &map, &Qgs3DMapSettings::terrainShadingChanged, this, &Qgs3DMapScene::createTerrain );

// create entities of renderers

@@ -37,6 +37,8 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other )
, mMapTileResolution( other.mMapTileResolution )
, mMaxTerrainScreenError( other.mMaxTerrainScreenError )
, mMaxTerrainGroundError( other.mMaxTerrainGroundError )
, mTerrainShadingEnabled( other.mTerrainShadingEnabled )
, mTerrainShadingMaterial( other.mTerrainShadingMaterial )
, mShowTerrainBoundingBoxes( other.mShowTerrainBoundingBoxes )
, mShowTerrainTileInfo( other.mShowTerrainTileInfo )
, mShowCameraViewCenter( other.mShowCameraViewCenter )
@@ -79,6 +81,10 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte
mMapTileResolution = elemTerrain.attribute( QStringLiteral( "texture-size" ), QStringLiteral( "512" ) ).toInt();
mMaxTerrainScreenError = elemTerrain.attribute( QStringLiteral( "max-terrain-error" ), QStringLiteral( "3" ) ).toFloat();
mMaxTerrainGroundError = elemTerrain.attribute( QStringLiteral( "max-ground-error" ), QStringLiteral( "1" ) ).toFloat();
mTerrainShadingEnabled = elemTerrain.attribute( QStringLiteral( "shading-enabled" ), QStringLiteral( "0" ) ).toInt();
QDomElement elemTerrainShadingMaterial = elemTerrain.firstChildElement( QStringLiteral( "shading-material" ) );
if ( !elemTerrainShadingMaterial.isNull() )
mTerrainShadingMaterial.readXml( elemTerrainShadingMaterial );
mShowLabels = elemTerrain.attribute( QStringLiteral( "show-labels" ), QStringLiteral( "0" ) ).toInt();
QDomElement elemMapLayers = elemTerrain.firstChildElement( QStringLiteral( "layers" ) );
QDomElement elemMapLayer = elemMapLayers.firstChildElement( QStringLiteral( "layer" ) );
@@ -169,6 +175,10 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon
elemTerrain.setAttribute( QStringLiteral( "texture-size" ), mMapTileResolution );
elemTerrain.setAttribute( QStringLiteral( "max-terrain-error" ), QString::number( mMaxTerrainScreenError ) );
elemTerrain.setAttribute( QStringLiteral( "max-ground-error" ), QString::number( mMaxTerrainGroundError ) );
elemTerrain.setAttribute( QStringLiteral( "shading-enabled" ), mTerrainShadingEnabled ? 1 : 0 );
QDomElement elemTerrainShadingMaterial = doc.createElement( QStringLiteral( "shading-material" ) );
mTerrainShadingMaterial.writeXml( elemTerrainShadingMaterial );
elemTerrain.appendChild( elemTerrainShadingMaterial );
elemTerrain.setAttribute( QStringLiteral( "show-labels" ), mShowLabels ? 1 : 0 );
QDomElement elemMapLayers = doc.createElement( QStringLiteral( "layers" ) );
Q_FOREACH ( const QgsMapLayerRef &layerRef, mLayers )
@@ -370,6 +380,24 @@ void Qgs3DMapSettings::setTerrainGenerator( QgsTerrainGenerator *gen )
emit terrainGeneratorChanged();
}

void Qgs3DMapSettings::setTerrainShadingEnabled( bool enabled )
{
if ( mTerrainShadingEnabled == enabled )
return;

mTerrainShadingEnabled = enabled;
emit terrainShadingChanged();
}

void Qgs3DMapSettings::setTerrainShadingMaterial( const QgsPhongMaterialSettings &material )
{
if ( mTerrainShadingMaterial == material )
return;

mTerrainShadingMaterial = material;
emit terrainShadingChanged();
}

void Qgs3DMapSettings::setRenderers( const QList<QgsAbstract3DRenderer *> &renderers )
{
mRenderers = renderers;
@@ -24,6 +24,7 @@

#include "qgscoordinatereferencesystem.h"
#include "qgsmaplayerref.h"
#include "qgsphongmaterialsettings.h"
#include "qgsterraingenerator.h"
#include "qgsvector3d.h"

@@ -200,6 +201,35 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
//! Returns terrain generator. It takes care of producing terrain tiles from the input data.
QgsTerrainGenerator *terrainGenerator() const { return mTerrainGenerator.get(); }

/**
* Sets whether terrain shading is enabled.
* \sa isTerrainShadingEnabled()
* \since QGIS 3.6
*/
void setTerrainShadingEnabled( bool enabled );

/**
* Returns whether terrain shading is enabled. When enabled, in addition to the terrain texture
* generated from the map, the terrain rendering will take into account position of the lights,
* terrain normals and terrain shading material (ambient and specular colors, shininess).
* \since QGIS 3.6
*/
bool isTerrainShadingEnabled() const { return mTerrainShadingEnabled; }

/**
* Sets terrain shading material.
* \sa terrainShadingMaterial()
* \since QGIS 3.6
*/
void setTerrainShadingMaterial( const QgsPhongMaterialSettings &material );

/**
* Returns terrain shading material. Diffuse color component is ignored since the diffuse component
* is provided by 2D rendered map texture. Only used when isTerrainShadingEnabled() is true.
* \since QGIS 3.6
*/
QgsPhongMaterialSettings terrainShadingMaterial() const { return mTerrainShadingMaterial; }

//! Sets list of extra 3D renderers to use in the scene. Takes ownership of the objects.
void setRenderers( const QList<QgsAbstract3DRenderer *> &renderers SIP_TRANSFER );
//! Returns list of extra 3D renderers
@@ -261,6 +291,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
void maxTerrainScreenErrorChanged();
//! Emitted when the maximum terrain ground error has changed
void maxTerrainGroundErrorChanged();

/**
* Emitted when terrain shading enabled flag or terrain shading material has changed
* \since QGIS 3.6
*/
void terrainShadingChanged();
//! Emitted when the flag whether terrain's bounding boxes are shown has changed
void showTerrainBoundingBoxesChanged();
//! Emitted when the flag whether terrain's tile info is shown has changed
@@ -285,6 +321,8 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject
int mMapTileResolution = 512; //!< Size of map textures of tiles in pixels (width/height)
float mMaxTerrainScreenError = 3.f; //!< Maximum allowed terrain error in pixels (determines when tiles are switched to more detailed ones)
float mMaxTerrainGroundError = 1.f; //!< Maximum allowed horizontal map error in map units (determines how many zoom levels will be used)
bool mTerrainShadingEnabled = false; //!< Whether terrain should be shaded taking lights into account
QgsPhongMaterialSettings mTerrainShadingMaterial; //!< Material to use for the terrain (if shading is enabled). Diffuse color is ignored.
bool mShowTerrainBoundingBoxes = false; //!< Whether to show bounding boxes of entities - useful for debugging
bool mShowTerrainTileInfo = false; //!< Whether to draw extra information about terrain tiles to the textures - useful for debugging
bool mShowCameraViewCenter = false; //!< Whether to show camera view center as a sphere - useful for debugging
@@ -65,6 +65,14 @@ class _3D_EXPORT QgsPhongMaterialSettings
//! Writes settings to a DOM element
void writeXml( QDomElement &elem ) const;

bool operator==( const QgsPhongMaterialSettings &other ) const
{
return mAmbient == other.mAmbient &&
mDiffuse == other.mDiffuse &&
mSpecular == other.mSpecular &&
mShininess == other.mShininess;
}

private:
QColor mAmbient;
QColor mDiffuse;
@@ -94,7 +94,7 @@ Qt3DCore::QEntity *QgsDemTerrainTileLoader::createEntity( Qt3DCore::QEntity *par

// create material

createTextureComponent( entity );
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial() );

// create transform

@@ -52,7 +52,8 @@ Qt3DCore::QEntity *FlatTerrainChunkLoader::createEntity( Qt3DCore::QEntity *pare

// create material

createTextureComponent( entity );
const Qgs3DMapSettings &map = terrain()->map3D();
createTextureComponent( entity, map.isTerrainShadingEnabled(), map.terrainShadingMaterial() );

// create transform

@@ -25,11 +25,8 @@

#include <Qt3DRender/QTexture>

#if QT_VERSION >= 0x050900
#include <Qt3DExtras/QTextureMaterial>
#else
#include <Qt3DExtras/QDiffuseMapMaterial>
#endif

#include "quantizedmeshterraingenerator.h"

@@ -67,23 +64,32 @@ void QgsTerrainTileLoader::loadTexture()
mTextureJobId = mTerrain->textureGenerator()->render( mExtentMapCrs, mTileDebugText );
}

void QgsTerrainTileLoader::createTextureComponent( QgsTerrainTileEntity *entity )
void QgsTerrainTileLoader::createTextureComponent( QgsTerrainTileEntity *entity, bool isShadingEnabled, const QgsPhongMaterialSettings &shadingMaterial )
{
Qt3DRender::QTexture2D *texture = new Qt3DRender::QTexture2D( entity );
QgsTerrainTextureImage *textureImage = new QgsTerrainTextureImage( mTextureImage, mExtentMapCrs, mTileDebugText );
texture->addTextureImage( textureImage );
texture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
texture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
Qt3DExtras::QTextureMaterial *material = nullptr;
#if QT_VERSION >= 0x050900
material = new Qt3DExtras::QTextureMaterial;
material->setTexture( texture );
#else
material = new Qt3DExtras::QDiffuseMapMaterial;
material->setDiffuse( texture );
material->setShininess( 1 );
material->setAmbient( Qt::white );
#endif

Qt3DRender::QMaterial *material = nullptr;
if ( isShadingEnabled )
{
Qt3DExtras::QDiffuseMapMaterial *diffuseMapMaterial;
diffuseMapMaterial = new Qt3DExtras::QDiffuseMapMaterial;
diffuseMapMaterial->setDiffuse( texture );
diffuseMapMaterial->setAmbient( shadingMaterial.ambient() );
diffuseMapMaterial->setSpecular( shadingMaterial.specular() );
diffuseMapMaterial->setShininess( shadingMaterial.shininess() );
material = diffuseMapMaterial;
}
else
{
Qt3DExtras::QTextureMaterial *textureMaterial = new Qt3DExtras::QTextureMaterial;
textureMaterial->setTexture( texture );
material = textureMaterial;
}

entity->setTextureImage( textureImage );
entity->addComponent( material ); // takes ownership if the component has no parent
}
@@ -32,6 +32,7 @@
#include <QImage>
#include "qgsrectangle.h"

class QgsPhongMaterialSettings;
class QgsTerrainEntity;
class QgsTerrainTileEntity;

@@ -54,7 +55,7 @@ class QgsTerrainTileLoader : public QgsChunkLoader
//! Starts asynchronous rendering of map texture
void loadTexture();
//! Creates material component for the entity with the rendered map as a texture
void createTextureComponent( QgsTerrainTileEntity *entity );
void createTextureComponent( QgsTerrainTileEntity *entity, bool isShadingEnabled, const QgsPhongMaterialSettings &shadingMaterial );
//! Gives access to the terain entity
QgsTerrainEntity *terrain() { return mTerrain; }

@@ -69,6 +69,10 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas
chkShowBoundingBoxes->setChecked( mMap->showTerrainBoundingBoxes() );
chkShowCameraViewCenter->setChecked( mMap->showCameraViewCenter() );

groupTerrainShading->setChecked( mMap->isTerrainShadingEnabled() );
widgetTerrainMaterial->setDiffuseVisible( false );
widgetTerrainMaterial->setMaterial( mMap->terrainShadingMaterial() );

connect( cboTerrainLayer, static_cast<void ( QComboBox::* )( int )>( &QgsMapLayerComboBox::currentIndexChanged ), this, &Qgs3DMapConfigWidget::onTerrainLayerChanged );
connect( spinMapResolution, static_cast<void ( QSpinBox::* )( int )>( &QSpinBox::valueChanged ), this, &Qgs3DMapConfigWidget::updateMaxZoomLevel );
connect( spinGroundError, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &Qgs3DMapConfigWidget::updateMaxZoomLevel );
@@ -134,6 +138,9 @@ void Qgs3DMapConfigWidget::apply()
mMap->setShowTerrainTilesInfo( chkShowTileInfo->isChecked() );
mMap->setShowTerrainBoundingBoxes( chkShowBoundingBoxes->isChecked() );
mMap->setShowCameraViewCenter( chkShowCameraViewCenter->isChecked() );

mMap->setTerrainShadingEnabled( groupTerrainShading->isChecked() );
mMap->setTerrainShadingMaterial( widgetTerrainMaterial->material() );
}

void Qgs3DMapConfigWidget::onTerrainLayerChanged()
@@ -31,6 +31,17 @@ QgsPhongMaterialWidget::QgsPhongMaterialWidget( QWidget *parent )
connect( spinShininess, static_cast<void ( QDoubleSpinBox::* )( double )>( &QDoubleSpinBox::valueChanged ), this, &QgsPhongMaterialWidget::changed );
}

void QgsPhongMaterialWidget::setDiffuseVisible( bool visible )
{
label->setVisible( visible );
btnDiffuse->setVisible( visible );
}

bool QgsPhongMaterialWidget::isDiffuseVisible() const
{
return btnDiffuse->isVisible();
}

void QgsPhongMaterialWidget::setMaterial( const QgsPhongMaterialSettings &material )
{
btnDiffuse->setColor( material.diffuse() );
@@ -30,6 +30,9 @@ class QgsPhongMaterialWidget : public QWidget, private Ui::PhongMaterialWidget
public:
explicit QgsPhongMaterialWidget( QWidget *parent = nullptr );

void setDiffuseVisible( bool visible );
bool isDiffuseVisible() const;

void setMaterial( const QgsPhongMaterialSettings &material );
QgsPhongMaterialSettings material() const;

@@ -7,7 +7,7 @@
<x>0</x>
<y>0</y>
<width>691</width>
<height>830</height>
<height>1135</height>
</rect>
</property>
<property name="windowTitle">
@@ -87,6 +87,21 @@
</layout>
</widget>
</item>
<item>
<widget class="QgsCollapsibleGroupBox" name="groupTerrainShading">
<property name="title">
<string>Terrain shading</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QgsPhongMaterialWidget" name="widgetTerrainMaterial" native="true"/>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout_2">
<item row="2" column="0">
@@ -224,6 +239,18 @@
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
<customwidget>
<class>QgsPhongMaterialWidget</class>
<extends>QWidget</extends>
<header>qgsphongmaterialwidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsCollapsibleGroupBox</class>
<extends>QGroupBox</extends>
<header>qgscollapsiblegroupbox.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>cboTerrainLayer</tabstop>

0 comments on commit d3e81f6

Please sign in to comment.
You can’t perform that action at this time.