Skip to content

Rework BaseMaterial3D deep parallax implementation and add new features#97646

Open
Calinou wants to merge 2 commits into
godotengine:masterfrom
Calinou:rework-parallax-occlusion-mapping
Open

Rework BaseMaterial3D deep parallax implementation and add new features#97646
Calinou wants to merge 2 commits into
godotengine:masterfrom
Calinou:rework-parallax-occlusion-mapping

Conversation

@Calinou
Copy link
Copy Markdown
Member

@Calinou Calinou commented Sep 30, 2024

The deep parallax effect now features built-in interpolation to make low layer counts look better. It also integrates Z correction for a more convincing depth effect at oblique angles.

The new features are disabled by default, but allow for greater realism at a performance cost. Each of them can be enabled separately:

  • Correct Shadow Receive: This allows the material to receive shadows (and even cast self-shadows) according to the deep parallax effect. Self-shadowing is powered by the existing shadow map system, so it works best with sharp shadows such as the ones cast by OmniLight3D and SpotLight3D, especially when placed at grazing angles. This only has a small performance cost, but it can cause self-shadowing artifacts if lights have a shadow bias that's too low or if the material has a heightmap scale that's too high. Enabling Write Depth at the same time can alleviate those self-shadowing artifacts, with the associated performance cost of that feature.

  • Write Depth: This allows the material to correctly interact with other materials in the scene, regardless of whether the other materials have deep parallax enabled. This has a significant performance cost.

  • Trim Edges: This allows the material to have its silhouette affected by the deep parallax effect. This works best with certain UV layouts, but it can have artifacts if the UV layout has visible seams. This has a moderate performance cost.

Thanks to @sphynx-owner who made the initial implementation and Tentabrobpy from the Godot VFX Discord for further assistance 🙂

Testing project: https://github.com/Calinou/godot-parallax-test-4.0

Preview

Parallax 1 Parallax 2
Parallax 3 Parallax 4
parallax_1.mp4
parallax_2.mp4

Comparison with default settings

8/32 layers (default quality)

Before After
Before After

64/64 layers (maximum quality)

Before After
Before After

Visual impact of each feature being toggled

None CSR1 WD2
none png webp csr png webp wd png webp
TE3 CSR1 + WD2 CSR1 + TE3
te png webp csr_wd png webp csr_te png webp
WD2 + TE3 CSR1 + WD2 + TE3
wd_te png webp csr_wd_te png webp

Benchmark

PC specifications
  • CPU: Intel Core i9-13900K
  • GPU: NVIDIA GeForce RTX 4090
  • RAM: 64 GB (2×32 GB DDR5-5800 C30)
  • SSD: Solidigm P44 Pro 2 TB
  • OS: Linux (Fedora 40)

Measured using the test scene shown in the Before/After comparison before. Materials cover most (but not all) of the view. Using a 3840×2160 viewport.

Features Performance
None 843 FPS (1.19 mspf)
CSR1 856 FPS (1.17 mspf)
WD2 351 FPS (2.85 mspf)
TE3 689 FPS (1.45 mspf)
CSR1 + WD2 350 FPS (2.86 mspf)
CSR1 + TE3 689 FPS (1.45 mspf)
WD2 + TE3 341 FPS (2.93 mspf)
CSR1 + WD2 + TE3 344 FPS (2.91 mspf)

TODO

  • Investigate Correct Shadow Receive not having a visible effect when using the Compatibility rendering method. Is it due to an engine bug?
  • Investigate Write Depth being broken when using the Compatibility rendering method. Is it due to an engine bug, or is some kind of specific adjustment needed here?
  • When Write Depth is enabled, see if we can disable the depth prepass without having to use discard within if (false). Since Write Depth does not actually require using discard, doing so would probably improve performance a bit. For reference, the Mobile rendering method (which lacks a depth prepass) works fine without this discard statement (although removing it reintroduces the occasional self-shadowing artifacts that occur when Correct Shadow Receive is enabled).
    • Or, better, see if the depth prepass can be kept when Write Depth is enabled, by having the depth prepass shrink the material's vertices (only within the depth prepass), so that it can still be effective and improve performance significantly.
  • Check the interactions with the UV Scale property again to ensure Correct Shadow Receive looks as good as possible regardless of the scale. Right now, it can get a bit off when the scale is changed from the default (i.e. when the texture tiles more often), although it's not too noticeable during actual gameplay.
  • Consider combining Correct Shadow Receive and Write Depth into a single enum option, since there isn't much point in having Write Depth enabled but not Correct Shadow Receive. This means there would be 3 options provided: Disabled, Correct Shadow Receive, Correct Shadow Receive + Write Depth.

Footnotes

  1. Correct Shadow Receive 2 3 4 5 6 7 8

  2. Write Depth 2 3 4 5 6 7 8

  3. Trim Edges 2 3 4 5 6 7 8

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Dec 10, 2024

I've pushed an update that makes use of @tetrapod00's suggested fix for Compatibility. However, it causes a shader compilation error:

SHADER ERROR: Tokenizer: Unknown character #35: '#'
          at: (null) (:96)
ERROR: Shader compilation failed.
   at: set_code (./drivers/gles3/storage/material_storage.cpp:2983)

For some reason, preprocessor macros don't work in the shader generated in BaseMaterial3D, even though it doesn't use #pragma disable_preprocessor. They start working if you convert it to a ShaderMaterial.

@solitaryurt
Copy link
Copy Markdown

solitaryurt commented Feb 5, 2025

MacOS x86_64 (Forward+), looks like its not working:
Deep Parallax disabled:
image

Deep Parallax enabled:
image

Used build from: Calinou@1f6e871

Same shader compile error from above.

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Feb 5, 2025

MacOS x86_64 (Forward+), looks like its not working:

This is expected for now until we fix BaseMaterial3D to allow using shader preprocessor within its generated code. This should be done in a separate PR.

@solitaryurt
Copy link
Copy Markdown

MacOS x86_64 (Forward+), looks like its not working:

This is expected for now until we fix BaseMaterial3D to allow using shader preprocessor within its generated code. This should be done in a separate PR.

Is there an older hash I could build without the shader compilation issue?

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Feb 5, 2025

Is there an older hash I could build without the shader compilation issue?

No, but you can remove the uses of the shader preprocessor within the PR's changes if you only intend to use Forward+/Mobile in your project.

@solitaryurt
Copy link
Copy Markdown

None:
image

Correct Shadows:
image

Write Depth:
image

Correct Shadows + Write Depth:
image

Unfortunately the mesh I'm using isn't happy with Trim Edges:
image

@solitaryurt
Copy link
Copy Markdown

I'm sure Trim Edges and Write Depth will meet the needs of many people who desired displacement mapping.

image
image

image
image

I will note that I believe the addition of these features makes godotengine/godot-proposals#2455 even more relevant now.

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Feb 6, 2025

Unfortunately the mesh I'm using isn't happy with Trim Edges:

It doesn't work well with repeating textures unfortunately, since UV will go outside the [0; 1] range. I'm not aware of a way to make it work with repeating textures (unless the number of texture repetitions is known in advance and configured in the material - which is how it can work with the UV Offset/Scale property).

@fire
Copy link
Copy Markdown
Member

fire commented Feb 6, 2025

Where uvs go outside the range I have discovered a cutting method described by Steve Steeting (Sinbad of Ogre3D) https://github.com/sinbad/FbxUdimUnpack?tab=readme-ov-file#fbx-udim-unpacker

@solitaryurt
Copy link
Copy Markdown

I noticed that when "Deep Parallax" is enabled both "Flip Tangent" and "Flip Binormal" don't appear to have an effect. Is that intended?

@solitaryurt
Copy link
Copy Markdown

Is there any possibility of anti-aliasing the edges Trim Edges or Write Depth?

I noticed some aliasing:
image

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Feb 10, 2025

Is there any possibility of anti-aliasing the edges Trim Edges or Write Depth?

Not by MSAA, because the edges are created by a shader and not by geometry. However, all other antialiasing methods that are based on post-processing (TAA, FXAA, FSR2, SSAA, SMAA) will work.

@Calinou
Copy link
Copy Markdown
Member Author

Calinou commented Mar 5, 2026

Rebased and tested again on Linux + GeForce RTX 5090 (NVIDIA 580.119.02), I get a driver crash when enabling Deep Parallax on a material. --accurate-breadcrumbs doesn't tell me much:

ERROR: Condition "err != VK_SUCCESS" is true. Returning: FAILED
   at: fence_wait (drivers/vulkan/rendering_device_driver_vulkan.cpp:2962)
ERROR: Printing last known breadcrumbs in reverse order (last executed first).
   at: print_lost_device_info (drivers/vulkan/rendering_device_driver_vulkan.cpp:6861)
ERROR: Searching last breadcrumb. We've sent up to ID: 39
ERROR: Last breadcrumb ID found: 37
ERROR: Last known breadcrumb: BLIT_PASS
ERROR: Last known breadcrumb: UI_PASS
ERROR: Last known breadcrumb: BLIT_PASS
ERROR: Last known breadcrumb: UI_PASS
ERROR: Last known breadcrumb: BLIT_PASS
ERROR: Last known breadcrumb: UI_PASS
ERROR: Last known breadcrumb: BLIT_PASS
ERROR: Last known breadcrumb: UI_PASS

Comment thread doc/classes/BaseMaterial3D.xml Outdated
@Nintorch Nintorch requested review from a team and removed request for a team March 5, 2026 16:05
Calinou and others added 2 commits March 6, 2026 19:56
The deep parallax effect now features built-in interpolation to make
low layer counts look better. It also integrates Z correction for a more
convincing depth effect at oblique angles.

The new features are disabled by default, but allow for greater realism
at a performance cost. Each of them can be enabled separately:

- Correct Shadow Receive: This allows the material to receive shadows
  (and even cast self-shadows) according to the deep parallax effect.
  Self-shadowing is powered by the existing shadow map system, so it
  works best with sharp shadows such as the ones cast by OmniLight3D
  and SpotLight3D, especially when placed at grazing angles.
  This only has a small performance cost, but it can cause self-shadowing
  artifacts if lights have a shadow bias that's too low or if the material
  has a heightmap scale that's too high. Enabling Write Depth at the
  same time can alleviate those self-shadowing artifacts,
  with the associated performance cost of that feature.

- Write Depth: This allows the material to correctly interact with
  other materials in the scene, regardless of whether the other materials
  have deep  parallax enabled. This has a significant performance cost.

- Trim Edges: This allows the material to have its silhouette affected
  by the deep parallax effect. This works best with certain UV layouts,
  but it can have artifacts if the UV layout has visible seams.
  This has a moderate performance cost.

Co-authored-by: sphynx-owner <61445300+sphynx-owner@users.noreply.github.com>
@Calinou Calinou force-pushed the rework-parallax-occlusion-mapping branch from 88836bd to 9616117 Compare March 6, 2026 18:56
@Calinou Calinou requested a review from a team as a code owner March 6, 2026 18:56
@Foyezes
Copy link
Copy Markdown

Foyezes commented Apr 13, 2026

Enabling Deep Parallax with heightmap crashes for me.

crash_log_d3d12.txt
crash_log_vulkan.txt

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants