diff --git a/python/3d/auto_generated/qgs3dmapsettings.sip.in b/python/3d/auto_generated/qgs3dmapsettings.sip.in index 7a22aec052a..3be131bcb28 100644 --- a/python/3d/auto_generated/qgs3dmapsettings.sip.in +++ b/python/3d/auto_generated/qgs3dmapsettings.sip.in @@ -542,6 +542,8 @@ Default value is 96 + + bool isSkyboxEnabled() const; %Docstring Returns whether the skybox is enabled. @@ -874,6 +876,14 @@ Emitted when shadow rendering settings are changed .. versionadded:: 3.16 %End + + void ambientOcclusionSettingsChanged(); +%Docstring +Emitted when ambient occlusion rendering settings are changed + +.. versionadded:: 3.28 +%End + void fpsCounterEnabledChanged( bool fpsCounterEnabled ); %Docstring Emitted when the FPS counter is enabled or disabled diff --git a/src/3d/CMakeLists.txt b/src/3d/CMakeLists.txt index e41afc78e89..8d897791bfb 100644 --- a/src/3d/CMakeLists.txt +++ b/src/3d/CMakeLists.txt @@ -34,10 +34,14 @@ set(QGIS_3D_SRCS qgsskyboxsettings.cpp qgsshadowrenderingframegraph.cpp qgspostprocessingentity.cpp + qgsrenderpassquad.cpp + qgsambientocclusionrenderentity.cpp + qgsambientocclusionblurentity.cpp qgspreviewquad.cpp qgsshadowsettings.cpp qgscolorramptexture.cpp qgsrubberband3d.cpp + qgsambientocclusionsettings.cpp qgspointcloudlayer3drenderer.cpp qgspointcloudlayerchunkloader_p.cpp @@ -130,6 +134,10 @@ set(QGIS_3D_HDRS qgspreviewquad.h qgsshadowsettings.h qgspointcloudlayer3drenderer.h + qgsrenderpassquad.h + qgsambientocclusionrenderentity.h + qgsambientocclusionblurentity.h + qgsambientocclusionsettings.h lights/qgsdirectionallightsettings.h lights/qgslightsource.h diff --git a/src/3d/qgs3dmapscene.cpp b/src/3d/qgs3dmapscene.cpp index 9909a7671b8..b9d64567764 100644 --- a/src/3d/qgs3dmapscene.cpp +++ b/src/3d/qgs3dmapscene.cpp @@ -138,6 +138,7 @@ Qgs3DMapScene::Qgs3DMapScene( Qgs3DMapSettings &map, QgsAbstract3DEngine *engine connect( &map, &Qgs3DMapSettings::renderersChanged, this, &Qgs3DMapScene::onRenderersChanged ); connect( &map, &Qgs3DMapSettings::skyboxSettingsChanged, this, &Qgs3DMapScene::onSkyboxSettingsChanged ); connect( &map, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapScene::onShadowSettingsChanged ); + connect( &map, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapScene::onAmbientOcclusionSettingsChanged ); connect( &map, &Qgs3DMapSettings::eyeDomeLightingEnabledChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged ); connect( &map, &Qgs3DMapSettings::eyeDomeLightingStrengthChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged ); connect( &map, &Qgs3DMapSettings::eyeDomeLightingDistanceChanged, this, &Qgs3DMapScene::onEyeDomeShadingSettingsChanged ); @@ -237,6 +238,8 @@ Qgs3DMapScene::Qgs3DMapScene( Qgs3DMapSettings &map, QgsAbstract3DEngine *engine // force initial update of debugging setting of preview quads onDebugShadowMapSettingsChanged(); onDebugDepthMapSettingsChanged(); + // force initial update of ambient occlusion settings + onAmbientOcclusionSettingsChanged(); mCameraController->setCameraNavigationMode( mMap.cameraNavigationMode() ); onCameraMovementSpeedChanged(); @@ -1102,6 +1105,16 @@ void Qgs3DMapScene::onShadowSettingsChanged() shadowRenderingFrameGraph->setShadowRenderingEnabled( false ); } +void Qgs3DMapScene::onAmbientOcclusionSettingsChanged() +{ + QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph(); + QgsAmbientOcclusionSettings ambientOcclusionSettings = mMap.ambientOcclusionSettings(); + shadowRenderingFrameGraph->setAmbientOcclusionEnabled( ambientOcclusionSettings.isEnabled() ); + shadowRenderingFrameGraph->setAmbientOcclusionRadius( ambientOcclusionSettings.radius() ); + shadowRenderingFrameGraph->setAmbientOcclusionIntensity( ambientOcclusionSettings.intensity() ); + shadowRenderingFrameGraph->setAmbientOcclusionThreshold( ambientOcclusionSettings.threshold() ); +} + void Qgs3DMapScene::onDebugShadowMapSettingsChanged() { QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph(); diff --git a/src/3d/qgs3dmapscene.h b/src/3d/qgs3dmapscene.h index 2705b589011..fc393c4b505 100644 --- a/src/3d/qgs3dmapscene.h +++ b/src/3d/qgs3dmapscene.h @@ -201,6 +201,7 @@ class _3D_EXPORT Qgs3DMapScene : public Qt3DCore::QEntity void onRenderersChanged(); void onSkyboxSettingsChanged(); void onShadowSettingsChanged(); + void onAmbientOcclusionSettingsChanged(); void onEyeDomeShadingSettingsChanged(); void onDebugShadowMapSettingsChanged(); void onDebugDepthMapSettingsChanged(); diff --git a/src/3d/qgs3dmapsettings.cpp b/src/3d/qgs3dmapsettings.cpp index 46f47722df3..f74c49d257f 100644 --- a/src/3d/qgs3dmapsettings.cpp +++ b/src/3d/qgs3dmapsettings.cpp @@ -78,6 +78,7 @@ Qgs3DMapSettings::Qgs3DMapSettings( const Qgs3DMapSettings &other ) , mIsSkyboxEnabled( other.mIsSkyboxEnabled ) , mSkyboxSettings( other.mSkyboxSettings ) , mShadowSettings( other.mShadowSettings ) + , mAmbientOcclusionSettings( other.mAmbientOcclusionSettings ) , mEyeDomeLightingEnabled( other.mEyeDomeLightingEnabled ) , mEyeDomeLightingStrength( other.mEyeDomeLightingStrength ) , mEyeDomeLightingDistance( other.mEyeDomeLightingDistance ) @@ -290,6 +291,9 @@ void Qgs3DMapSettings::readXml( const QDomElement &elem, const QgsReadWriteConte QDomElement elemShadows = elem.firstChildElement( QStringLiteral( "shadow-rendering" ) ); mShadowSettings.readXml( elemShadows, context ); + QDomElement elemAmbientOcclusion = elem.firstChildElement( QStringLiteral( "screen-space-ambient-occlusion" ) ); + mAmbientOcclusionSettings.readXml( elemAmbientOcclusion, context ); + QDomElement elemEyeDomeLighting = elem.firstChildElement( QStringLiteral( "eye-dome-lighting" ) ); mEyeDomeLightingEnabled = elemEyeDomeLighting.attribute( "enabled", QStringLiteral( "0" ) ).toInt(); mEyeDomeLightingStrength = elemEyeDomeLighting.attribute( "eye-dome-lighting-strength", QStringLiteral( "1000.0" ) ).toDouble(); @@ -419,6 +423,10 @@ QDomElement Qgs3DMapSettings::writeXml( QDomDocument &doc, const QgsReadWriteCon mShadowSettings.writeXml( elemShadows, context ); elem.appendChild( elemShadows ); + QDomElement elemAmbientOcclusion = doc.createElement( QStringLiteral( "screen-space-ambient-occlusion" ) ); + mAmbientOcclusionSettings.writeXml( elemAmbientOcclusion, context ); + elem.appendChild( elemAmbientOcclusion ); + QDomElement elemDebug = doc.createElement( QStringLiteral( "debug" ) ); elemDebug.setAttribute( QStringLiteral( "bounding-boxes" ), mShowTerrainBoundingBoxes ? 1 : 0 ); elemDebug.setAttribute( QStringLiteral( "terrain-tile-info" ), mShowTerrainTileInfo ? 1 : 0 ); @@ -861,6 +869,12 @@ void Qgs3DMapSettings::setShadowSettings( const QgsShadowSettings &shadowSetting emit shadowSettingsChanged(); } +void Qgs3DMapSettings::setAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &ambientOcclusionSettings ) +{ + mAmbientOcclusionSettings = ambientOcclusionSettings; + emit ambientOcclusionSettingsChanged(); +} + void Qgs3DMapSettings::setDebugShadowMapSettings( bool enabled, Qt::Corner corner, double size ) { mDebugShadowMapEnabled = enabled; @@ -959,6 +973,7 @@ void Qgs3DMapSettings::connectChangedSignalsToSettingsChanged() connect( this, &Qgs3DMapSettings::shadowSettingsChanged, this, &Qgs3DMapSettings::settingsChanged ); connect( this, &Qgs3DMapSettings::fpsCounterEnabledChanged, this, &Qgs3DMapSettings::settingsChanged ); connect( this, &Qgs3DMapSettings::axisSettingsChanged, this, &Qgs3DMapSettings::settingsChanged ); + connect( this, &Qgs3DMapSettings::ambientOcclusionSettingsChanged, this, &Qgs3DMapSettings::settingsChanged ); } diff --git a/src/3d/qgs3dmapsettings.h b/src/3d/qgs3dmapsettings.h index 9081c0089d4..9b477d6be4a 100644 --- a/src/3d/qgs3dmapsettings.h +++ b/src/3d/qgs3dmapsettings.h @@ -36,6 +36,7 @@ #include "qgsshadowsettings.h" #include "qgscameracontroller.h" #include "qgstemporalrangeobject.h" +#include "qgsambientocclusionsettings.h" class QgsMapLayer; class QgsRasterLayer; @@ -530,6 +531,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec */ QgsShadowSettings shadowSettings() const SIP_SKIP { return mShadowSettings; } + /** + * Returns the current configuration of screen space ambient occlusion + * \since QGIS 3.28 + */ + QgsAmbientOcclusionSettings ambientOcclusionSettings() const SIP_SKIP { return mAmbientOcclusionSettings; } + /** * Sets the current configuration of the skybox * \since QGIS 3.16 @@ -542,6 +549,12 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec */ void setShadowSettings( const QgsShadowSettings &shadowSettings ) SIP_SKIP; + /** + * Sets the current configuration of screen space ambient occlusion + * \since QGIS 3.28 + */ + void setAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &ambientOcclusionSettings ) SIP_SKIP; + /** * Returns whether the skybox is enabled. * \see setIsSkyboxEnabled() @@ -824,6 +837,13 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec */ void shadowSettingsChanged(); + + /** + * Emitted when ambient occlusion rendering settings are changed + * \since QGIS 3.28 + */ + void ambientOcclusionSettingsChanged(); + /** * Emitted when the FPS counter is enabled or disabled * \since QGIS 3.18 @@ -896,6 +916,7 @@ class _3D_EXPORT Qgs3DMapSettings : public QObject, public QgsTemporalRangeObjec bool mIsSkyboxEnabled = false; //!< Whether the skybox is enabled QgsSkyboxSettings mSkyboxSettings; //!< Skybox related configuration QgsShadowSettings mShadowSettings; //!< Shadow rendering related settings + QgsAmbientOcclusionSettings mAmbientOcclusionSettings; //!< Screen Space Ambient Occlusion related settings bool mEyeDomeLightingEnabled = false; double mEyeDomeLightingStrength = 1000.0; diff --git a/src/3d/qgsambientocclusionblurentity.cpp b/src/3d/qgsambientocclusionblurentity.cpp new file mode 100644 index 00000000000..57b67ac0492 --- /dev/null +++ b/src/3d/qgsambientocclusionblurentity.cpp @@ -0,0 +1,32 @@ +/*************************************************************************** + qgsambientocclusionblurentity.cpp + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsambientocclusionblurentity.h" + +#include + +QgsAmbientOcclusionBlurEntity::QgsAmbientOcclusionBlurEntity( Qt3DRender::QTexture2D *texture, QNode *parent ) + : QgsRenderPassQuad( parent ) +{ + mAmbientOcclusionFactorTextureParameter = new Qt3DRender::QParameter( QStringLiteral( "texture" ), texture ); + mMaterial->addParameter( mAmbientOcclusionFactorTextureParameter ); + + const QString vertexShaderPath = QStringLiteral( "qrc:/shaders/ssao_factor_blur.vert" ); + const QString fragmentShaderPath = QStringLiteral( "qrc:/shaders/ssao_factor_blur.frag" ); + + mShader->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( vertexShaderPath ) ) ); + mShader->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( fragmentShaderPath ) ) ); +} + diff --git a/src/3d/qgsambientocclusionblurentity.h b/src/3d/qgsambientocclusionblurentity.h new file mode 100644 index 00000000000..ed6562bfdd8 --- /dev/null +++ b/src/3d/qgsambientocclusionblurentity.h @@ -0,0 +1,41 @@ +/*************************************************************************** + qgsambientocclusionblurentity.h + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSAMBIENTOCCLUSIONBLURENTITY_H +#define QGSAMBIENTOCCLUSIONBLURENTITY_H + +#include "qgsrenderpassquad.h" + +#define SIP_NO_FILE + +/** + * \ingroup 3d + * \brief An entity that is responsible for blurring the ambient occlusion factor texture. + * + * \note Not available in Python bindings + * + * \since QGIS 3.28 + */ +class QgsAmbientOcclusionBlurEntity : public QgsRenderPassQuad +{ + Q_OBJECT + public: + //! Constructor + QgsAmbientOcclusionBlurEntity( Qt3DRender::QTexture2D *texture, QNode *parent = nullptr ); + private: + Qt3DRender::QParameter *mAmbientOcclusionFactorTextureParameter = nullptr; +}; + +#endif // QGSAMBIENTOCCLUSIONBLURENTITY_H diff --git a/src/3d/qgsambientocclusionrenderentity.cpp b/src/3d/qgsambientocclusionrenderentity.cpp new file mode 100644 index 00000000000..5dcd274665f --- /dev/null +++ b/src/3d/qgsambientocclusionrenderentity.cpp @@ -0,0 +1,124 @@ +/*************************************************************************** + qgsambientocclusionrenderentity.cpp + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsambientocclusionrenderentity.h" + +#include + +#include + +QgsAmbientOcclusionRenderEntity::QgsAmbientOcclusionRenderEntity( Qt3DRender::QTexture2D *depthTexture, Qt3DRender::QCamera *camera, QNode *parent ) + : QgsRenderPassQuad( parent ) +{ + mDepthTextureParameter = new Qt3DRender::QParameter( QStringLiteral( "depthTexture" ), depthTexture ); + mMaterial->addParameter( mDepthTextureParameter ); + + mFarPlaneParameter = new Qt3DRender::QParameter( QStringLiteral( "farPlane" ), camera->farPlane() ); + mMaterial->addParameter( mFarPlaneParameter ); + connect( camera, &Qt3DRender::QCamera::farPlaneChanged, mFarPlaneParameter, [&]( float farPlane ) + { + mFarPlaneParameter->setValue( farPlane ); + } ); + mNearPlaneParameter = new Qt3DRender::QParameter( QStringLiteral( "nearPlane" ), camera->nearPlane() ); + mMaterial->addParameter( mNearPlaneParameter ); + connect( camera, &Qt3DRender::QCamera::nearPlaneChanged, mNearPlaneParameter, [&]( float nearPlane ) + { + mNearPlaneParameter->setValue( nearPlane ); + } ); + mProjMatrixParameter = new Qt3DRender::QParameter( QStringLiteral( "origProjMatrix" ), camera->projectionMatrix() ); + mMaterial->addParameter( mProjMatrixParameter ); + connect( camera, &Qt3DRender::QCamera::projectionMatrixChanged, mProjMatrixParameter, [&]( const QMatrix4x4 & projectionMatrix ) + { + mProjMatrixParameter->setValue( projectionMatrix ); + } ); + mAspectRatioParameter = new Qt3DRender::QParameter( QStringLiteral( "uAspectRatio" ), camera->aspectRatio() ); + mMaterial->addParameter( mAspectRatioParameter ); + connect( camera, &Qt3DRender::QCamera::aspectRatioChanged, mAspectRatioParameter, [&]( float ratio ) + { + mAspectRatioParameter->setValue( ratio ); + } ); + mTanHalfFovParameter = new Qt3DRender::QParameter( QStringLiteral( "uTanHalfFov" ), tan( camera->fieldOfView() / 2 * M_PI / 180 ) ); + mMaterial->addParameter( mTanHalfFovParameter ); + connect( camera, &Qt3DRender::QCamera::fieldOfViewChanged, mTanHalfFovParameter, [&]( float fov ) + { + mTanHalfFovParameter->setValue( tan( fov / 2 * M_PI / 180 ) ); + } ); + + QVariantList ssaoKernelValues; + + std::uniform_real_distribution randomFloats( 0.0, 1.0 ); // random floats between [0.0, 1.0] + std::default_random_engine generator; + unsigned int kernelSize = 64; + for ( unsigned int i = 0; i < kernelSize; ++i ) + { + QVector3D sample( + randomFloats( generator ) * 2.0 - 1.0, + randomFloats( generator ) * 2.0 - 1.0, + randomFloats( generator ) * 2.0 - 1.0 + ); + sample.normalize(); + float scale = i / kernelSize; + scale = 0.1 + 0.9 * scale * scale; + sample *= scale; + ssaoKernelValues.push_back( sample ); + } + + // 4x4 array of random rotation vectors + QVariantList ssaoNoise; + for ( unsigned int i = 0; i < 16; ++i ) + { + QVector3D sample( + randomFloats( generator ), + randomFloats( generator ), + 0.0 + ); + ssaoNoise.push_back( sample ); + } + mAmbientOcclusionKernelParameter = new Qt3DRender::QParameter( QStringLiteral( "ssaoKernel[0]" ), ssaoKernelValues ); + mMaterial->addParameter( mAmbientOcclusionKernelParameter ); + + Qt3DRender::QParameter *noiseParameter = new Qt3DRender::QParameter( QStringLiteral( "ssaoNoise[0]" ), ssaoNoise ); + mMaterial->addParameter( noiseParameter ); + + mIntensityParameter = new Qt3DRender::QParameter( QStringLiteral( "intensity" ), 0.5f ); + mMaterial->addParameter( mIntensityParameter ); + + mRadiusParameter = new Qt3DRender::QParameter( QStringLiteral( "radius" ), 25.0f ); + mMaterial->addParameter( mRadiusParameter ); + + mThresholdParameter = new Qt3DRender::QParameter( QStringLiteral( "threshold" ), 0.5f ); + mMaterial->addParameter( mThresholdParameter ); + + const QString vertexShaderPath = QStringLiteral( "qrc:/shaders/ssao_factor_render.vert" ); + const QString fragmentShaderPath = QStringLiteral( "qrc:/shaders/ssao_factor_render.frag" ); + + mShader->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( vertexShaderPath ) ) ); + mShader->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( fragmentShaderPath ) ) ); +} + +void QgsAmbientOcclusionRenderEntity::setIntensity( float intensity ) +{ + mIntensityParameter->setValue( intensity ); +} + +void QgsAmbientOcclusionRenderEntity::setRadius( float radius ) +{ + mRadiusParameter->setValue( radius ); +} + +void QgsAmbientOcclusionRenderEntity::setThreshold( float threshold ) +{ + mThresholdParameter->setValue( threshold ); +} diff --git a/src/3d/qgsambientocclusionrenderentity.h b/src/3d/qgsambientocclusionrenderentity.h new file mode 100644 index 00000000000..b8daeb3c373 --- /dev/null +++ b/src/3d/qgsambientocclusionrenderentity.h @@ -0,0 +1,65 @@ +/*************************************************************************** + qgsambientocclusionrenderentity.h + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSAMBIENTOCCLUSIONRENDERENTITY_H +#define QGSAMBIENTOCCLUSIONRENDERENTITY_H + +#include "qgsrenderpassquad.h" + +#define SIP_NO_FILE + +/** + * \ingroup 3d + * \brief An entity that is responsible for producing an ambient occlusion factor map. + * + * \note Not available in Python bindings + * + * \since QGIS 3.28 + */ +class QgsAmbientOcclusionRenderEntity : public QgsRenderPassQuad +{ + Q_OBJECT + public: + //! Constructor + QgsAmbientOcclusionRenderEntity( Qt3DRender::QTexture2D *depthTexture, Qt3DRender::QCamera *camera, QNode *parent = nullptr ); + + //! Sets the intensity for the ambient occlusion effect + void setIntensity( float intensity ); + + //! Sets the radius for the ambient occlusion effect + void setRadius( float radius ); + + //! Sets the amount of occlusion when the effects starts to kick in + void setThreshold( float threshold ); + + private: + + Qt3DRender::QParameter *mDepthTextureParameter = nullptr; + Qt3DRender::QParameter *mAmbientOcclusionKernelParameter = nullptr; + + // user configurable + Qt3DRender::QParameter *mIntensityParameter = nullptr; + Qt3DRender::QParameter *mRadiusParameter = nullptr; + Qt3DRender::QParameter *mThresholdParameter = nullptr; + + // derived from camera parameters + Qt3DRender::QParameter *mFarPlaneParameter = nullptr; + Qt3DRender::QParameter *mNearPlaneParameter = nullptr; + Qt3DRender::QParameter *mProjMatrixParameter = nullptr; + Qt3DRender::QParameter *mAspectRatioParameter = nullptr; + Qt3DRender::QParameter *mTanHalfFovParameter = nullptr; +}; + +#endif // QGSAMBIENTOCCLUSIONRENDERENTITY_H diff --git a/src/3d/qgsambientocclusionsettings.cpp b/src/3d/qgsambientocclusionsettings.cpp new file mode 100644 index 00000000000..8a140fdfddf --- /dev/null +++ b/src/3d/qgsambientocclusionsettings.cpp @@ -0,0 +1,59 @@ +/*************************************************************************** + qgsambientocclusionsettings.cpp + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsambientocclusionsettings.h" + +#include + +#include "qgsreadwritecontext.h" + + +QgsAmbientOcclusionSettings::QgsAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &other ) + : mEnabled( other.mEnabled ) + , mIntensity( other.mIntensity ) + , mRadius( other.mRadius ) + , mThreshold( other.mThreshold ) +{ + +} + +QgsAmbientOcclusionSettings &QgsAmbientOcclusionSettings::operator=( QgsAmbientOcclusionSettings const &rhs ) +{ + mEnabled = rhs.mEnabled; + mIntensity = rhs.mIntensity; + mRadius = rhs.mRadius; + mThreshold = rhs.mThreshold; + return *this; +} + +void QgsAmbientOcclusionSettings::readXml( const QDomElement &element, const QgsReadWriteContext &context ) +{ + mEnabled = element.attribute( QStringLiteral( "enabled" ), QStringLiteral( "0" ) ).toInt(); + mIntensity = element.attribute( QStringLiteral( "intensity" ), QStringLiteral( "0.5" ) ).toFloat(); + mRadius = element.attribute( QStringLiteral( "radius" ), QStringLiteral( "25" ) ).toFloat(); + mThreshold = element.attribute( QStringLiteral( "threshold" ), QStringLiteral( "0.5" ) ).toFloat(); + + Q_UNUSED( context ); +} + +void QgsAmbientOcclusionSettings::writeXml( QDomElement &element, const QgsReadWriteContext &context ) const +{ + element.setAttribute( QStringLiteral( "enabled" ), mEnabled ); + element.setAttribute( QStringLiteral( "intensity" ), mIntensity ); + element.setAttribute( QStringLiteral( "radius" ), mRadius ); + element.setAttribute( QStringLiteral( "threshold" ), mThreshold ); + + Q_UNUSED( context ); +} diff --git a/src/3d/qgsambientocclusionsettings.h b/src/3d/qgsambientocclusionsettings.h new file mode 100644 index 00000000000..0be2e83e6be --- /dev/null +++ b/src/3d/qgsambientocclusionsettings.h @@ -0,0 +1,81 @@ +/*************************************************************************** + qgsambientocclusionsettings.h + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSAMBIENTOCCLUSIONSETTINGS_H +#define QGSAMBIENTOCCLUSIONSETTINGS_H + +#include +#include + +#include "qgis_3d.h" + +class QgsReadWriteContext; +class QDomElement; + +#define SIP_NO_FILE + +/** + * \brief class containing the configuration of ambient occlusion rendering + * \ingroup 3d + * \note Not available in Python bindings + * \since QGIS 3.28 + */ +class _3D_EXPORT QgsAmbientOcclusionSettings +{ + public: + //! Default constructor + QgsAmbientOcclusionSettings() = default; + //! Copy constructor + QgsAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &other ); + //! delete assignment operator + QgsAmbientOcclusionSettings &operator=( QgsAmbientOcclusionSettings const &rhs ); + + //! Reads settings from a DOM \a element + void readXml( const QDomElement &element, const QgsReadWriteContext &context ); + //! Writes settings to a DOM \a element + void writeXml( QDomElement &element, const QgsReadWriteContext &context ) const; + + //! Sets whether ambient occlusion effect is enabled + void setEnabled( bool enabled ) { mEnabled = enabled; } + + //! Returns whether ambient occlusion effect is enabled + bool isEnabled() const { return mEnabled; } + + //! Sets the shading factor of the ambient occlusion effect + void setIntensity( float factor ) { mIntensity = factor; } + + //! Returns the shading factor of the ambient occlusion effect + float intensity() const { return mIntensity; } + + //! Sets the radius parameter of the ambient occlusion effect + void setRadius( float radius ) { mRadius = radius; } + + //! Returns the radius parameter of the ambient occlusion effect + float radius() const { return mRadius; } + + //! Sets at what amount of occlusion the effect will kick in + void setThreshold( float threshold ) { mThreshold = threshold; } + + //! Returns at what amount of occlusion the effect will kick in + float threshold() const { return mThreshold; } + + private: + bool mEnabled = false; + float mIntensity = 0.5f; + float mRadius = 25.0f; + float mThreshold = 0.5f; +}; + +#endif // QGSAMBIENTOCCLUSIONSETTINGS_H diff --git a/src/3d/qgspostprocessingentity.cpp b/src/3d/qgspostprocessingentity.cpp index 2ad9cc748cb..1c291c6a5d4 100644 --- a/src/3d/qgspostprocessingentity.cpp +++ b/src/3d/qgspostprocessingentity.cpp @@ -43,41 +43,17 @@ typedef Qt3DCore::QGeometry Qt3DQGeometry; #include "qgsshadowrenderingframegraph.h" QgsPostprocessingEntity::QgsPostprocessingEntity( QgsShadowRenderingFrameGraph *frameGraph, QNode *parent ) - : Qt3DCore::QEntity( parent ) + : QgsRenderPassQuad( parent ) + , mFrameGraph( frameGraph ) { - Qt3DQGeometry *geom = new Qt3DQGeometry( this ); - Qt3DQAttribute *positionAttribute = new Qt3DQAttribute( this ); - const QVector vert = { -1.0f, -1.0f, 0.0f, /**/ 1.0f, -1.0f, 0.0f, /**/ -1.0f, 1.0f, 0.0f, /**/ -1.0f, 1.0f, 0.0f, /**/ 1.0f, -1.0f, 0.0f, /**/ 1.0f, 1.0f, 0.0f }; - - const QByteArray vertexArr( ( const char * ) vert.constData(), vert.size() * sizeof( float ) ); - Qt3DQBuffer *vertexBuffer = nullptr; - vertexBuffer = new Qt3DQBuffer( this ); - vertexBuffer->setData( vertexArr ); - - positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() ); - positionAttribute->setVertexBaseType( Qt3DQAttribute::Float ); - positionAttribute->setVertexSize( 3 ); - positionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute ); - positionAttribute->setBuffer( vertexBuffer ); - positionAttribute->setByteOffset( 0 ); - positionAttribute->setByteStride( 3 * sizeof( float ) ); - positionAttribute->setCount( 6 ); - - geom->addAttribute( positionAttribute ); - - Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer( this ); - renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::PrimitiveType::Triangles ); - renderer->setGeometry( geom ); - - addComponent( renderer ); - - mMaterial = new Qt3DRender::QMaterial( this ); mColorTextureParameter = new Qt3DRender::QParameter( QStringLiteral( "colorTexture" ), frameGraph->forwardRenderColorTexture() ); mDepthTextureParameter = new Qt3DRender::QParameter( QStringLiteral( "depthTexture" ), frameGraph->forwardRenderDepthTexture() ); mShadowMapParameter = new Qt3DRender::QParameter( QStringLiteral( "shadowTexture" ), frameGraph->shadowMapTexture() ); + mAmbientOcclusionTextureParameter = new Qt3DRender::QParameter( QStringLiteral( "ssaoTexture" ), frameGraph->blurredAmbientOcclusionFactorMap() ); mMaterial->addParameter( mColorTextureParameter ); mMaterial->addParameter( mDepthTextureParameter ); mMaterial->addParameter( mShadowMapParameter ); + mMaterial->addParameter( mAmbientOcclusionTextureParameter ); mMainCamera = frameGraph->mainCamera(); mLightCamera = frameGraph->lightCamera(); @@ -143,39 +119,19 @@ QgsPostprocessingEntity::QgsPostprocessingEntity( QgsShadowRenderingFrameGraph * mMaterial->addParameter( mEyeDomeLightingStrengthParameter ); mMaterial->addParameter( mEyeDomeLightingDistanceParameter ); + mAmbientOcclusionEnabledParameter = new Qt3DRender::QParameter( QStringLiteral( "ssaoEnabled" ), QVariant::fromValue( 0 ) ); + mMaterial->addParameter( mAmbientOcclusionEnabledParameter ); + mLightPosition = new Qt3DRender::QParameter( QStringLiteral( "lightPosition" ), QVariant::fromValue( QVector3D() ) ); mLightDirection = new Qt3DRender::QParameter( QStringLiteral( "lightDirection" ), QVariant::fromValue( QVector3D() ) ); mMaterial->addParameter( mLightPosition ); mMaterial->addParameter( mLightDirection ); - mEffect = new Qt3DRender::QEffect( this ); - Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique( this ); - Qt3DRender::QGraphicsApiFilter *graphicsApiFilter = technique->graphicsApiFilter(); - graphicsApiFilter->setApi( Qt3DRender::QGraphicsApiFilter::Api::OpenGL ); - graphicsApiFilter->setProfile( Qt3DRender::QGraphicsApiFilter::OpenGLProfile::CoreProfile ); - graphicsApiFilter->setMajorVersion( 1 ); - graphicsApiFilter->setMinorVersion( 5 ); - Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( this ); - Qt3DRender::QShaderProgram *shader = new Qt3DRender::QShaderProgram( this ); - const QString vertexShaderPath = QStringLiteral( "qrc:/shaders/postprocess.vert" ); const QString fragmentShaderPath = QStringLiteral( "qrc:/shaders/postprocess.frag" ); - shader->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( vertexShaderPath ) ) ); - shader->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( fragmentShaderPath ) ) ); - renderPass->setShaderProgram( shader ); - - Qt3DRender::QDepthTest *depthTest = new Qt3DRender::QDepthTest( this ); - depthTest->setDepthFunction( Qt3DRender::QDepthTest::Always ); - - renderPass->addRenderState( depthTest ); - - technique->addRenderPass( renderPass ); - - mEffect->addTechnique( technique ); - mMaterial->setEffect( mEffect ); - - addComponent( mMaterial ); + mShader->setVertexShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( vertexShaderPath ) ) ); + mShader->setFragmentShaderCode( Qt3DRender::QShaderProgram::loadSource( QUrl( fragmentShaderPath ) ) ); } void QgsPostprocessingEntity::setupShadowRenderingExtent( float minX, float maxX, float minZ, float maxZ ) @@ -216,3 +172,8 @@ void QgsPostprocessingEntity::setEyeDomeLightingDistance( int distance ) { mEyeDomeLightingDistanceParameter->setValue( QVariant::fromValue( distance ) ); } + +void QgsPostprocessingEntity::setAmbientOcclusionEnabled( bool enabled ) +{ + mAmbientOcclusionEnabledParameter->setValue( enabled ); +} diff --git a/src/3d/qgspostprocessingentity.h b/src/3d/qgspostprocessingentity.h index b5f9db07cdf..c8d656f8b32 100644 --- a/src/3d/qgspostprocessingentity.h +++ b/src/3d/qgspostprocessingentity.h @@ -16,11 +16,7 @@ #ifndef QGSPOSTPROCESSINGENTITY_H #define QGSPOSTPROCESSINGENTITY_H -#include -#include -#include -#include -#include +#include "qgsrenderpassquad.h" class QgsShadowRenderingFrameGraph; @@ -30,13 +26,11 @@ class QgsShadowRenderingFrameGraph; * \ingroup 3d * \brief An entity that is responsible for applying post processing effect. * - * Now it is used to make shadows. - * * \note Not available in Python bindings * * \since QGIS 3.16 */ -class QgsPostprocessingEntity : public Qt3DCore::QEntity +class QgsPostprocessingEntity : public QgsRenderPassQuad { Q_OBJECT @@ -57,13 +51,21 @@ class QgsPostprocessingEntity : public Qt3DCore::QEntity void setEyeDomeLightingStrength( double strength ); //! Sets the eye dome lighting distance (contributes to the contrast of the image) void setEyeDomeLightingDistance( int distance ); + + /** + * Sets whether screen space ambient occlusion is enabled + * \since QGIS 3.28 + */ + void setAmbientOcclusionEnabled( bool enabled ); + private: - Qt3DRender::QMaterial *mMaterial = nullptr; - Qt3DRender::QEffect *mEffect = nullptr; + QgsShadowRenderingFrameGraph *mFrameGraph = nullptr; + Qt3DRender::QCamera *mMainCamera = nullptr; + Qt3DRender::QParameter *mColorTextureParameter = nullptr; Qt3DRender::QParameter *mDepthTextureParameter = nullptr; Qt3DRender::QParameter *mShadowMapParameter = nullptr; - Qt3DRender::QCamera *mMainCamera = nullptr; + Qt3DRender::QParameter *mAmbientOcclusionTextureParameter = nullptr; Qt3DRender::QParameter *mFarPlaneParameter = nullptr; Qt3DRender::QParameter *mNearPlaneParameter = nullptr; Qt3DRender::QParameter *mMainCameraInvViewMatrixParameter = nullptr; @@ -86,6 +88,8 @@ class QgsPostprocessingEntity : public Qt3DCore::QEntity Qt3DRender::QParameter *mEyeDomeLightingEnabledParameter = nullptr; Qt3DRender::QParameter *mEyeDomeLightingStrengthParameter = nullptr; Qt3DRender::QParameter *mEyeDomeLightingDistanceParameter = nullptr; + + Qt3DRender::QParameter *mAmbientOcclusionEnabledParameter = nullptr; }; #endif // QGSPOSTPROCESSINGENTITY_H diff --git a/src/3d/qgsrenderpassquad.cpp b/src/3d/qgsrenderpassquad.cpp new file mode 100644 index 00000000000..331a9c76dd4 --- /dev/null +++ b/src/3d/qgsrenderpassquad.cpp @@ -0,0 +1,105 @@ +/*************************************************************************** + qgsrenderpassquad.cpp + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsrenderpassquad.h" + +#include + +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) +#include +#include +#include + +typedef Qt3DRender::QAttribute Qt3DQAttribute; +typedef Qt3DRender::QBuffer Qt3DQBuffer; +typedef Qt3DRender::QGeometry Qt3DQGeometry; +#else +#include +#include +#include + +typedef Qt3DCore::QAttribute Qt3DQAttribute; +typedef Qt3DCore::QBuffer Qt3DQBuffer; +typedef Qt3DCore::QGeometry Qt3DQGeometry; +#endif + +#include +#include +#include +#include +#include +#include +#include + +QgsRenderPassQuad::QgsRenderPassQuad( QNode *parent ) + : Qt3DCore::QEntity( parent ) +{ + Qt3DQGeometry *geom = new Qt3DQGeometry( this ); + Qt3DQAttribute *positionAttribute = new Qt3DQAttribute( this ); + const QVector vert = { -1.0f, -1.0f, 0.0f, /**/ 1.0f, -1.0f, 0.0f, /**/ -1.0f, 1.0f, 0.0f, /**/ -1.0f, 1.0f, 0.0f, /**/ 1.0f, -1.0f, 0.0f, /**/ 1.0f, 1.0f, 0.0f }; + + const QByteArray vertexArr( ( const char * ) vert.constData(), vert.size() * sizeof( float ) ); + Qt3DQBuffer *vertexBuffer = nullptr; + vertexBuffer = new Qt3DQBuffer( this ); + vertexBuffer->setData( vertexArr ); + + positionAttribute->setName( Qt3DQAttribute::defaultPositionAttributeName() ); + positionAttribute->setVertexBaseType( Qt3DQAttribute::Float ); + positionAttribute->setVertexSize( 3 ); + positionAttribute->setAttributeType( Qt3DQAttribute::VertexAttribute ); + positionAttribute->setBuffer( vertexBuffer ); + positionAttribute->setByteOffset( 0 ); + positionAttribute->setByteStride( 3 * sizeof( float ) ); + positionAttribute->setCount( 6 ); + + geom->addAttribute( positionAttribute ); + + Qt3DRender::QGeometryRenderer *renderer = new Qt3DRender::QGeometryRenderer( this ); + renderer->setPrimitiveType( Qt3DRender::QGeometryRenderer::PrimitiveType::Triangles ); + renderer->setGeometry( geom ); + + addComponent( renderer ); + + mMaterial = new Qt3DRender::QMaterial( this ); + + Qt3DRender::QEffect *effect = new Qt3DRender::QEffect( this ); + Qt3DRender::QTechnique *technique = new Qt3DRender::QTechnique( this ); + Qt3DRender::QGraphicsApiFilter *graphicsApiFilter = technique->graphicsApiFilter(); + graphicsApiFilter->setApi( Qt3DRender::QGraphicsApiFilter::Api::OpenGL ); + graphicsApiFilter->setProfile( Qt3DRender::QGraphicsApiFilter::OpenGLProfile::CoreProfile ); + graphicsApiFilter->setMajorVersion( 3 ); + graphicsApiFilter->setMinorVersion( 1 ); + Qt3DRender::QRenderPass *renderPass = new Qt3DRender::QRenderPass( this ); + mShader = new Qt3DRender::QShaderProgram( this ); + + renderPass->setShaderProgram( mShader ); + + Qt3DRender::QDepthTest *depthTest = new Qt3DRender::QDepthTest( this ); + depthTest->setDepthFunction( Qt3DRender::QDepthTest::Always ); + + renderPass->addRenderState( depthTest ); + + technique->addRenderPass( renderPass ); + + effect->addTechnique( technique ); + mMaterial->setEffect( effect ); + + addComponent( mMaterial ); + + mLayer = new Qt3DRender::QLayer( this ); + mLayer->setRecursive( true ); + addComponent( mLayer ); +} + diff --git a/src/3d/qgsrenderpassquad.h b/src/3d/qgsrenderpassquad.h new file mode 100644 index 00000000000..35d61971b67 --- /dev/null +++ b/src/3d/qgsrenderpassquad.h @@ -0,0 +1,53 @@ +/*************************************************************************** + qgsrenderpassquad.h + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#ifndef QGSRENDERPASSQUAD_H +#define QGSRENDERPASSQUAD_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#define SIP_NO_FILE + +/** + * \ingroup 3d + * \brief An entity that is responsible for rendering a screen quad for a specific rendering pass. + * + * \note Not available in Python bindings + * + * \since QGIS 3.28 + */ +class QgsRenderPassQuad : public Qt3DCore::QEntity +{ + Q_OBJECT + public: + //! Constructor + QgsRenderPassQuad( QNode *parent = nullptr ); + + //! Returns the layer object used to select this entity for rendering in a specific rendering pass + Qt3DRender::QLayer *layer() { return mLayer; } + protected: + Qt3DRender::QMaterial *mMaterial = nullptr; + Qt3DRender::QShaderProgram *mShader = nullptr; + Qt3DRender::QLayer *mLayer = nullptr; +}; + +#endif // QGSRENDERPASSQUAD_H diff --git a/src/3d/qgsshadowrenderingframegraph.cpp b/src/3d/qgsshadowrenderingframegraph.cpp index 990bceefd17..f2741bbc4a3 100644 --- a/src/3d/qgsshadowrenderingframegraph.cpp +++ b/src/3d/qgsshadowrenderingframegraph.cpp @@ -17,6 +17,9 @@ #include "qgsdirectionallightsettings.h" #include "qgspostprocessingentity.h" #include "qgspreviewquad.h" +#include "qgs3dutils.h" +#include "qgsambientocclusionrenderentity.h" +#include "qgsambientocclusionblurentity.h" #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) #include @@ -230,7 +233,6 @@ Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructPostprocessi mPostProcessingCameraSelector->setCamera( mLightCamera ); mPostprocessPassLayerFilter = new Qt3DRender::QLayerFilter( mPostProcessingCameraSelector ); - mPostprocessPassLayerFilter->addLayer( mPostprocessPassLayer ); mPostprocessClearBuffers = new Qt3DRender::QClearBuffers( mPostprocessPassLayerFilter ); @@ -274,9 +276,106 @@ Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructPostprocessi mRenderCapture = new Qt3DRender::QRenderCapture( mRenderCaptureTargetSelector ); + mPostprocessingEntity = new QgsPostprocessingEntity( this, mRootEntity ); + mPostprocessPassLayerFilter->addLayer( mPostprocessingEntity->layer() ); + return mPostProcessingCameraSelector; } +Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructAmbientOcclusionRenderPass() +{ + mAmbientOcclusionRenderCameraSelector = new Qt3DRender::QCameraSelector; + mAmbientOcclusionRenderCameraSelector->setCamera( mMainCamera ); + + mAmbientOcclusionRenderStateSet = new Qt3DRender::QRenderStateSet( mAmbientOcclusionRenderCameraSelector ); + + Qt3DRender::QDepthTest *depthRenderDepthTest = new Qt3DRender::QDepthTest; + depthRenderDepthTest->setDepthFunction( Qt3DRender::QDepthTest::Always );; + Qt3DRender::QCullFace *depthRenderCullFace = new Qt3DRender::QCullFace; + depthRenderCullFace->setMode( Qt3DRender::QCullFace::NoCulling ); + + mAmbientOcclusionRenderStateSet->addRenderState( depthRenderDepthTest ); + mAmbientOcclusionRenderStateSet->addRenderState( depthRenderCullFace ); + + mAmbientOcclusionRenderLayerFilter = new Qt3DRender::QLayerFilter( mAmbientOcclusionRenderStateSet ); + + mAmbientOcclusionRenderCaptureTargetSelector = new Qt3DRender::QRenderTargetSelector( mAmbientOcclusionRenderLayerFilter ); + Qt3DRender::QRenderTarget *depthRenderTarget = new Qt3DRender::QRenderTarget( mAmbientOcclusionRenderCaptureTargetSelector ); + + // The lifetime of the objects created here is managed + // automatically, as they become children of this object. + + // Create a render target output for rendering color. + Qt3DRender::QRenderTargetOutput *colorOutput = new Qt3DRender::QRenderTargetOutput( depthRenderTarget ); + colorOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Color0 ); + + // Create a texture to render into. + mAmbientOcclusionRenderTexture = new Qt3DRender::QTexture2D( colorOutput ); + mAmbientOcclusionRenderTexture->setSize( mSize.width(), mSize.height() ); + mAmbientOcclusionRenderTexture->setFormat( Qt3DRender::QAbstractTexture::R32F ); + mAmbientOcclusionRenderTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear ); + mAmbientOcclusionRenderTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear ); + + // Hook the texture up to our output, and the output up to this object. + colorOutput->setTexture( mAmbientOcclusionRenderTexture ); + depthRenderTarget->addOutput( colorOutput ); + + mAmbientOcclusionRenderCaptureTargetSelector->setTarget( depthRenderTarget ); + + mAmbientOcclusionRenderEntity = new QgsAmbientOcclusionRenderEntity( mForwardDepthTexture, mMainCamera, mRootEntity ); + mAmbientOcclusionRenderLayerFilter->addLayer( mAmbientOcclusionRenderEntity->layer() ); + + return mAmbientOcclusionRenderCameraSelector; +} + +Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructAmbientOcclusionBlurPass() +{ + mAmbientOcclusionBlurCameraSelector = new Qt3DRender::QCameraSelector; + mAmbientOcclusionBlurCameraSelector->setCamera( mMainCamera ); + + mAmbientOcclusionBlurStateSet = new Qt3DRender::QRenderStateSet( mAmbientOcclusionBlurCameraSelector ); + + Qt3DRender::QDepthTest *depthRenderDepthTest = new Qt3DRender::QDepthTest; + depthRenderDepthTest->setDepthFunction( Qt3DRender::QDepthTest::Always );; + Qt3DRender::QCullFace *depthRenderCullFace = new Qt3DRender::QCullFace; + depthRenderCullFace->setMode( Qt3DRender::QCullFace::NoCulling ); + + mAmbientOcclusionBlurStateSet->addRenderState( depthRenderDepthTest ); + mAmbientOcclusionBlurStateSet->addRenderState( depthRenderCullFace ); + + mAmbientOcclusionBlurLayerFilter = new Qt3DRender::QLayerFilter( mAmbientOcclusionBlurStateSet ); + + mAmbientOcclusionBlurRenderCaptureTargetSelector = new Qt3DRender::QRenderTargetSelector( mAmbientOcclusionBlurLayerFilter ); + Qt3DRender::QRenderTarget *depthRenderTarget = new Qt3DRender::QRenderTarget( mAmbientOcclusionBlurRenderCaptureTargetSelector ); + + // The lifetime of the objects created here is managed + // automatically, as they become children of this object. + + // Create a render target output for rendering color. + Qt3DRender::QRenderTargetOutput *colorOutput = new Qt3DRender::QRenderTargetOutput( depthRenderTarget ); + colorOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Color0 ); + + // Create a texture to render into. + mAmbientOcclusionBlurTexture = new Qt3DRender::QTexture2D( colorOutput ); + mAmbientOcclusionBlurTexture->setSize( mSize.width(), mSize.height() ); + mAmbientOcclusionBlurTexture->setFormat( Qt3DRender::QAbstractTexture::R32F ); + mAmbientOcclusionBlurTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear ); + mAmbientOcclusionBlurTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear ); + + // Hook the texture up to our output, and the output up to this object. + colorOutput->setTexture( mAmbientOcclusionBlurTexture ); + depthRenderTarget->addOutput( colorOutput ); + + mAmbientOcclusionBlurRenderCaptureTargetSelector->setTarget( depthRenderTarget ); + + mAmbientOcclusionBlurEntity = new QgsAmbientOcclusionBlurEntity( mAmbientOcclusionRenderTexture, mRootEntity ); + mAmbientOcclusionBlurLayerFilter->addLayer( mAmbientOcclusionBlurEntity->layer() ); + + return mAmbientOcclusionBlurCameraSelector; +} + + + Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructDepthRenderPass() { // depth buffer render to copy pass @@ -417,14 +516,12 @@ QgsShadowRenderingFrameGraph::QgsShadowRenderingFrameGraph( QSurface *surface, Q mMainCamera = mainCamera; mLightCamera = new Qt3DRender::QCamera; - mPostprocessPassLayer = new Qt3DRender::QLayer; mPreviewLayer = new Qt3DRender::QLayer; mCastShadowsLayer = new Qt3DRender::QLayer; mForwardRenderLayer = new Qt3DRender::QLayer; mDepthRenderPassLayer = new Qt3DRender::QLayer; mTransparentObjectsPassLayer = new Qt3DRender::QLayer; - mPostprocessPassLayer->setRecursive( true ); mPreviewLayer->setRecursive( true ); mCastShadowsLayer->setRecursive( true ); mForwardRenderLayer->setRecursive( true ); @@ -455,6 +552,13 @@ QgsShadowRenderingFrameGraph::QgsShadowRenderingFrameGraph( QSurface *surface, Q Qt3DRender::QFrameGraphNode *depthBufferProcessingPass = constructDepthRenderPass(); depthBufferProcessingPass->setParent( mMainViewPort ); + // Ambient occlusion factor render pass + Qt3DRender::QFrameGraphNode *ambientOcclusionFactorRender = constructAmbientOcclusionRenderPass(); + ambientOcclusionFactorRender->setParent( mMainViewPort ); + + Qt3DRender::QFrameGraphNode *ambientOcclusionBlurPass = constructAmbientOcclusionBlurPass(); + ambientOcclusionBlurPass->setParent( mMainViewPort ); + // post process Qt3DRender::QFrameGraphNode *postprocessingPass = constructPostprocessingPass(); postprocessingPass->setParent( mMainViewPort ); @@ -463,9 +567,6 @@ QgsShadowRenderingFrameGraph::QgsShadowRenderingFrameGraph( QSurface *surface, Q Qt3DRender::QFrameGraphNode *previewPass = constructTexturesPreviewPass(); previewPass->setParent( mMainViewPort ); - mPostprocessingEntity = new QgsPostprocessingEntity( this, mRootEntity ); - mPostprocessingEntity->addComponent( mPostprocessPassLayer ); - Qt3DRender::QParameter *depthMapIsDepthParam = new Qt3DRender::QParameter( "isDepth", true ); Qt3DRender::QParameter *shadowMapIsDepthParam = new Qt3DRender::QParameter( "isDepth", true ); @@ -603,6 +704,31 @@ void QgsShadowRenderingFrameGraph::setShadowMapResolution( int resolution ) mShadowMapTexture->setHeight( mShadowMapResolution ); } +void QgsShadowRenderingFrameGraph::setAmbientOcclusionEnabled( bool enabled ) +{ + mAmbientOcclusionEnabled = enabled; + mAmbientOcclusionRenderEntity->setEnabled( enabled ); + mPostprocessingEntity->setAmbientOcclusionEnabled( enabled ); +} + +void QgsShadowRenderingFrameGraph::setAmbientOcclusionIntensity( float intensity ) +{ + mAmbientOcclusionIntensity = intensity; + mAmbientOcclusionRenderEntity->setIntensity( intensity ); +} + +void QgsShadowRenderingFrameGraph::setAmbientOcclusionRadius( float radius ) +{ + mAmbientOcclusionRadius = radius; + mAmbientOcclusionRenderEntity->setRadius( radius ); +} + +void QgsShadowRenderingFrameGraph::setAmbientOcclusionThreshold( float threshold ) +{ + mAmbientOcclusionThreshold = threshold; + mAmbientOcclusionRenderEntity->setThreshold( threshold ); +} + void QgsShadowRenderingFrameGraph::setFrustumCullingEnabled( bool enabled ) { if ( enabled == mFrustumCullingEnabled ) @@ -681,6 +807,9 @@ void QgsShadowRenderingFrameGraph::setSize( QSize s ) mDepthRenderCaptureDepthTexture->setSize( mSize.width(), mSize.height() ); mDepthRenderCaptureColorTexture->setSize( mSize.width(), mSize.height() ); mRenderSurfaceSelector->setExternalRenderTargetSize( mSize ); + + mAmbientOcclusionRenderTexture->setSize( mSize.width(), mSize.height() ); + mAmbientOcclusionBlurTexture->setSize( mSize.width(), mSize.height() ); } void QgsShadowRenderingFrameGraph::setRenderCaptureEnabled( bool enabled ) diff --git a/src/3d/qgsshadowrenderingframegraph.h b/src/3d/qgsshadowrenderingframegraph.h index ea4f1ac1ed5..2e5c44e1ed7 100644 --- a/src/3d/qgsshadowrenderingframegraph.h +++ b/src/3d/qgsshadowrenderingframegraph.h @@ -44,7 +44,9 @@ class QgsDirectionalLightSettings; class QgsCameraController; class QgsRectangle; class QgsPostprocessingEntity; +class QgsAmbientOcclusionRenderEntity; class QgsPreviewQuad; +class QgsAmbientOcclusionBlurEntity; #define SIP_NO_FILE @@ -74,8 +76,18 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity //! Returns the shadow map (a depth texture for the shadow rendering pass) Qt3DRender::QTexture2D *shadowMapTexture() { return mShadowMapTexture; } - //! Returns a layer object used to indicate that an entity is to be rendered during the post processing rendering pass - Qt3DRender::QLayer *postprocessingPassLayer() { return mPostprocessPassLayer; } + /** + * Returns ambient occlusion factor values texture + * \since QGIS 3.28 + */ + Qt3DRender::QTexture2D *ambientOcclusionFactorMap() { return mAmbientOcclusionRenderTexture; } + + /** + * Returns blurred ambient occlusion factor values texture + * \since QGIS 3.28 + */ + Qt3DRender::QTexture2D *blurredAmbientOcclusionFactorMap() { return mAmbientOcclusionBlurTexture; } + //! Returns a layer object used to indicate that an entity is to be rendered during the preview textures rendering pass Qt3DRender::QLayer *previewLayer() { return mPreviewLayer; } //! Returns a layer object used to indicate that an entity will cast shadows @@ -125,6 +137,55 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity //! Sets the resolution of the shadow map void setShadowMapResolution( int resolution ); + + /** + * Sets whether Screen Space Ambient Occlusion will be enabled + * \since QGIS 3.28 + */ + void setAmbientOcclusionEnabled( bool enabled ); + + /** + * Returns whether Screen Space Ambient Occlusion is enabled + * \since QGIS 3.28 + */ + bool ambientOcclusionEnabled() const { return mAmbientOcclusionEnabled; } + + /** + * Sets the ambient occlusion intensity + * \since QGIS 3.28 + */ + void setAmbientOcclusionIntensity( float intensity ); + + /** + * Returns the ambient occlusion intensity + * \since QGIS 3.28 + */ + float ambientOcclusionIntensity() const { return mAmbientOcclusionIntensity; } + + /** + * Sets the ambient occlusion radius + * \since QGIS 3.28 + */ + void setAmbientOcclusionRadius( float radius ); + + /** + * Returns the ambient occlusion radius + * \since QGIS 3.28 + */ + float ambientOcclusionRadius() const { return mAmbientOcclusionRadius; } + + /** + * Sets the ambient occlusion threshold + * \since QGIS 3.28 + */ + void setAmbientOcclusionThreshold( float threshold ); + + /** + * Returns the ambient occlusion threshold + * \since QGIS 3.28 + */ + float ambientOcclusionThreshold() const { return mAmbientOcclusionThreshold; } + //! Sets the clear color of the scene (background color) void setClearColor( const QColor &clearColor ); //! Adds an preview entity that shows a texture in real time for debugging purposes @@ -212,6 +273,22 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity Qt3DRender::QTexture2D *mRenderCaptureColorTexture = nullptr; Qt3DRender::QTexture2D *mRenderCaptureDepthTexture = nullptr; + // Ambient occlusion factor generation pass + Qt3DRender::QCameraSelector *mAmbientOcclusionRenderCameraSelector = nullptr; + Qt3DRender::QRenderStateSet *mAmbientOcclusionRenderStateSet = nullptr;; + Qt3DRender::QLayerFilter *mAmbientOcclusionRenderLayerFilter = nullptr; + Qt3DRender::QRenderTargetSelector *mAmbientOcclusionRenderCaptureTargetSelector = nullptr; + // Ambient occlusion factor generation pass texture related objects: + Qt3DRender::QTexture2D *mAmbientOcclusionRenderTexture = nullptr; + + // Ambient occlusion factor blur pass + Qt3DRender::QCameraSelector *mAmbientOcclusionBlurCameraSelector = nullptr; + Qt3DRender::QRenderStateSet *mAmbientOcclusionBlurStateSet = nullptr;; + Qt3DRender::QLayerFilter *mAmbientOcclusionBlurLayerFilter = nullptr; + Qt3DRender::QRenderTargetSelector *mAmbientOcclusionBlurRenderCaptureTargetSelector = nullptr; + // Ambient occlusion factor blur pass texture related objects: + Qt3DRender::QTexture2D *mAmbientOcclusionBlurTexture = nullptr; + // Texture preview: Qt3DRender::QLayerFilter *mPreviewLayerFilter = nullptr; Qt3DRender::QRenderStateSet *mPreviewRenderStateSet = nullptr; @@ -222,6 +299,12 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity float mShadowBias = 0.00001f; int mShadowMapResolution = 2048; + // Ambient occlusion related settings + bool mAmbientOcclusionEnabled = false; + float mAmbientOcclusionIntensity = 0.5f; + float mAmbientOcclusionRadius = 25.f; + float mAmbientOcclusionThreshold = 0.5f; + QSize mSize = QSize( 1024, 768 ); bool mEyeDomeLightingEnabled = false; @@ -237,7 +320,6 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity Qt3DCore::QEntity *mRootEntity = nullptr; - Qt3DRender::QLayer *mPostprocessPassLayer = nullptr; Qt3DRender::QLayer *mPreviewLayer = nullptr; Qt3DRender::QLayer *mForwardRenderLayer = nullptr; Qt3DRender::QLayer *mCastShadowsLayer = nullptr; @@ -245,6 +327,8 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity Qt3DRender::QLayer *mTransparentObjectsPassLayer = nullptr; QgsPostprocessingEntity *mPostprocessingEntity = nullptr; + QgsAmbientOcclusionRenderEntity *mAmbientOcclusionRenderEntity = nullptr; + QgsAmbientOcclusionBlurEntity *mAmbientOcclusionBlurEntity = nullptr; QVector mPreviewQuads; @@ -253,6 +337,8 @@ class QgsShadowRenderingFrameGraph : public Qt3DCore::QEntity Qt3DRender::QFrameGraphNode *constructTexturesPreviewPass(); Qt3DRender::QFrameGraphNode *constructPostprocessingPass(); Qt3DRender::QFrameGraphNode *constructDepthRenderPass(); + Qt3DRender::QFrameGraphNode *constructAmbientOcclusionRenderPass(); + Qt3DRender::QFrameGraphNode *constructAmbientOcclusionBlurPass(); Qt3DCore::QEntity *constructDepthRenderQuad(); diff --git a/src/3d/shaders.qrc b/src/3d/shaders.qrc index e9a77dc41e0..503d4a5b6d4 100644 --- a/src/3d/shaders.qrc +++ b/src/3d/shaders.qrc @@ -30,5 +30,9 @@ shaders/goochDataDefined.vert shaders/depth_render.frag shaders/depth_render.vert + shaders/ssao_factor_render.frag + shaders/ssao_factor_render.vert + shaders/ssao_factor_blur.frag + shaders/ssao_factor_blur.vert diff --git a/src/3d/shaders/postprocess.frag b/src/3d/shaders/postprocess.frag index a346b35826d..9d77bdcb71b 100644 --- a/src/3d/shaders/postprocess.frag +++ b/src/3d/shaders/postprocess.frag @@ -4,6 +4,7 @@ uniform sampler2D colorTexture; uniform sampler2D depthTexture; //uniform sampler2DShadow shadowTexture; uniform sampler2D shadowTexture; +uniform sampler2D ssaoTexture; // light camera uniforms uniform mat4 viewMatrix; @@ -32,6 +33,8 @@ uniform int edlEnabled; uniform float edlStrength; uniform int edlDistance; +uniform int ssaoEnabled; + in vec2 texCoord; out vec4 fragColor; @@ -131,4 +134,8 @@ void main() float shade = exp(-edlFactor(texCoord) * edlStrength); fragColor = vec4(fragColor.rgb * shade, fragColor.a); } + if ( ssaoEnabled != 0 ) + { + fragColor = vec4( fragColor.rgb * texture( ssaoTexture, texCoord ).r, fragColor.a ); + } } diff --git a/src/3d/shaders/postprocess.vert b/src/3d/shaders/postprocess.vert index 34dbbac9493..a06b7a5e1f9 100644 --- a/src/3d/shaders/postprocess.vert +++ b/src/3d/shaders/postprocess.vert @@ -2,7 +2,6 @@ in vec3 vertexPosition; -out vec3 worldPosition; out vec2 texCoord; void main() diff --git a/src/3d/shaders/ssao_factor_blur.frag b/src/3d/shaders/ssao_factor_blur.frag new file mode 100644 index 00000000000..7f314a5cdaf --- /dev/null +++ b/src/3d/shaders/ssao_factor_blur.frag @@ -0,0 +1,21 @@ +#version 330 + +uniform sampler2D texture; + +in vec2 texCoord; + +out float fragColor; + +void main() +{ + // simple 4x4 box blur which smoothes the 4x4 noise pattern + float result = 0.0; + for (int x = -2; x < 2; ++x) + { + for (int y = -2; y < 2; ++y) + { + result += texelFetch(texture, ivec2(gl_FragCoord) + ivec2(x,y), 0).r; + } + } + fragColor = result / (4.0 * 4.0); +} diff --git a/src/3d/shaders/ssao_factor_blur.vert b/src/3d/shaders/ssao_factor_blur.vert new file mode 100644 index 00000000000..a06b7a5e1f9 --- /dev/null +++ b/src/3d/shaders/ssao_factor_blur.vert @@ -0,0 +1,11 @@ +#version 150 core + +in vec3 vertexPosition; + +out vec2 texCoord; + +void main() +{ + gl_Position = vec4(vertexPosition, 1.0f); + texCoord = (vertexPosition.xy + vec2(1.0f)) / 2.0f; +} diff --git a/src/3d/shaders/ssao_factor_render.frag b/src/3d/shaders/ssao_factor_render.frag new file mode 100644 index 00000000000..73b46e316eb --- /dev/null +++ b/src/3d/shaders/ssao_factor_render.frag @@ -0,0 +1,77 @@ +#version 330 + +uniform sampler2D depthTexture; + +uniform float farPlane; +uniform float nearPlane; +uniform mat4 origProjMatrix; // Perspective projection matrix used for forward rendering + +uniform vec3 ssaoKernel[64]; // Random sample vectors in a unit sphere +const int kernelSize = 64; +uniform vec3 ssaoNoise[16]; // 4x4 random noise pattern + +uniform float intensity; // Amplification of shading +uniform float radius; // Radius of neighborhood sphere (in world units) +uniform float threshold; // At what amount of occlusion to start darkening + +noperspective in vec3 vViewRay; // Ray to far plane + +out vec4 fragColor; + + +float linearizeDepth(float depth) +{ + float ndc = depth * 2.0 - 1.0; + return (2.0 * nearPlane * farPlane) / (farPlane + nearPlane - ndc * (farPlane - nearPlane)); +} + +vec3 rotate_x(vec3 vct, float angle) +{ + return vec3(vct.x*cos(angle)-vct.y*sin(angle), vct.x*sin(angle)+vct.y*cos(angle), vct.z); +} +vec3 rotate_y(vec3 vct, float angle) +{ + return vec3(vct.x*cos(angle)+vct.z*sin(angle), vct.y, -vct.x*sin(angle)+vct.z*cos(angle)); +} + +float ssao(vec3 originPos, vec3 noise) +{ + float occlusion = 0.0; + for (int i = 0; i < kernelSize; ++i) + { + // get sample position: + vec3 samplePos = rotate_y(rotate_x(ssaoKernel[i], noise.x), noise.y); + samplePos = samplePos * radius + originPos; + + // project sample position: + vec4 offset = origProjMatrix * vec4(samplePos, 1.0); + offset.xy /= offset.w; // only need xy (range [-1,1]) + offset.xy = offset.xy * 0.5 + 0.5; // scale/bias to texcoords (range [0,1]) + + // get sample depth: + float sampleDepth = texture(depthTexture, offset.xy).r; + sampleDepth = linearizeDepth(sampleDepth); + + float rangeCheck = smoothstep(0.0, 1.0, radius / abs(originPos.z - sampleDepth)); + occlusion += rangeCheck * step(sampleDepth, samplePos.z); + } + + return min(1.0, (1.0 - (occlusion / float(kernelSize))) / (1.0 - threshold)); +} + + +void main() +{ + // calculate view-space 3D coordinates of this pixel + vec2 texelSize = 1.0 / vec2(textureSize(depthTexture, 0)); + vec2 screenTexCoords = gl_FragCoord.xy * texelSize; + float originDepth = linearizeDepth(texture(depthTexture, screenTexCoords).r); + vec3 originPos = vViewRay * originDepth; + + int a_idx = int(gl_FragCoord.x) % 4 + 4 * (int(gl_FragCoord.y) % 4); + vec3 noise = ssaoNoise[a_idx] * 2*3.14; + + float ssao_res = ssao(originPos, noise); + + fragColor = vec4(pow(ssao_res,intensity)); +} diff --git a/src/3d/shaders/ssao_factor_render.vert b/src/3d/shaders/ssao_factor_render.vert new file mode 100644 index 00000000000..0a84634378d --- /dev/null +++ b/src/3d/shaders/ssao_factor_render.vert @@ -0,0 +1,23 @@ +#version 150 core + +in vec3 vertexPosition; + +out vec2 texCoord; + +noperspective out vec3 vViewRay; // ray to far plane + +// view frustum parameters: +uniform float uTanHalfFov; +uniform float uAspectRatio; + +void main() +{ + gl_Position = vec4(vertexPosition, 1.0f); + texCoord = (vertexPosition.xy + vec2(1.0f)) / 2.0f; + + vViewRay = vec3( + -vertexPosition.x * uTanHalfFov * uAspectRatio, + -vertexPosition.y * uTanHalfFov, + 1.0 // since we'll be multiplying by linear depth, leave z as 1 + ); +} diff --git a/src/app/3d/qgs3dmapcanvaswidget.cpp b/src/app/3d/qgs3dmapcanvaswidget.cpp index 1c4678d6297..188781568ad 100644 --- a/src/app/3d/qgs3dmapcanvaswidget.cpp +++ b/src/app/3d/qgs3dmapcanvaswidget.cpp @@ -154,6 +154,17 @@ Qgs3DMapCanvasWidget::Qgs3DMapCanvasWidget( const QString &name, bool isDocked ) mCanvas->map()->setEyeDomeLightingEnabled( enabled ); } ); mOptionsMenu->addAction( mActionEnableEyeDome ); + + mActionEnableAmbientOcclusion = new QAction( tr( "Show Ambient Occlusion" ), this ); + mActionEnableAmbientOcclusion->setCheckable( true ); + connect( mActionEnableAmbientOcclusion, &QAction::triggered, this, [ = ]( bool enabled ) + { + QgsAmbientOcclusionSettings ambientOcclusionSettings = mCanvas->map()->ambientOcclusionSettings(); + ambientOcclusionSettings.setEnabled( enabled ); + mCanvas->map()->setAmbientOcclusionSettings( ambientOcclusionSettings ); + } ); + mOptionsMenu->addAction( mActionEnableAmbientOcclusion ); + mOptionsMenu->addSeparator(); mActionSync2DNavTo3D = new QAction( tr( "2D Map View Follows 3D Camera" ), this ); @@ -344,6 +355,7 @@ void Qgs3DMapCanvasWidget::setMapSettings( Qgs3DMapSettings *map ) { whileBlocking( mActionEnableShadows )->setChecked( map->shadowSettings().renderShadows() ); whileBlocking( mActionEnableEyeDome )->setChecked( map->eyeDomeLightingEnabled() ); + whileBlocking( mActionEnableAmbientOcclusion )->setChecked( map->ambientOcclusionSettings().isEnabled() ); whileBlocking( mActionSync2DNavTo3D )->setChecked( map->viewSyncMode().testFlag( Qgis::ViewSyncModeFlag::Sync2DTo3D ) ); whileBlocking( mActionSync3DNavTo2D )->setChecked( map->viewSyncMode().testFlag( Qgis::ViewSyncModeFlag::Sync3DTo2D ) ); whileBlocking( mShowFrustumPolyogon )->setChecked( map->viewFrustumVisualizationEnabled() ); @@ -452,6 +464,7 @@ void Qgs3DMapCanvasWidget::configure() whileBlocking( mActionEnableShadows )->setChecked( map->shadowSettings().renderShadows() ); whileBlocking( mActionEnableEyeDome )->setChecked( map->eyeDomeLightingEnabled() ); + whileBlocking( mActionEnableAmbientOcclusion )->setChecked( map->ambientOcclusionSettings().isEnabled() ); whileBlocking( mActionSync2DNavTo3D )->setChecked( map->viewSyncMode().testFlag( Qgis::ViewSyncModeFlag::Sync2DTo3D ) ); whileBlocking( mActionSync3DNavTo2D )->setChecked( map->viewSyncMode().testFlag( Qgis::ViewSyncModeFlag::Sync3DTo2D ) ); whileBlocking( mShowFrustumPolyogon )->setChecked( map->viewFrustumVisualizationEnabled() ); diff --git a/src/app/3d/qgs3dmapcanvaswidget.h b/src/app/3d/qgs3dmapcanvaswidget.h index ad0f485d8e7..66d11b9fe87 100644 --- a/src/app/3d/qgs3dmapcanvaswidget.h +++ b/src/app/3d/qgs3dmapcanvaswidget.h @@ -108,6 +108,7 @@ class APP_EXPORT Qgs3DMapCanvasWidget : public QWidget QToolButton *mBtnMapThemes = nullptr; QAction *mActionEnableShadows = nullptr; QAction *mActionEnableEyeDome = nullptr; + QAction *mActionEnableAmbientOcclusion = nullptr; QAction *mActionSync2DNavTo3D = nullptr; QAction *mActionSync3DNavTo2D = nullptr; QAction *mShowFrustumPolyogon = nullptr; diff --git a/src/app/3d/qgs3dmapconfigwidget.cpp b/src/app/3d/qgs3dmapconfigwidget.cpp index c07f9d077b2..dc323177bf0 100644 --- a/src/app/3d/qgs3dmapconfigwidget.cpp +++ b/src/app/3d/qgs3dmapconfigwidget.cpp @@ -33,6 +33,7 @@ #include "qgssettings.h" #include "qgsskyboxrenderingsettingswidget.h" #include "qgsshadowrenderingsettingswidget.h" +#include "qgsambientocclusionsettingswidget.h" #include "qgs3dmapcanvas.h" #include "qgs3dmapscene.h" #include "qgs3daxis.h" @@ -245,6 +246,9 @@ Qgs3DMapConfigWidget::Qgs3DMapConfigWidget( Qgs3DMapSettings *map, QgsMapCanvas mDebugDepthMapGroupBox->setChecked( map->debugDepthMapEnabled() ); mDebugDepthMapCornerComboBox->setCurrentIndex( static_cast( map->debugDepthMapCorner() ) ); mDebugDepthMapSizeSpinBox->setValue( map->debugDepthMapSize() ); + + // Ambient occlusion + mAmbientOcclusionSettingsWidget->setAmbientOcclusionSettings( map->ambientOcclusionSettings() ); } Qgs3DMapConfigWidget::~Qgs3DMapConfigWidget() @@ -392,6 +396,8 @@ void Qgs3DMapConfigWidget::apply() mMap->setEyeDomeLightingStrength( edlStrengthSpinBox->value() ); mMap->setEyeDomeLightingDistance( edlDistanceSpinBox->value() ); + mMap->setAmbientOcclusionSettings( mAmbientOcclusionSettingsWidget->toAmbientOcclusionSettings() ); + Qgis::ViewSyncModeFlags viewSyncMode; viewSyncMode.setFlag( Qgis::ViewSyncModeFlag::Sync2DTo3D, mSync2DTo3DCheckbox->isChecked() ); viewSyncMode.setFlag( Qgis::ViewSyncModeFlag::Sync3DTo2D, mSync3DTo2DCheckbox->isChecked() ); diff --git a/src/app/3d/qgsambientocclusionsettingswidget.cpp b/src/app/3d/qgsambientocclusionsettingswidget.cpp new file mode 100644 index 00000000000..9658cba4677 --- /dev/null +++ b/src/app/3d/qgsambientocclusionsettingswidget.cpp @@ -0,0 +1,48 @@ +/*************************************************************************** + qgsambientocclusionsettingswidget.cpp + -------------------------------------- + Date : June 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +#include "qgsambientocclusionsettingswidget.h" + +QgsAmbientOcclusionSettingsWidget::QgsAmbientOcclusionSettingsWidget( QWidget *parent ) + : QWidget( parent ) +{ + setupUi( this ); + + mIntensitySpinBox->setToolTip( tr( "The strength of the shading applied, bigger values means more pronounced and darker colors." ) ); + mRadiusSpinBox->setToolTip( tr( "The radius of the neighborhood: bigger values mean objects further away will add to the occlusion." ) ); + mThresholdSpinBox->setToolTip( tr( "Only apply occlusion effect when at least the specified amount of neighboring points is occluded." ) ); + + mIntensitySpinBox->setClearValue( 0.5 ); + mRadiusSpinBox->setClearValue( 25. ); + mThresholdSpinBox->setClearValue( 50 ); +} + +void QgsAmbientOcclusionSettingsWidget::setAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &settings ) +{ + mAmbientOcclusionGroupBox->setChecked( settings.isEnabled() ); + mIntensitySpinBox->setValue( settings.intensity() ); + mRadiusSpinBox->setValue( settings.radius() ); + mThresholdSpinBox->setValue( settings.threshold() * 100 ); +} + +QgsAmbientOcclusionSettings QgsAmbientOcclusionSettingsWidget::toAmbientOcclusionSettings() +{ + QgsAmbientOcclusionSettings settings; + settings.setEnabled( mAmbientOcclusionGroupBox->isChecked() ); + settings.setIntensity( mIntensitySpinBox->value() ); + settings.setRadius( mRadiusSpinBox->value() ); + settings.setThreshold( mThresholdSpinBox->value() / 100. ); + return settings; +} diff --git a/src/app/3d/qgsambientocclusionsettingswidget.h b/src/app/3d/qgsambientocclusionsettingswidget.h new file mode 100644 index 00000000000..22af82abe62 --- /dev/null +++ b/src/app/3d/qgsambientocclusionsettingswidget.h @@ -0,0 +1,35 @@ +/*************************************************************************** + qgsambientocclusionsettingswidget.h + -------------------------------------- + Date : Juin 2022 + Copyright : (C) 2022 by Belgacem Nedjima + Email : belgacem dot nedjima at gmail dot com + *************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ +#ifndef QGSAMBIENTOCCLUSIONSETTINGSWIDGET_H +#define QGSAMBIENTOCCLUSIONSETTINGSWIDGET_H + +#include "ui_ambientocclusionsettingswidget.h" + +#include "qgsambientocclusionsettings.h" + +class QgsAmbientOcclusionSettingsWidget : public QWidget, private Ui::QgsAmbientOcclusionSettingsWidget +{ + Q_OBJECT + + public: + explicit QgsAmbientOcclusionSettingsWidget( QWidget *parent = nullptr ); + + //! Sets the ambient occlusion settings in the current widget UI + void setAmbientOcclusionSettings( const QgsAmbientOcclusionSettings &settings ); + //! Returns the ambient occlusion settings from the widget UI + QgsAmbientOcclusionSettings toAmbientOcclusionSettings(); +}; + +#endif // QGSAMBIENTOCCLUSIONSETTINGSWIDGET_H diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index c52e0baee81..4919d53ce1c 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -358,6 +358,7 @@ if (WITH_3D) layout/qgslayout3dmapwidget.cpp 3d/qgsskyboxrenderingsettingswidget.cpp 3d/qgsshadowrenderingsettingswidget.cpp + 3d/qgsambientocclusionsettingswidget.cpp 3d/qgspointcloud3dsymbolwidget.cpp 3d/qgspointcloudlayer3drendererwidget.cpp 3d/qgs3dmapcanvaswidget.cpp diff --git a/src/ui/3d/ambientocclusionsettingswidget.ui b/src/ui/3d/ambientocclusionsettingswidget.ui new file mode 100644 index 00000000000..a36fe4eb215 --- /dev/null +++ b/src/ui/3d/ambientocclusionsettingswidget.ui @@ -0,0 +1,140 @@ + + + QgsAmbientOcclusionSettingsWidget + + + + 0 + 0 + 400 + 185 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Ambient Occlusion + + + true + + + false + + + + + + Radius + + + + + + + Intensity + + + + + + + map units + + + 1 + + + 0.100000000000000 + + + 1000.000000000000000 + + + 1.000000000000000 + + + 25.000000000000000 + + + + + + + 1 + + + 0.100000000000000 + + + 3.000000000000000 + + + 0.100000000000000 + + + 0.500000000000000 + + + + + + + Occlusion Threshold + + + + + + + % + + + 95 + + + 5 + + + 50 + + + + + + + + + + + QgsDoubleSpinBox + QDoubleSpinBox +
qgsdoublespinbox.h
+
+ + QgsSpinBox + QSpinBox +
qgsspinbox.h
+
+
+ + mAmbientOcclusionGroupBox + mRadiusSpinBox + mIntensitySpinBox + + + +
diff --git a/src/ui/3d/map3dconfigwidget.ui b/src/ui/3d/map3dconfigwidget.ui index 64b5d59da6e..e1c40e51958 100644 --- a/src/ui/3d/map3dconfigwidget.ui +++ b/src/ui/3d/map3dconfigwidget.ui @@ -179,7 +179,7 @@ - 3 + 4 @@ -206,7 +206,7 @@ 0 0 280 - 301 + 283 @@ -417,7 +417,7 @@ 0 0 77 - 47 + 43 @@ -500,8 +500,8 @@ 0 0 - 151 - 47 + 149 + 43 @@ -590,8 +590,8 @@ 0 0 - 816 - 649 + 335 + 488 @@ -802,8 +802,8 @@ 0 0 - 802 - 732 + 803 + 706 @@ -825,65 +825,7 @@ Advanced Settings - - - - Debug Shadow Map - - - true - - - false - - - - - - - - - Corner - - - - - - - Size - - - - - - - 1.000000000000000 - - - 0.100000000000000 - - - 0.100000000000000 - - - - - - - - - Qt::Vertical - - - - 20 - 0 - - - - - Debug Depth Map @@ -1027,12 +969,12 @@ + + false + Show debug overlay - - false - @@ -1065,6 +1007,51 @@ + + + + Debug Shadow Map + + + true + + + false + + + + + + + + + Corner + + + + + + + Size + + + + + + + 1.000000000000000 + + + 0.100000000000000 + + + 0.100000000000000 + + + + + + @@ -1117,6 +1104,22 @@ + + + + Qt::Vertical + + + + 20 + 0 + + + + + + + @@ -1174,6 +1177,12 @@
qgslightswidget.h
1 + + QgsAmbientOcclusionSettingsWidget + QWidget +
qgsambientocclusionsettingswidget.h
+ 1 +
m3DOptionsListWidget