QGIS API Documentation  3.16.0-Hannover (43b64b13f3)
qgsshadowrenderingframegraph.cpp
Go to the documentation of this file.
1 /***************************************************************************
2  qgsshadowrenderingframegraph.cpp
3  --------------------------------------
4  Date : August 2020
5  Copyright : (C) 2020 by Belgacem Nedjima
6  Email : gb underscore nedjima at esi dot dz
7  ***************************************************************************
8  * *
9  * This program is free software; you can redistribute it and/or modify *
10  * it under the terms of the GNU General Public License as published by *
11  * the Free Software Foundation; either version 2 of the License, or *
12  * (at your option) any later version. *
13  * *
14  ***************************************************************************/
15 
17 
19 #include "qgscameracontroller.h"
20 #include "qgsrectangle.h"
22 #include "qgspreviewquad.h"
23 
24 Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructTexturesPreviewPass()
25 {
26  mPreviewLayerFilter = new Qt3DRender::QLayerFilter;
27  mPreviewLayerFilter->addLayer( mPreviewLayer );
28 
29  mPreviewRenderStateSet = new Qt3DRender::QRenderStateSet( mPreviewLayerFilter );
30  mPreviewDepthTest = new Qt3DRender::QDepthTest;
31  mPreviewDepthTest->setDepthFunction( Qt3DRender::QDepthTest::Always );
32  mPreviewRenderStateSet->addRenderState( mPreviewDepthTest );
33  mPreviewCullFace = new Qt3DRender::QCullFace;
34  mPreviewCullFace->setMode( Qt3DRender::QCullFace::NoCulling );
35  mPreviewRenderStateSet->addRenderState( mPreviewCullFace );
36 
37  return mPreviewLayerFilter;
38 }
39 
40 Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructForwardRenderPass()
41 {
42  mForwardRenderLayerFilter = new Qt3DRender::QLayerFilter( mMainCameraSelector );
43  mForwardRenderLayerFilter->addLayer( mForwardRenderLayer );
44 
45  mRenderCapture = new Qt3DRender::QRenderCapture( mForwardRenderLayerFilter );
46 
47  // TODO: make the width and height change dynamically as the 3D viewer is resized
48  int width = 1024;
49  int height = 768;
50 
51  mForwardColorTexture = new Qt3DRender::QTexture2D;
52  mForwardColorTexture->setWidth( width );
53  mForwardColorTexture->setHeight( height );
54  mForwardColorTexture->setFormat( Qt3DRender::QTexture2D::TextureFormat::RGBA16F );
55  mForwardColorTexture->setGenerateMipMaps( false );
56  mForwardColorTexture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
57  mForwardColorTexture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
58  mForwardColorTexture->wrapMode()->setX( Qt3DRender::QTextureWrapMode::ClampToEdge );
59  mForwardColorTexture->wrapMode()->setY( Qt3DRender::QTextureWrapMode::ClampToEdge );
60 
61  mForwardDepthTexture = new Qt3DRender::QTexture2D;
62  mForwardDepthTexture->setWidth( width );
63  mForwardDepthTexture->setHeight( height );
64  mForwardDepthTexture->setFormat( Qt3DRender::QTexture2D::TextureFormat::DepthFormat );
65  mForwardDepthTexture->setGenerateMipMaps( false );
66  mForwardDepthTexture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
67  mForwardDepthTexture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
68  mForwardDepthTexture->wrapMode()->setX( Qt3DRender::QTextureWrapMode::ClampToEdge );
69  mForwardDepthTexture->wrapMode()->setY( Qt3DRender::QTextureWrapMode::ClampToEdge );
70  mForwardDepthTexture->setComparisonFunction( Qt3DRender::QTexture2D::ComparisonFunction::CompareLessEqual );
71  mForwardDepthTexture->setComparisonMode( Qt3DRender::QTexture2D::ComparisonMode::CompareRefToTexture );
72 
73  mForwardRenderTarget = new Qt3DRender::QRenderTarget;
74  mForwardRenderTargetDepthOutput = new Qt3DRender::QRenderTargetOutput;
75  mForwardRenderTargetDepthOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Depth );
76  mForwardRenderTargetDepthOutput->setTexture( mForwardDepthTexture );
77  mForwardRenderTarget->addOutput( mForwardRenderTargetDepthOutput );
78  mForwardRenderTargetColorOutput = new Qt3DRender::QRenderTargetOutput;
79  mForwardRenderTargetColorOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Color0 );
80  mForwardRenderTargetColorOutput->setTexture( mForwardColorTexture );
81  mForwardRenderTarget->addOutput( mForwardRenderTargetColorOutput );
82 
83  mForwardRenderTargetSelector = new Qt3DRender::QRenderTargetSelector( mForwardRenderLayerFilter );
84  mForwardRenderTargetSelector->setTarget( mForwardRenderTarget );
85 
86  mForwardClearBuffers = new Qt3DRender::QClearBuffers( mForwardRenderTargetSelector );
87  mForwardClearBuffers->setClearColor( QColor::fromRgbF( 0.0, 1.0, 0.0, 1.0 ) );
88  mForwardClearBuffers->setBuffers( Qt3DRender::QClearBuffers::ColorDepthBuffer );
89 
90  mFrustumCulling = new Qt3DRender::QFrustumCulling;
91  mFrustumCulling->setParent( mForwardClearBuffers );
92 
93  return mForwardRenderLayerFilter;
94 }
95 
96 Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructShadowRenderPass()
97 {
98  mShadowSceneEntitiesFilter = new Qt3DRender::QLayerFilter;
99  mShadowSceneEntitiesFilter->addLayer( mCastShadowsLayer );
100 
101  mShadowMapTexture = new Qt3DRender::QTexture2D;
102  mShadowMapTexture->setWidth( mShadowMapResolution );
103  mShadowMapTexture->setHeight( mShadowMapResolution );
104  mShadowMapTexture->setFormat( Qt3DRender::QTexture2D::TextureFormat::D32F );
105  mShadowMapTexture->setGenerateMipMaps( false );
106  mShadowMapTexture->setMagnificationFilter( Qt3DRender::QTexture2D::Linear );
107  mShadowMapTexture->setMinificationFilter( Qt3DRender::QTexture2D::Linear );
108  mShadowMapTexture->wrapMode()->setX( Qt3DRender::QTextureWrapMode::ClampToEdge );
109  mShadowMapTexture->wrapMode()->setY( Qt3DRender::QTextureWrapMode::ClampToEdge );
110 
111  mShadowRenderTarget = new Qt3DRender::QRenderTarget;
112  mShadowRenderTargetOutput = new Qt3DRender::QRenderTargetOutput;
113  mShadowRenderTargetOutput->setAttachmentPoint( Qt3DRender::QRenderTargetOutput::Depth );
114  mShadowRenderTargetOutput->setTexture( mShadowMapTexture );
115  mShadowRenderTarget->addOutput( mShadowRenderTargetOutput );
116 
117  mShadowRenderTargetSelector = new Qt3DRender::QRenderTargetSelector( mShadowSceneEntitiesFilter );
118  mShadowRenderTargetSelector->setTarget( mShadowRenderTarget );
119 
120  mShadowClearBuffers = new Qt3DRender::QClearBuffers( mShadowRenderTargetSelector );
121  mShadowClearBuffers ->setBuffers( Qt3DRender::QClearBuffers::BufferType::ColorDepthBuffer );
122 
123  mShadowRenderStateSet = new Qt3DRender::QRenderStateSet( mShadowClearBuffers );
124  mShadowDepthTest = new Qt3DRender::QDepthTest;
125  mShadowDepthTest->setDepthFunction( Qt3DRender::QDepthTest::Less );
126  mShadowRenderStateSet->addRenderState( mShadowDepthTest );
127  mShadowCullFace = new Qt3DRender::QCullFace;
128  mShadowCullFace->setMode( Qt3DRender::QCullFace::NoCulling );
129  mShadowRenderStateSet->addRenderState( mShadowCullFace );
130 
131  return mShadowSceneEntitiesFilter;
132 }
133 
134 Qt3DRender::QFrameGraphNode *QgsShadowRenderingFrameGraph::constructPostprocessingPass()
135 {
136  mPostprocessPassLayerFilter = new Qt3DRender::QLayerFilter;
137  mPostprocessPassLayerFilter->addLayer( mPostprocessPassLayer );
138 
139  mPostprocessClearBuffers = new Qt3DRender::QClearBuffers( mPostprocessPassLayerFilter );
140  mPostprocessClearBuffers->setClearColor( QColor::fromRgbF( 0.0f, 0.0f, 0.0f ) );
141 
142 
143  return mPostprocessPassLayerFilter;
144 }
145 
146 QgsShadowRenderingFrameGraph::QgsShadowRenderingFrameGraph( QWindow *window, Qt3DRender::QCamera *mainCamera, Qt3DCore::QEntity *root )
147 {
148  mRootEntity = root;
149  mMainCamera = mainCamera;
150  mLightCamera = new Qt3DRender::QCamera;
151 
152  mPostprocessPassLayer = new Qt3DRender::QLayer;
153  mPreviewLayer = new Qt3DRender::QLayer;
154  mCastShadowsLayer = new Qt3DRender::QLayer;
155  mForwardRenderLayer = new Qt3DRender::QLayer;
156 
157 #if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)
158  mPostprocessPassLayer->setRecursive( true );
159  mPreviewLayer->setRecursive( true );
160  mCastShadowsLayer->setRecursive( true );
161  mForwardRenderLayer->setRecursive( true );
162 #endif
163 
164  mRenderSurfaceSelector = new Qt3DRender::QRenderSurfaceSelector;
165  mRenderSurfaceSelector->setSurface( window );
166 
167  mMainViewPort = new Qt3DRender::QViewport( mRenderSurfaceSelector );
168  mMainViewPort->setNormalizedRect( QRectF( 0.0f, 0.0f, 1.0f, 1.0f ) );
169 
170  mMainCameraSelector = new Qt3DRender::QCameraSelector( mMainViewPort );
171  mMainCameraSelector->setCamera( mMainCamera );
172 
173  // Forward render
174  Qt3DRender::QFrameGraphNode *forwardRenderPass = constructForwardRenderPass();
175  forwardRenderPass->setParent( mMainCameraSelector );
176 
177  // shadow rendering pass
178  mLightCameraSelector = new Qt3DRender::QCameraSelector( mMainViewPort );
179  mLightCameraSelector->setCamera( mLightCamera );
180 
181  Qt3DRender::QFrameGraphNode *shadowRenderPass = constructShadowRenderPass();
182  shadowRenderPass->setParent( mLightCameraSelector );
183 
184  // post process
185  Qt3DRender::QFrameGraphNode *postprocessingPass = constructPostprocessingPass();
186  postprocessingPass->setParent( mLightCameraSelector );
187 
188 
189  mPostprocessingEntity = new QgsPostprocessingEntity( this, mRootEntity );
190  mPostprocessingEntity->addComponent( mPostprocessPassLayer );
191 
192  // textures preview pass
193  Qt3DRender::QFrameGraphNode *previewPass = constructTexturesPreviewPass();
194  previewPass->setParent( mMainViewPort );
195 }
196 
197 void QgsShadowRenderingFrameGraph::addTexturePreviewOverlay( Qt3DRender::QTexture2D *texture, const QPointF &centerNDC, const QSizeF &size, QVector<Qt3DRender::QParameter *> additionalShaderParameters )
198 {
199  QgsPreviewQuad *previewQuad = new QgsPreviewQuad( texture, centerNDC, size, additionalShaderParameters );
200  previewQuad->addComponent( mPreviewLayer );
201  previewQuad->setParent( mRootEntity );
202 }
203 
204 QVector3D WorldPosFromDepth( QMatrix4x4 projMatrixInv, QMatrix4x4 viewMatrixInv, float texCoordX, float texCoordY, float depth )
205 {
206  float z = depth * 2.0 - 1.0;
207 
208  QVector4D clipSpacePosition( texCoordX * 2.0 - 1.0, texCoordY * 2.0 - 1.0, z, 1.0 );
209  QVector4D viewSpacePosition = projMatrixInv * clipSpacePosition;
210 
211  // Perspective division
212  viewSpacePosition /= viewSpacePosition.w();
213  QVector4D worldSpacePosition = viewMatrixInv * viewSpacePosition;
214  worldSpacePosition /= worldSpacePosition.w();
215 
216  return QVector3D( worldSpacePosition.x(), worldSpacePosition.y(), worldSpacePosition.z() );
217 }
218 
219 // computes the portion of the Y=y plane the camera is looking at
220 void calculateViewExtent( Qt3DRender::QCamera *camera, float shadowRenderingDistance, float y, float &minX, float &maxX, float &minY, float &maxY, float &minZ, float &maxZ )
221 {
222  QVector3D cameraPos = camera->position();
223  QMatrix4x4 projectionMatrix = camera->projectionMatrix();
224  QMatrix4x4 viewMatrix = camera->viewMatrix();
225  QMatrix4x4 projectionMatrixInv = projectionMatrix.inverted();
226  QMatrix4x4 viewMatrixInv = viewMatrix.inverted();
227  float depth = 1.0f;
228  QVector4D viewCenter = viewMatrix * QVector4D( camera->viewCenter(), 1.0f );
229  viewCenter /= viewCenter.w();
230  viewCenter = projectionMatrix * viewCenter;
231  viewCenter /= viewCenter.w();
232  depth = viewCenter.z();
233  QVector<QVector3D> viewFrustumPoints =
234  {
235  QVector3D( 0.0f, 0.0f, depth ),
236  QVector3D( 0.0f, 1.0f, depth ),
237  QVector3D( 1.0f, 0.0f, depth ),
238  QVector3D( 1.0f, 1.0f, depth )
239  };
240  maxX = std::numeric_limits<float>::lowest();
241  maxY = std::numeric_limits<float>::lowest();
242  maxZ = std::numeric_limits<float>::lowest();
243  minX = std::numeric_limits<float>::max();
244  minY = std::numeric_limits<float>::max();
245  minZ = std::numeric_limits<float>::max();
246  for ( int i = 0; i < viewFrustumPoints.size(); ++i )
247  {
248  // convert from view port space to world space
249  viewFrustumPoints[i] = WorldPosFromDepth(
250  projectionMatrixInv, viewMatrixInv,
251  viewFrustumPoints[i].x(), viewFrustumPoints[i].y(), viewFrustumPoints[i].z() );
252  minX = std::min( minX, viewFrustumPoints[i].x() );
253  maxX = std::max( maxX, viewFrustumPoints[i].x() );
254  minY = std::min( minY, viewFrustumPoints[i].y() );
255  maxY = std::max( maxY, viewFrustumPoints[i].y() );
256  minZ = std::min( minZ, viewFrustumPoints[i].z() );
257  maxZ = std::max( maxZ, viewFrustumPoints[i].z() );
258  // find the intersection between the line going from cameraPos to the frustum quad point
259  // and the horizontal plane Y=y
260  // if the intersection is on the back side of the viewing panel we get a point that is
261  // shadowRenderingDistance units in front of the camera
262  QVector3D pt = cameraPos;
263  QVector3D vect = ( viewFrustumPoints[i] - pt ).normalized();
264  float t = ( y - pt.y() ) / vect.y();
265  if ( t < 0 )
266  t = shadowRenderingDistance;
267  else
268  t = std::min( t, shadowRenderingDistance );
269  viewFrustumPoints[i] = pt + t * vect;
270  minX = std::min( minX, viewFrustumPoints[i].x() );
271  maxX = std::max( maxX, viewFrustumPoints[i].x() );
272  minY = std::min( minY, viewFrustumPoints[i].y() );
273  maxY = std::max( maxY, viewFrustumPoints[i].y() );
274  minZ = std::min( minZ, viewFrustumPoints[i].z() );
275  maxZ = std::max( maxZ, viewFrustumPoints[i].z() );
276  }
277 }
278 
279 void QgsShadowRenderingFrameGraph::setupDirectionalLight( const QgsDirectionalLightSettings &light, float maximumShadowRenderingDistance )
280 {
281  float minX, maxX, minY, maxY, minZ, maxZ;
282  QVector3D lookingAt = mMainCamera->viewCenter();
283  float d = 2 * ( mMainCamera->position() - mMainCamera->viewCenter() ).length();
284 
285  QVector3D vertical = QVector3D( 0.0f, d, 0.0f );
286  QVector3D lightDirection = QVector3D( light.direction().x(), light.direction().y(), light.direction().z() ).normalized();
287  calculateViewExtent( mMainCamera, maximumShadowRenderingDistance, lookingAt.y(), minX, maxX, minY, maxY, minZ, maxZ );
288 
289  lookingAt = QVector3D( 0.5 * ( minX + maxX ), mMainCamera->viewCenter().y(), 0.5 * ( minZ + maxZ ) );
290  QVector3D lightPosition = lookingAt + vertical;
291  mLightCamera->setPosition( lightPosition );
292  mLightCamera->setViewCenter( lookingAt );
293  mLightCamera->setUpVector( QVector3D( 0.0f, 1.0f, 0.0f ) );
294  mLightCamera->rotateAboutViewCenter( QQuaternion::rotationTo( vertical.normalized(), -lightDirection.normalized() ) );
295 
296  mLightCamera->setProjectionType( Qt3DRender::QCameraLens::ProjectionType::OrthographicProjection );
297  mLightCamera->lens()->setOrthographicProjection(
298  - 0.7 * ( maxX - minX ), 0.7 * ( maxX - minX ),
299  - 0.7 * ( maxZ - minZ ), 0.7 * ( maxZ - minZ ),
300  1.0f, 2 * ( lookingAt - lightPosition ).length() );
301 
302  mPostprocessingEntity->setupShadowRenderingExtent( minX, maxX, minZ, maxZ );
303  mPostprocessingEntity->setupDirectionalLight( lightPosition, lightDirection );
304 }
305 
306 void QgsShadowRenderingFrameGraph::setClearColor( const QColor &clearColor )
307 {
308  mForwardClearBuffers->setClearColor( clearColor );
309 }
310 
312 {
313  mShadowRenderingEnabled = enabled;
314  mPostprocessingEntity->setShadowRenderingEnabled( mShadowRenderingEnabled );
315  if ( mShadowRenderingEnabled )
316  mShadowSceneEntitiesFilter->setEnabled( true );
317  else
318  mShadowSceneEntitiesFilter->setEnabled( false );
319 }
320 
322 {
323  mShadowBias = shadowBias;
324  mPostprocessingEntity->setShadowBias( mShadowBias );
325 }
326 
328 {
329  mShadowMapResolution = resolution;
330  mShadowMapTexture->setWidth( mShadowMapResolution );
331  mShadowMapTexture->setHeight( mShadowMapResolution );
332 }
333 
335 {
336  if ( enabled == mFrustumCullingEnabled )
337  return;
338  mFrustumCullingEnabled = enabled;
339  if ( mFrustumCullingEnabled )
340  mFrustumCulling->setParent( mForwardClearBuffers );
341  else
342  mFrustumCulling->setParent( ( Qt3DCore::QNode * )nullptr );
343 }
QgsShadowRenderingFrameGraph::setShadowBias
void setShadowBias(float shadowBias)
Sets the shadow bias value.
Definition: qgsshadowrenderingframegraph.cpp:321
qgspreviewquad.h
QgsShadowRenderingFrameGraph::QgsShadowRenderingFrameGraph
QgsShadowRenderingFrameGraph(QWindow *window, Qt3DRender::QCamera *mainCamera, Qt3DCore::QEntity *root)
Constructor.
Definition: qgsshadowrenderingframegraph.cpp:146
qgsrectangle.h
QgsVector3D::y
double y() const
Returns Y coordinate.
Definition: qgsvector3d.h:51
calculateViewExtent
void calculateViewExtent(Qt3DRender::QCamera *camera, float shadowRenderingDistance, float y, float &minX, float &maxX, float &minY, float &maxY, float &minZ, float &maxZ)
Definition: qgsshadowrenderingframegraph.cpp:220
QgsShadowRenderingFrameGraph::addTexturePreviewOverlay
void addTexturePreviewOverlay(Qt3DRender::QTexture2D *texture, const QPointF &centerNDC, const QSizeF &size, QVector< Qt3DRender::QParameter * > additionalShaderParameters=QVector< Qt3DRender::QParameter * >())
Adds an preview entity that shows a texture in real time for debugging purposes.
Definition: qgsshadowrenderingframegraph.cpp:197
QgsPostprocessingEntity::setupDirectionalLight
void setupDirectionalLight(QVector3D position, QVector3D direction)
Sets up a directional light that is used to render shadows.
Definition: qgspostprocessingentity.cpp:168
QgsShadowRenderingFrameGraph::shadowBias
float shadowBias() const
Returns the shadow bias value.
Definition: qgsshadowrenderingframegraph.h:104
QgsShadowRenderingFrameGraph::mainCamera
Qt3DRender::QCamera * mainCamera()
Returns the main camera.
Definition: qgsshadowrenderingframegraph.h:82
qgsshadowrenderingframegraph.h
QgsPostprocessingEntity::setupShadowRenderingExtent
void setupShadowRenderingExtent(float minX, float maxX, float minZ, float maxZ)
Sets the parts of the scene where objects cast shadows.
Definition: qgspostprocessingentity.cpp:160
QgsVector3D::z
double z() const
Returns Z coordinate.
Definition: qgsvector3d.h:53
qgscameracontroller.h
QgsShadowRenderingFrameGraph::setFrustumCullingEnabled
void setFrustumCullingEnabled(bool enabled)
Sets whether frustum culling is enabled.
Definition: qgsshadowrenderingframegraph.cpp:334
QgsDirectionalLightSettings::direction
QgsVector3D direction() const
Returns the direction of the light in degrees.
Definition: qgsdirectionallightsettings.h:39
QgsPostprocessingEntity::setShadowBias
void setShadowBias(float shadowBias)
Sets the shadow bias value.
Definition: qgspostprocessingentity.cpp:179
QgsPostprocessingEntity::setShadowRenderingEnabled
void setShadowRenderingEnabled(bool enabled)
Sets whether shadow rendering is enabled.
Definition: qgspostprocessingentity.cpp:174
QgsShadowRenderingFrameGraph::setupDirectionalLight
void setupDirectionalLight(const QgsDirectionalLightSettings &light, float maximumShadowRenderingDistance)
Sets shadow rendering to use a directional light.
Definition: qgsshadowrenderingframegraph.cpp:279
qgspostprocessingentity.h
QgsShadowRenderingFrameGraph::setClearColor
void setClearColor(const QColor &clearColor)
Sets the clear color of the scene (background color)
Definition: qgsshadowrenderingframegraph.cpp:306
QgsPostprocessingEntity
3 An entity that is responsible for applying post processing effect Now it is used to make shadows
Definition: qgspostprocessingentity.h:39
QgsShadowRenderingFrameGraph::setShadowRenderingEnabled
void setShadowRenderingEnabled(bool enabled)
Sets whether the shadow rendering is enabled.
Definition: qgsshadowrenderingframegraph.cpp:311
QgsVector3D::x
double x() const
Returns X coordinate.
Definition: qgsvector3d.h:49
qgsdirectionallightsettings.h
QgsPreviewQuad
3 Rectangular quad entity used for debugging depth maps
Definition: qgspreviewquad.h:52
WorldPosFromDepth
QVector3D WorldPosFromDepth(QMatrix4x4 projMatrixInv, QMatrix4x4 viewMatrixInv, float texCoordX, float texCoordY, float depth)
Definition: qgsshadowrenderingframegraph.cpp:204
QgsShadowRenderingFrameGraph::setShadowMapResolution
void setShadowMapResolution(int resolution)
Sets the resolution of the shadow map.
Definition: qgsshadowrenderingframegraph.cpp:327
QgsDirectionalLightSettings
3 Definition of a directional light in a 3D map scene
Definition: qgsdirectionallightsettings.h:33