Skip to content

Commit

Permalink
[Backport to release-3_18] fix shadow and EDL not being exported with…
Browse files Browse the repository at this point in the history
… 3D animation tool (#42005)

Backport of #41773
  • Loading branch information
NEDJIMAbelgacem committed Mar 8, 2021
1 parent 55e967b commit e7fb462
Show file tree
Hide file tree
Showing 12 changed files with 191 additions and 196 deletions.
20 changes: 4 additions & 16 deletions src/3d/qgs3dmapscene.cpp
Expand Up @@ -1014,10 +1014,7 @@ void Qgs3DMapScene::onSkyboxSettingsChanged()

void Qgs3DMapScene::onShadowSettingsChanged()
{
QgsWindow3DEngine *windowEngine = dynamic_cast<QgsWindow3DEngine *>( mEngine );
if ( windowEngine == nullptr )
return;
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = windowEngine->shadowRenderingFrameGraph();
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();

QList<QgsDirectionalLightSettings> directionalLights = mMap.directionalLights();
QgsShadowSettings shadowSettings = mMap.shadowSettings();
Expand All @@ -1036,28 +1033,19 @@ void Qgs3DMapScene::onShadowSettingsChanged()

void Qgs3DMapScene::onDebugShadowMapSettingsChanged()
{
QgsWindow3DEngine *windowEngine = dynamic_cast<QgsWindow3DEngine *>( mEngine );
if ( windowEngine == nullptr )
return;
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = windowEngine->shadowRenderingFrameGraph();
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
shadowRenderingFrameGraph->setupShadowMapDebugging( mMap.debugShadowMapEnabled(), mMap.debugShadowMapCorner(), mMap.debugShadowMapSize() );
}

void Qgs3DMapScene::onDebugDepthMapSettingsChanged()
{
QgsWindow3DEngine *windowEngine = dynamic_cast<QgsWindow3DEngine *>( mEngine );
if ( windowEngine == nullptr )
return;
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = windowEngine->shadowRenderingFrameGraph();
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();
shadowRenderingFrameGraph->setupDepthMapDebugging( mMap.debugDepthMapEnabled(), mMap.debugDepthMapCorner(), mMap.debugDepthMapSize() );
}

void Qgs3DMapScene::onEyeDomeShadingSettingsChanged()
{
QgsWindow3DEngine *windowEngine = dynamic_cast<QgsWindow3DEngine *>( mEngine );
if ( windowEngine == nullptr )
return;
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = windowEngine->shadowRenderingFrameGraph();
QgsShadowRenderingFrameGraph *shadowRenderingFrameGraph = mEngine->frameGraph();

bool edlEnabled = mMap.eyeDomeLightingEnabled();
double edlStrength = mMap.eyeDomeLightingStrength();
Expand Down
4 changes: 0 additions & 4 deletions src/3d/qgs3dutils.cpp
Expand Up @@ -153,10 +153,6 @@ bool Qgs3DUtils::exportAnimation( const Qgs3DAnimationSettings &animationSetting
fileName.replace( token, frameNoPaddedLeft );
const QString path = QDir( outputDirectory ).filePath( fileName );

// It would initially return empty rendered image.
// Capturing the initial image and throwing it away fixes that.
// Hopefully we will find a better fix in the future.
Qgs3DUtils::captureSceneImage( engine, scene );
QImage img = Qgs3DUtils::captureSceneImage( engine, scene );

img.save( path );
Expand Down
25 changes: 25 additions & 0 deletions src/3d/qgsabstract3dengine.cpp
Expand Up @@ -15,8 +15,33 @@

#include "qgsabstract3dengine.h"

#include "qgsshadowrenderingframegraph.h"

#include <Qt3DRender/QRenderCapture>

QgsAbstract3DEngine::QgsAbstract3DEngine( QObject *parent )
: QObject( parent )
{

}

void QgsAbstract3DEngine::requestCaptureImage()
{
Qt3DRender::QRenderCaptureReply *captureReply;
captureReply = mFrameGraph->renderCapture()->requestCapture();
connect( captureReply, &Qt3DRender::QRenderCaptureReply::completed, this, [ = ]
{
emit imageCaptured( captureReply->image() );
captureReply->deleteLater();
} );
}

void QgsAbstract3DEngine::setRenderCaptureEnabled( bool enabled )
{
mFrameGraph->setRenderCaptureEnabled( enabled );
}

bool QgsAbstract3DEngine::renderCaptureEnabled() const
{
return mFrameGraph->renderCaptureEnabled();
}
28 changes: 27 additions & 1 deletion src/3d/qgsabstract3dengine.h
Expand Up @@ -38,6 +38,8 @@ namespace Qt3DRender
class QFrameGraphNode;
}

class QgsShadowRenderingFrameGraph;

/**
* \ingroup 3d
* \brief Base class for 3D engine implementation. A 3D engine is responsible for setting up
Expand Down Expand Up @@ -85,7 +87,7 @@ class _3D_EXPORT QgsAbstract3DEngine : public QObject
* The function does not block - when the rendered image is captured, it is returned in imageCaptured() signal.
* Only one image request can be active at a time.
*/
virtual void requestCaptureImage() = 0;
void requestCaptureImage();

/**
* Returns the surface of the engine
Expand All @@ -94,9 +96,33 @@ class _3D_EXPORT QgsAbstract3DEngine : public QObject
*/
virtual QSurface *surface() const = 0;

/**
* Returns the shadow rendering frame graph object used to render the scene
*
* \since QGIS 3.18
*/
QgsShadowRenderingFrameGraph *frameGraph() { return mFrameGraph; }

/**
* Sets whether it will be possible to render to an image
*
* \note for QgsWindow3DEngine render capture will be disabled by default
* and for QgsOffscreen3DEngine it is enabled by default
* \since QGIS 3.18
*/
void setRenderCaptureEnabled( bool enabled );

/**
* Returns whether it will be possible to render to an image
* \since QGIS 3.18
*/
bool renderCaptureEnabled() const;
signals:
//! Emitted after a call to requestCaptureImage() to return the captured image.
void imageCaptured( const QImage &image );

protected:
QgsShadowRenderingFrameGraph *mFrameGraph = nullptr;
};


Expand Down
119 changes: 14 additions & 105 deletions src/3d/qgsoffscreen3dengine.cpp
Expand Up @@ -36,7 +36,6 @@
#include <Qt3DRender/QViewport>
#include <QtGui/QOpenGLContext>


QgsOffscreen3DEngine::QgsOffscreen3DEngine()
{
// Set up the default OpenGL surface format.
Expand Down Expand Up @@ -80,19 +79,24 @@ QgsOffscreen3DEngine::QgsOffscreen3DEngine()
// component must be held by the root of the QEntity tree,
// so it is added to this one. The 3D scene is added as a subtree later,
// in setRootEntity().
mRoot = new Qt3DCore::QEntity();
mRoot = new Qt3DCore::QEntity;
mRenderSettings = new Qt3DRender::QRenderSettings( mRoot );
mRoot->addComponent( mRenderSettings );

mCamera->setParent( mRoot );

// Create the offscreen frame graph, which will manage all of the resources required
// for rendering without a QWindow.
createFrameGraph();
mOffscreenSurface = new QOffscreenSurface();
mOffscreenSurface->setFormat( QSurfaceFormat::defaultFormat() );
mOffscreenSurface->create();

mFrameGraph = new QgsShadowRenderingFrameGraph( mOffscreenSurface, mSize, mCamera, mRoot );
mFrameGraph->setRenderCaptureEnabled( true );
mFrameGraph->setShadowRenderingEnabled( false );
// Set this frame graph to be in use.
// the render settings also sets itself as the parent of mSurfaceSelector
mRenderSettings->setActiveFrameGraph( mSurfaceSelector );
mRenderSettings->setActiveFrameGraph( mFrameGraph->frameGraphRoot() );

// Set the root entity of the engine. This causes the engine to begin running.
mAspectEngine->setRootEntity( Qt3DCore::QEntityPtr( mRoot ) );
Expand All @@ -109,98 +113,18 @@ void QgsOffscreen3DEngine::setSize( QSize s )
{
mSize = s;

mTexture->setSize( mSize.width(), mSize.height() );
mDepthTexture->setSize( mSize.width(), mSize.height() );
mSurfaceSelector->setExternalRenderTargetSize( mSize );

mFrameGraph->setSize( mSize );
mCamera->setAspectRatio( float( mSize.width() ) / float( mSize.height() ) );
}

void QgsOffscreen3DEngine::setClearColor( const QColor &color )
{
mClearBuffers->setClearColor( color );
mFrameGraph->setClearColor( color );
}

void QgsOffscreen3DEngine::setFrustumCullingEnabled( bool enabled )
{
// TODO
Q_UNUSED( enabled )
}

void QgsOffscreen3DEngine::createRenderTarget()
{
mTextureTarget = new Qt3DRender::QRenderTarget;

// 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.
mTextureOutput = new Qt3DRender::QRenderTargetOutput( mTextureTarget );
mTextureOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Color0 );

// Create a texture to render into.
mTexture = new Qt3DRender::QTexture2D( mTextureOutput );
mTexture->setSize( mSize.width(), mSize.height() );
mTexture->setFormat( Qt3DRender::QAbstractTexture::RGB8_UNorm );
mTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear );
mTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear );

// Hook the texture up to our output, and the output up to this object.
mTextureOutput->setTexture( mTexture );
mTextureTarget->addOutput( mTextureOutput );

mDepthTextureOutput = new Qt3DRender::QRenderTargetOutput( mTextureTarget );
mDepthTextureOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Depth );
mDepthTexture = new Qt3DRender::QTexture2D( mDepthTextureOutput );
mDepthTexture->setSize( mSize.width(), mSize.height() );
mDepthTexture->setFormat( Qt3DRender::QAbstractTexture::DepthFormat );
mDepthTexture->setMinificationFilter( Qt3DRender::QAbstractTexture::Linear );
mDepthTexture->setMagnificationFilter( Qt3DRender::QAbstractTexture::Linear );
mDepthTexture->setComparisonFunction( Qt3DRender::QAbstractTexture::CompareLessEqual );
mDepthTexture->setComparisonMode( Qt3DRender::QAbstractTexture::CompareRefToTexture );
// Hook up the depth texture
mDepthTextureOutput->setTexture( mDepthTexture );
mTextureTarget->addOutput( mDepthTextureOutput );
}

void QgsOffscreen3DEngine::createFrameGraph()
{
// Firstly, create the offscreen surface. This will take the place
// of a QWindow, allowing us to render our scene without one.
mOffscreenSurface = new QOffscreenSurface();
mOffscreenSurface->setFormat( QSurfaceFormat::defaultFormat() );
mOffscreenSurface->create();

// Hook it up to the frame graph.
mSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector( mRenderSettings );
mSurfaceSelector->setSurface( mOffscreenSurface );
mSurfaceSelector->setExternalRenderTargetSize( mSize );

// Create a texture to render into. This acts as the buffer that
// holds the rendered image.
mRenderTargetSelector = new Qt3DRender::QRenderTargetSelector( mSurfaceSelector );
createRenderTarget();
// the target selector also sets itself as the parent of mTextureTarget
mRenderTargetSelector->setTarget( mTextureTarget );

// Create a node used for clearing the required buffers.
mClearBuffers = new Qt3DRender::QClearBuffers( mRenderTargetSelector );
mClearBuffers->setClearColor( QColor( 100, 100, 100, 255 ) );
mClearBuffers->setBuffers( Qt3DRender::QClearBuffers::ColorDepthBuffer );

// Create a viewport node. The viewport here just covers the entire render area.
mViewport = new Qt3DRender::QViewport( mRenderTargetSelector );
mViewport->setNormalizedRect( QRectF( 0.0, 0.0, 1.0, 1.0 ) );

// Create a camera selector node, and tell it to use the camera we've ben given.
mCameraSelector = new Qt3DRender::QCameraSelector( mViewport );
mCameraSelector->setCamera( mCamera );

// Add a render capture node to the frame graph.
// This is set as the next child of the render target selector node,
// so that the capture will be taken from the specified render target
// once all other rendering operations have taken place.
mRenderCapture = new Qt3DRender::QRenderCapture( mRenderTargetSelector );
mFrameGraph->setFrustumCullingEnabled( enabled );
}

void QgsOffscreen3DEngine::setRootEntity( Qt3DCore::QEntity *root )
Expand All @@ -213,7 +137,9 @@ void QgsOffscreen3DEngine::setRootEntity( Qt3DCore::QEntity *root )

// Parent the incoming scene root to our current root entity.
mSceneRoot = root;
mSceneRoot->setParent( mAspectEngine->rootEntity().data() );
mSceneRoot->setParent( mRoot );
root->addComponent( mFrameGraph->forwardRenderLayer() );
root->addComponent( mFrameGraph->castShadowsLayer() );
}

Qt3DRender::QRenderSettings *QgsOffscreen3DEngine::renderSettings()
Expand All @@ -235,20 +161,3 @@ QSurface *QgsOffscreen3DEngine::surface() const
{
return mOffscreenSurface;
}

void QgsOffscreen3DEngine::requestCaptureImage()
{
if ( mReply )
{
QgsDebugMsgLevel( QStringLiteral( "already having a pending capture, skipping" ), 2 );
return;
}
mReply = mRenderCapture->requestCapture();
connect( mReply, &Qt3DRender::QRenderCaptureReply::completed, this, [ = ]
{
QImage image = mReply->image();
mReply->deleteLater();
mReply = nullptr;
emit imageCaptured( image );
} );
}
29 changes: 5 additions & 24 deletions src/3d/qgsoffscreen3dengine.h
Expand Up @@ -48,6 +48,8 @@ namespace Qt3DLogic
class QLogicAspect;
}

#include "qgsshadowrenderingframegraph.h"

#define SIP_NO_FILE

/**
Expand Down Expand Up @@ -78,19 +80,14 @@ class _3D_EXPORT QgsOffscreen3DEngine : public QgsAbstract3DEngine
Qt3DRender::QCamera *camera() override;
QSize size() const override;
QSurface *surface() const override;

void requestCaptureImage() override;

private:
void createRenderTarget();
void createFrameGraph();

signals:
//! Emitted after a call to requestCaptureImage() to return the captured image.
void imageCaptured( const QImage &image );
private:

QSize mSize = QSize( 640, 480 );
Qt3DRender::QCamera *mCamera = nullptr;
QOffscreenSurface *mOffscreenSurface = nullptr;
Qt3DRender::QRenderCaptureReply *mReply = nullptr;

// basic Qt3D stuff
Qt3DCore::QAspectEngine *mAspectEngine = nullptr; // The aspect engine, which holds the scene and related aspects.
Expand All @@ -99,22 +96,6 @@ class _3D_EXPORT QgsOffscreen3DEngine : public QgsAbstract3DEngine
Qt3DRender::QRenderSettings *mRenderSettings = nullptr; // The render settings, which control the general rendering behavior.
Qt3DCore::QNode *mSceneRoot = nullptr; // The scene root, which becomes a child of the engine's root entity.
Qt3DCore::QEntity *mRoot = nullptr;

// render target stuff
Qt3DRender::QRenderTarget *mTextureTarget = nullptr;
Qt3DRender::QRenderTargetOutput *mTextureOutput = nullptr;
Qt3DRender::QTexture2D *mTexture = nullptr;
Qt3DRender::QRenderTargetOutput *mDepthTextureOutput = nullptr;
Qt3DRender::QTexture2D *mDepthTexture = nullptr;

// frame graph stuff
Qt3DRender::QRenderSurfaceSelector *mSurfaceSelector = nullptr;
Qt3DRender::QRenderTargetSelector *mRenderTargetSelector = nullptr;
Qt3DRender::QViewport *mViewport = nullptr;
Qt3DRender::QClearBuffers *mClearBuffers = nullptr;
Qt3DRender::QCameraSelector *mCameraSelector = nullptr;
Qt3DRender::QRenderCapture *mRenderCapture = nullptr; // The render capture node, which is appended to the frame graph.

};

#endif // QGSOFFSCREEN3DENGINE_H

0 comments on commit e7fb462

Please sign in to comment.