[3d] Ambient Occlusion (reworked) (#49702)

* Initial implementation

* - Add disabling of blur pass
- Change default parameter for shading factor
- fix some tests

* - Implement bilateral filtering
- Optimize textures
- Fix layout test

* rename SSAO to ambient occlusion
fix ssao settings widget margin

* Remove unused variable

* fix naming and add tooltips

* - Refactor quad entities
- Address Stefanos's suggestions

* Rework SSAO implementation

Previously we based the code on CloudCompare's implementation,
however that did not work too well for us for a couple of reasons:
- the code does not deal well with perspective projection, causing incorrect shading (CC uses orthographic projection)
- there was no range check, so we would be getting false ambient occlusion on larger depth discontinuities (silhouttes)
- banding artifacts as the sampling kernel was not getting rotated
- parameters (shading radius, distance attenuation) that are difficult to understand

The new implementation is based on John Chapman's tutorial and LearnOpenGL page (derived from the original tutorial):
https://john-chapman-graphics.blogspot.com/2013/01/ssao-tutorial.html
https://learnopengl.com/Advanced-Lighting/SSAO

The general approach of the SSAO is the following:
- for each pixel, we pick a couple of random points nearby (64 samples currently)
  and check with the depth buffer whether they are visible from the camera or not.
  The nearby points that are occluded contribute to darkening of the pixel,
  this is saved to a texture
- in the next rendering pass, we blur the texture using 4x4 box. This is because
  in the first step we use 4x4 random noise pattern and it leaves a noticeable noise
  pattern on the screen. This pass gets rid of that noise
- in the post-processing step, the blurred texture is blended with the rendered scene

There are few differences to J.C.'s tutorial and LearnOpenGL page:
- the approches above use normal maps (a texture with a normal vector for each pixel),
  but we don't because we also want to support point clouds that do not have normals
  (at least not by default)
- we use full sphere for sampling instead of hemisphere (which is possible when you
  have normals), so maybe we are getting a bit lower quality / performance
- LearnOpenGL also uses a texture with positions of all pixels - we only use depth map
  to get the original positions (like JC's original code does)

* Clean up ssao parameters and GUI, add intensity parameter

* Add missing Q_OBJECT macro

* Add more missing Q_OBJECT macros

* Add occlusion threshold parameter to control the darkening

With the default threshold of 50%, pixels only start to get darker
when more than half of the neighborhood samples are occluded. That
means flat surfaces should not get any darker. (What we had previously
is an equivalent of having threshold set at 0%)

The downside is that with increased threshold, more subtle occlusions get lost.

* Review from Stefanos

* More review and better defaults

* Clear button fix

Co-authored-by: NEDJIMAbelgacem <gb_nedjima@esi.dz>
This commit is contained in:
Martin Dobias 2022-08-31 06:36:14 -07:00 committed by GitHub
parent cc933eefbc
commit 91100f816b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 1347 additions and 144 deletions

View File

@ -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

View File

@ -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

View File

@ -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();

View File

@ -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();

View File

@ -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 );
}

View File

@ -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;

View File

@ -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 <Qt3DRender/QParameter>
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 ) ) );
}

View File

@ -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

View File

@ -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 <random>
#include <Qt3DRender/QParameter>
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<float> 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 );
}

View File

@ -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

View File

@ -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 <QDomDocument>
#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 );
}

View File

@ -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 <QString>
#include <QMap>
#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

View File

@ -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<float> 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 );
}

View File

@ -16,11 +16,7 @@
#ifndef QGSPOSTPROCESSINGENTITY_H
#define QGSPOSTPROCESSINGENTITY_H
#include <Qt3DCore/QEntity>
#include <Qt3DRender/QTexture>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QCamera>
#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

View File

@ -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 <random>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <Qt3DRender/QAttribute>
#include <Qt3DRender/QBuffer>
#include <Qt3DRender/QGeometry>
typedef Qt3DRender::QAttribute Qt3DQAttribute;
typedef Qt3DRender::QBuffer Qt3DQBuffer;
typedef Qt3DRender::QGeometry Qt3DQGeometry;
#else
#include <Qt3DCore/QAttribute>
#include <Qt3DCore/QBuffer>
#include <Qt3DCore/QGeometry>
typedef Qt3DCore::QAttribute Qt3DQAttribute;
typedef Qt3DCore::QBuffer Qt3DQBuffer;
typedef Qt3DCore::QGeometry Qt3DQGeometry;
#endif
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DRender/QTechnique>
#include <Qt3DRender/QGraphicsApiFilter>
#include <Qt3DRender/QDepthTest>
#include <Qt3DRender/QEffect>
#include <QUrl>
#include <QVector3D>
QgsRenderPassQuad::QgsRenderPassQuad( QNode *parent )
: Qt3DCore::QEntity( parent )
{
Qt3DQGeometry *geom = new Qt3DQGeometry( this );
Qt3DQAttribute *positionAttribute = new Qt3DQAttribute( this );
const QVector<float> 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 );
}

View File

@ -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 <Qt3DCore/QEntity>
#include <Qt3DRender/QTexture>
#include <Qt3DRender/QParameter>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QEffect>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QShaderProgram>
#include <Qt3DRender/QLayer>
#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

View File

@ -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 <Qt3DRender/QAttribute>
@ -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 )

View File

@ -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<QgsPreviewQuad *> 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();

View File

@ -30,5 +30,9 @@
<file>shaders/goochDataDefined.vert</file>
<file>shaders/depth_render.frag</file>
<file>shaders/depth_render.vert</file>
<file>shaders/ssao_factor_render.frag</file>
<file>shaders/ssao_factor_render.vert</file>
<file>shaders/ssao_factor_blur.frag</file>
<file>shaders/ssao_factor_blur.vert</file>
</qresource>
</RCC>

View File

@ -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 );
}
}

View File

@ -2,7 +2,6 @@
in vec3 vertexPosition;
out vec3 worldPosition;
out vec2 texCoord;
void main()

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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));
}

View File

@ -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
);
}

View File

@ -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() );

View File

@ -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;

View File

@ -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<int>( 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() );

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,140 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>QgsAmbientOcclusionSettingsWidget</class>
<widget class="QWidget" name="QgsAmbientOcclusionSettingsWidget">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>400</width>
<height>185</height>
</rect>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<property name="leftMargin">
<number>0</number>
</property>
<property name="topMargin">
<number>0</number>
</property>
<property name="rightMargin">
<number>0</number>
</property>
<property name="bottomMargin">
<number>0</number>
</property>
<item row="0" column="0">
<widget class="QGroupBox" name="mAmbientOcclusionGroupBox">
<property name="title">
<string>Ambient Occlusion</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout">
<item row="0" column="0">
<widget class="QLabel" name="label_3">
<property name="text">
<string>Radius</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label">
<property name="text">
<string>Intensity</string>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QgsDoubleSpinBox" name="mRadiusSpinBox">
<property name="suffix">
<string> map units</string>
</property>
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>1000.000000000000000</double>
</property>
<property name="singleStep">
<double>1.000000000000000</double>
</property>
<property name="value">
<double>25.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mIntensitySpinBox">
<property name="decimals">
<number>1</number>
</property>
<property name="minimum">
<double>0.100000000000000</double>
</property>
<property name="maximum">
<double>3.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.500000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_2">
<property name="text">
<string>Occlusion Threshold</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QgsSpinBox" name="mThresholdSpinBox">
<property name="suffix">
<string>%</string>
</property>
<property name="maximum">
<number>95</number>
</property>
<property name="singleStep">
<number>5</number>
</property>
<property name="value">
<number>50</number>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<customwidgets>
<customwidget>
<class>QgsDoubleSpinBox</class>
<extends>QDoubleSpinBox</extends>
<header>qgsdoublespinbox.h</header>
</customwidget>
<customwidget>
<class>QgsSpinBox</class>
<extends>QSpinBox</extends>
<header>qgsspinbox.h</header>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>mAmbientOcclusionGroupBox</tabstop>
<tabstop>mRadiusSpinBox</tabstop>
<tabstop>mIntensitySpinBox</tabstop>
</tabstops>
<resources/>
<connections/>
</ui>

View File

@ -179,7 +179,7 @@
<item>
<widget class="QStackedWidget" name="m3DOptionsStackedWidget">
<property name="currentIndex">
<number>3</number>
<number>4</number>
</property>
<widget class="QWidget" name="mPageTerrain">
<layout class="QVBoxLayout" name="verticalLayout_61">
@ -206,7 +206,7 @@
<x>0</x>
<y>0</y>
<width>280</width>
<height>301</height>
<height>283</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutTerrain">
@ -417,7 +417,7 @@
<x>0</x>
<y>0</y>
<width>77</width>
<height>47</height>
<height>43</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutLight">
@ -500,8 +500,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>151</width>
<height>47</height>
<width>149</width>
<height>43</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutShadow">
@ -590,8 +590,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>816</width>
<height>649</height>
<width>335</width>
<height>488</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutCameraSkybox">
@ -802,8 +802,8 @@
<rect>
<x>0</x>
<y>0</y>
<width>802</width>
<height>732</height>
<width>803</width>
<height>706</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayoutAdvanced">
@ -825,65 +825,7 @@
<string>Advanced Settings</string>
</property>
<layout class="QGridLayout" name="gridLayout_11">
<item row="2" column="0">
<widget class="QGroupBox" name="mDebugShadowMapGroupBox">
<property name="title">
<string>Debug Shadow Map</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="1">
<widget class="QComboBox" name="mDebugShadowMapCornerComboBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Corner</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mDebugShadowMapSizeSpinBox">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="4" column="0">
<spacer name="verticalSpacerAdvanced">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="mDebugDepthMapGroupBox">
<property name="title">
<string>Debug Depth Map</string>
@ -1027,12 +969,12 @@
</item>
<item row="12" column="0" colspan="2">
<widget class="QCheckBox" name="mDebugOverlayCheckBox">
<property name="visible">
<bool>false</bool>
</property>
<property name="text">
<string>Show debug overlay</string>
</property>
<property name="visible">
<bool>false</bool>
</property>
</widget>
</item>
<item row="3" column="0">
@ -1065,6 +1007,51 @@
</item>
</layout>
</item>
<item row="3" column="0">
<widget class="QGroupBox" name="mDebugShadowMapGroupBox">
<property name="title">
<string>Debug Shadow Map</string>
</property>
<property name="checkable">
<bool>true</bool>
</property>
<property name="checked">
<bool>false</bool>
</property>
<layout class="QGridLayout" name="gridLayout_10">
<item row="0" column="1">
<widget class="QComboBox" name="mDebugShadowMapCornerComboBox"/>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_8">
<property name="text">
<string>Corner</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_10">
<property name="text">
<string>Size</string>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QgsDoubleSpinBox" name="mDebugShadowMapSizeSpinBox">
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.100000000000000</double>
</property>
<property name="value">
<double>0.100000000000000</double>
</property>
</widget>
</item>
</layout>
</widget>
</item>
<item row="1" column="0">
<widget class="QGroupBox" name="edlGroupBox">
<property name="title">
@ -1117,6 +1104,22 @@
</layout>
</widget>
</item>
<item row="5" column="0">
<spacer name="verticalSpacerAdvanced">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>0</height>
</size>
</property>
</spacer>
</item>
<item row="2" column="0">
<widget class="QgsAmbientOcclusionSettingsWidget" name="mAmbientOcclusionSettingsWidget" native="true"/>
</item>
</layout>
</widget>
</item>
@ -1174,6 +1177,12 @@
<header>qgslightswidget.h</header>
<container>1</container>
</customwidget>
<customwidget>
<class>QgsAmbientOcclusionSettingsWidget</class>
<extends>QWidget</extends>
<header>qgsambientocclusionsettingswidget.h</header>
<container>1</container>
</customwidget>
</customwidgets>
<tabstops>
<tabstop>m3DOptionsListWidget</tabstop>