Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3D] add support for gl_clipdistance (part 1) #57899

Open
wants to merge 21 commits into
base: master
Choose a base branch
from

Conversation

ptitjano
Copy link
Contributor

Description

This PR adds support for gl_clipdistance as suggested by @wonder-sk in #57593 (review)
This allows to properly cut objects which are outside of the 3d scene extent. Contrary to #57593 it does not contain the rotation part. It will be added in a following PR. This is still useful in the general case to propery cut the objects at the border of the scene:

without gl_clipdistance

cut_before

with gl_clipdistance

cut_after

The PR itself is quite simple. 3 changes are needed:

  • Add 4 Qt3DRender::QClipPlane in the framegraph to add gl_clipDistance support
    For each material:
    -call Qgs3DUtils::addBoundingBoxParametersToEffect to define the plane equation as uniform for the equation
  • compute gl_clipistance in each vertex or geometry shader by calling setClipDistance (see shaders/clipplane.inc)

The other changes provide some cleanup in the materials to limit the number of shader files and reduce the maintenance burden.

This should work for all the data sources (raster, vector, mesh, pointcloud, etc..)

@github-actions github-actions bot added this to the 3.40.0 milestone Jun 26, 2024
@ptitjano ptitjano self-assigned this Jun 26, 2024
@ptitjano ptitjano added the 3D Relates to QGIS' 3D engine or rendering label Jun 26, 2024
Copy link

github-actions bot commented Jun 26, 2024

🪟 Windows builds ready!

Windows builds of this PR are available for testing here. Debug symbols for this build are available here.

(Built from commit 0bb1e81)

Copy link

github-actions bot commented Jun 26, 2024

Tests failed for Qt 6

One or more tests failed using the build from commit b3e8f7e

pointcloud_3d_singlecolor (testPointCloudSingleColor)

pointcloud_3d_singlecolor

Test failed at testPointCloudSingleColor at tests/src/3d/testqgspointcloud3drendering.cpp:276

pointcloud_3d_colorramp (testPointCloudAttributeByRamp)

pointcloud_3d_colorramp

Test failed at testPointCloudAttributeByRamp at tests/src/3d/testqgspointcloud3drendering.cpp:316

pointcloud_3d_classification (testPointCloudClassification)

pointcloud_3d_classification

Test failed at testPointCloudClassification at tests/src/3d/testqgspointcloud3drendering.cpp:353

pointcloud_3d_classification_pointsizes (testPointCloudClassificationOverridePointSizes)

pointcloud_3d_classification_pointsizes

Test failed at testPointCloudClassificationOverridePointSizes at tests/src/3d/testqgspointcloud3drendering.cpp:392

pointcloud_3d_filtered_classification (testPointCloudFilteredClassification)

pointcloud_3d_filtered_classification

Test failed at testPointCloudFilteredClassification at tests/src/3d/testqgspointcloud3drendering.cpp:430

pointcloud_3d_filtered_scene_extent (testPointCloudFilteredSceneExtent)

pointcloud_3d_filtered_scene_extent

Test failed at testPointCloudFilteredSceneExtent at tests/src/3d/testqgspointcloud3drendering.cpp:472

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

Copy link

github-actions bot commented Jun 26, 2024

Tests failed for Qt 5

One or more tests failed using the build from commit b3e8f7e

pointcloud_3d_singlecolor (testPointCloudSingleColor)

pointcloud_3d_singlecolor

Test failed at testPointCloudSingleColor at tests/src/3d/testqgspointcloud3drendering.cpp:276

pointcloud_3d_colorramp (testPointCloudAttributeByRamp)

pointcloud_3d_colorramp

Test failed at testPointCloudAttributeByRamp at tests/src/3d/testqgspointcloud3drendering.cpp:316

pointcloud_3d_classification (testPointCloudClassification)

pointcloud_3d_classification

Test failed at testPointCloudClassification at tests/src/3d/testqgspointcloud3drendering.cpp:353

pointcloud_3d_classification_pointsizes (testPointCloudClassificationOverridePointSizes)

pointcloud_3d_classification_pointsizes

Test failed at testPointCloudClassificationOverridePointSizes at tests/src/3d/testqgspointcloud3drendering.cpp:392

pointcloud_3d_filtered_classification (testPointCloudFilteredClassification)

pointcloud_3d_filtered_classification

Test failed at testPointCloudFilteredClassification at tests/src/3d/testqgspointcloud3drendering.cpp:430

pointcloud_3d_filtered_scene_extent (testPointCloudFilteredSceneExtent)

pointcloud_3d_filtered_scene_extent

Test failed at testPointCloudFilteredSceneExtent at tests/src/3d/testqgspointcloud3drendering.cpp:472

The full test report (included comparison of rendered vs expected images) can be found here.

Further documentation on the QGIS test infrastructure can be found in the Developer's Guide.

@@ -179,7 +180,7 @@ class _3D_EXPORT QgsAbstractMaterialSettings SIP_ABSTRACT
* The \a technique argument specifies the rendering technique which will be used with the returned
* material.
*/
virtual Qt3DRender::QMaterial *toMaterial( QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context ) const = 0 SIP_FACTORY;
virtual Qt3DRender::QMaterial *toMaterial( const Qgs3DMapSettings &mapSettings, QgsMaterialSettingsRenderingTechnique technique, const QgsMaterialContext &context ) const = 0 SIP_FACTORY;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@wonder-sk is this safe? Qgs3DMapSettings is a QObject, but AFAICS we are accessing this on non-main threads. (Both in master and more so with this PR).

I'd argue we should be moving the properties required for material / symbol / entity creation to a different class which is cheap to copy and not a QObject, and then pass this new class around for uses like this.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@nyalldawson Do you prefer we create a function in Qgs3DMapSettings to extract a simple struct (with the needed values) to pass as parameter in toMaterial?

This is not used at the moment. It will be used in the following
commits to define the clip plane equations.
There is no need to define a special vertex shader code for this
material, it can use the default case. This way, it reduces the number
of vertex shader files.
This will be used by `QgsPhongMaterialSettings` in the next commit.
This commit makes two changes to `QgsPhongMaterialSettings`:
1. It merges `dataDefinedMaterial()` and `constantColorMaterial` into
one new function: `buildMaterial`.

2. The fragment shader files `phongDataDefined.frag` and
`phongConstant.frag` are merged into a new file `phong.frag`. This is
achieved by a `#define` logic. If `DATA_DEFINED` is defined, this is
the dataDefined case. Otherwise, this is the constant case.
This is similar to the previous QgsPhongMaterialSettings change:

This commit makes two changes to `QgsGoochMaterialSettings`:
1. It merges `dataDefinedMaterial()` and `constantColorMaterial` into
one new function: `buildMaterial`.

2. There is one fragment shader file called `gooch.frag`. This is
achieved by a `#define` logic. If `DATA_DEFINED` is defined, this is
the dataDefined case. Otherwise, this is the constant case.
There is no need to define a special vertex shader code for this
material, it can use the default case. This way, it reduces the number
of vertex shader files.
This is the same material as the qt3d one:
`Qt3DExtras::QTextureMaterial`. It will also be used in the following
commits to define the gl_ClipDistance for the textured terrain.
This is the same material as the qt3d one:
`Qt3DExtras::QgsMaskedTexturedMaterial`. It will also be used in the
following commits to define the gl_ClipDistance for the non texture
case of the terrain.
There is no functional change. It is simply needed to add
gl_ClipDistance support to the terrain shaders in the next commits.
`Qt3DRender::QClipPlane` enable OpenGL clipping plane that can be used
in shaders using gl_ClipDistance.

This change adds 4 clipPlane. This will be used to clip the 3dScene at
Xmin / Xmax / Ymin / Ymax. This change has no effect at the moment
because  `gl_ClipDistance` needs to be computed in each vertex or
geometry shader file. This will be achieved in the following commits.
@ptitjano ptitjano force-pushed the 3d-clip-part1 branch 2 times, most recently from 656cf3c to 6718c15 Compare July 5, 2024 21:35
@ptitjano ptitjano changed the title [3D] add support for gl_clipdistance [3D] add support for gl_clipdistance (part 1) Jul 10, 2024
Copy link
Contributor

@benoitdm-oslandia benoitdm-oslandia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs minor comment changes, but LGTM.

src/3d/qgs3dutils.cpp Outdated Show resolved Hide resolved
src/3d/shaders/lines.geom Outdated Show resolved Hide resolved
src/3d/shaders/lines.geom Outdated Show resolved Hide resolved
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.

The associated unit test needs to be updated because the objects at
the border of the scene are now partially cut.
All the existing parameters are stored in
`Qgs3DMapSettings`. Therefore, `Qgs3DMapSettings` can be directly
used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This one is a little bit to the other ones. Indeed, lines has a
geometry shader with redefine gl_Position. Therefore, gl_ClipDistance
needs to be computed in the geometry shader file.

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
This is achieved with two changes:
1. define the plane equation as uniform with the
`Qgs3DUtils::addBoundingBoxParametersToEffect` call
2. include `clipplane.inc` file in the vertex shader file and call
setClipDistance()

With these changes, the planes defined in the framegraph are used.
@wonder-sk
Copy link
Member

hi @ptitjano - sorry for taking so long to review this PR (holiday season is in full swing!)

Thanks a lot for your efforts, going the route of clipping planes!

It would be great to split this kind of work into smaller bits, it would make the review easier (e.g. one PR for addition of new materials, one PR for phong shaders refactoring, one PR for addition of clipping planes).

As for the code changes:

  • I like that all materials we use are now contained in the QGIS code, and we can customize them further as needed
  • good to see the phong "constant" and "data-defined" shaders merged together using #ifdefs
  • addition of QgsDiffuseSpecularMaterial
    • why is it still discarding fragments if alpha is not 1.0 ? is it still needed?
    • my understanding is that it is now used only for terrain, but there are also very similar materials created in QgsPhongMaterialSettings and QgsPhongTexturedMaterialSettings - and I strongly feel those materials should be also covered by the newly created QgsDiffuseSpecularMaterial class, given they have pretty much the same use case
    • it is quite confusing in addition to the existing diffuseSpecular.frag, this material adds diffusespecular.frag with different implementation - this should be cleaned up
  • clipping planes implementation
    • clipping should be an optional feature, not something that is enabled all the time - materials should apply clipping only when it is requested
    • clipping should not be tied to extent() of Qgs3DMapSettings - that is really limiting to how it can be used. There should be a getter/setter in Qgs3DMapSettings to get/set clipping planes (in world coordinates), independent from the extent() - whenever extent() gets changed, we need to do reloading of data, but we should be able to modify clipping planes just by a much "lighter" approach, where the existing materials will only update their uniforms, without heavy rebuilding of the whole scene.
    • I agree with Nyall that the introduction of Qgs3DMapSettings in toMaterial() is not a good design choice. In addition to the worry about use of QObject in threads:
      • materials should not need to know about the whole 3d map settings
      • materials should not have clipping planes fixed at the time the material is created - that should be configurable during the lifetime of the material object (just like e.g. diffuse color property of a material)
      • addition of it heavily pollutes many places in the 3D code and adds access to 3D map settings where it should not be available
    • what would make sense to me is that:
      1. there would be some kind of base material class derived from QMaterial, that adds function like setClippingPlanes(QVector planes)
      2. all "our" materials derive from it, and respect the clipping planes
      3. at any time, material's clipping planes could be changed, to update the clipping setup
      4. there is some convenience utility function e.g. in Qgs3DUtils that can generate clipping planes (e.g. take a 2D/3D extent and return the planes)

I think the Phong material implementation needs to be cleaned up and clipping planes implementation needs to be updated before we can merge this...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
3D Relates to QGIS' 3D engine or rendering Feature
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants