mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-13 00:03:09 -04:00
[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)
This commit is contained in:
parent
cf24e654b8
commit
d3e81f6d18
@ -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>
|
||||
|
Loading…
x
Reference in New Issue
Block a user