Skip to content

Conversation

@jon1solution
Copy link
Contributor

@jon1solution jon1solution commented Aug 8, 2025

This adds a very simple SSAO implementation to the Compatibility renderer. It addresses Proposal # 12059

This is not an implementation of a previous technique...I call this one S4AO (Stupid Simple Screen Space Ambient Occlusion [8^)
S4AO is designed to:

  • work directly in the reversed-Z clip space that Godot already uses
  • avoid branching (there are loops with fixed endpoints, so they can be trivially unrolled by the shader compiler)
  • not require half-resolution
  • not require a subsequent blur pass

The SSAO parameters used are:

  • Radius (works as you would expect, though this is used more like a ratio of inverse-depth, than as physical units)
  • Intensity (this is used exactly the same...higher values yield more darkening)
  • Detail (this one is hacked to be used as the sample falloff threshold...I recommend leaving it at the default 0.5)

There is a very simple 2D random rotation built into the sampling to break up obvious sampling patterns. On my GTX 1650 Ti, fullscreen at 1920x1080, S4AO takes approximately 0.6 ms.

Here is a screenshot of S4AO in subtle action:
image

Bugsquad edit:

@Saul2022
Copy link

Saul2022 commented Aug 8, 2025

You could add it to the mobile render too as a separate pr since there we dont have ssao

@AThousandShips AThousandShips changed the title Implemented a very simple SSAO in GLES3. Implement a very simple SSAO in GLES3. Aug 8, 2025
@jon1solution
Copy link
Contributor Author

You could add it to the mobile render too as a separate pr since there we dont have ssao

I will focus on that next. I hope to finalize this one first, to avoid having to make changes in 2 different places. The same shader should work, but I think linking to the settings will be slightly different.

@jon1solution
Copy link
Contributor Author

jon1solution commented Aug 9, 2025

In case anyone wants to play with the shader without recompiling Godot, here is a project that uses a full-screen-quad to test the S4AO with the stable or beta versions of Godot.

EDITED - updated to use filter_nearest (thanks mrjustaguy!)
MORE EDITS - there is an updated v004 down below.

@mrjustaguy
Copy link
Contributor

In the zip version, I noticed you're using filter_linear on the Depth buffer, use filter_nearest instead, it'll remove an unwanted dark outline around objects when the depth difference between the objects is large

@jon1solution
Copy link
Contributor Author

In the zip version, I noticed you're using filter_linear on the Depth buffer, use filter_nearest instead, it'll remove an unwanted dark outline around objects when the depth difference between the objects is large

Good catch! That makes sense, as linear interpolation lets a portion of even the sharpest discontinuity be closer than the falloff threshold. I'll update the code shortly.

@mrjustaguy
Copy link
Contributor

Another Observation I've made, the falloff pixelizes edges (on my Radeon RX 6600 atleast) when it's below 1

(0.25)
image

(1.0)
image

@mrjustaguy
Copy link
Contributor

Another Suggestion, you may want to experiment with hint_normal_roughness_texture to use the actual scene normal instead of getting it from depth, which would also include material Normals

@jon1solution
Copy link
Contributor Author

jon1solution commented Aug 9, 2025

Another Suggestion, you may want to experiment with hint_normal_roughness_texture to use the actual scene normal instead of getting it from depth, which would also include material Normals

I'm pretty sure hint_normal_roughness_texture is an only-Forward+ thing. I can actually ignore the normal all together and just assume it is "straight up", which simplifies the code a bit but also adds a slight shadow for all edges, regardless of the falloff setting. I have a coworker who prefers that look. To try it out, find the line slope *= smoothstep( 1.0f, 0.0f, dot( slope, slope ) ); // Mitigate discontinuities., and change that to slope *= 0.0f;.

Regarding the pixelized edges, if you toggle the color on and off, can you tell if those white pixels are on the foreground or background object? (And I assume you are using the updated v003 version that has the nearest sampling.)

Thanks for checking this out!

@mrjustaguy
Copy link
Contributor

I didn't even consider that possibility... I basically only ever really work with Forward+

The pixelized edges seem to appear on both, though it's really hard to tell for sure. (and yes on v003)

@jon1solution
Copy link
Contributor Author

I added s4ao_simple, which assumes the normal is straight out of the screen...it seems to get rid of the pixelized edges, while also being simpler code. I also improved the random sampling to take the screen resolution into account, so changing window aspect ratios doesn't mess with the random sampling.
s4ao - v004 - stupid simple screen space ambient occlusion for Godot - extra simple with improved randomization.zip
I will move these changes into the PR code.

@dsnopek
Copy link
Contributor

dsnopek commented Aug 13, 2025

I did a little bit of testing with this PR to see how it worked in VR.

Firstly, opening an existing scene in the editor (on Linux) which has a WorldEnvironment node, I get this message spammed to the console:

ERROR: Parameter "env" is null.
   at: environment_get_ssao_enabled (servers/rendering/storage/environment_storage.cpp:602)

If I close the scene with that node, then the errors stop.

Next, I tested running my fork of the GDQuest TPS demo (which adds VR support) on a Meta Quest 3. I disabled MSAA and enabled SSAO, and, unfortunately, it doesn't work. The stereo effect is broken - it's almost like the left and right eye are reversed? And, on the left eye, there is a weird texture overlaying the image.

Here's a screenshot from the left eye:

com oculus vrshell-20250813-101327

These errors are spammed to the log:

58009:08-13 10:56:24.757 23868 23914 E godot   : ERROR: GL ERROR: Source: OpenGL        Type: Error     ID: 2147483647  Severity: High  Message: texture object 204 to bind has a different dimensionality than texture target 3553
58010:08-13 10:56:24.757 23868 23914 E godot   :    at: _gl_debug_print (drivers/gles3/rasterizer_gles3.cpp:194)
58011:08-13 10:56:24.758 23868 23914 E godot   : ERROR: GL ERROR: Source: OpenGL        Type: Error     ID: 2147483647  Severity: High  Message: texture object 203 to bind has a different dimensionality than texture target 3553
58012:08-13 10:56:24.758 23868 23914 E godot   :    at: _gl_debug_print (drivers/gles3/rasterizer_gles3.cpp:194)

Also, performance is pretty bad. With MSAA 4x, this scene runs at 72 fps (which is the target, the display's v-sync is set to 72fps). But with MSAA disabled and SSAO enabled, it runs at about ~15 fps. This could be due to running on a tile-based GPU, where screen space effects tend to be quite slow. I expect that running this with PCVR (rather than standalone VR with a mobile chipset) that performance would be much better, possibly even faster than with MSAA, but someone should test it.

And lastly, I tested a much simpler project with WebXR, also on Meta Quest 3, with the default web browser. Unfortunately, it doesn't seem to work either. The right eye seems to be rendered normally, but the left eye simply blinks between black and the clear color - nothing is rendered there. Here are the console messages:

screenshot

However, there was no noticeable reduction in performance - but that could be because the scene is so simple

So, it seems this PR still needs some work to account for VR :-)

@jon1solution
Copy link
Contributor Author

I just made an update that may fix the issues in VR?

@BastiaanOlij
Copy link
Contributor

BastiaanOlij commented Aug 14, 2025

Two more concerns to be aware of on the VR side.

  1. the post process disables a number of optimisations (including foveated rendering) on XR and introduces a fair bit of bandwidth overhead. Likely this will heavily limit the ability to run this on standalone VR headsets and only makes it suitable for desktop VR
  2. screen space effects in VR have a serious problem in the result for the left eye potentially being different from the right eye. This is an issue we have in the forward+ renderer as well. The only way to solve this is to sample both left eye and right eye depth data for both eyes, doubling the overhead in processing this.

edit just to clarify as also mentioned in the rendering meeting. These are issues for XR developers that are known and not limited to this functionality and should not be a reason for us not to move forward with this PR. Its a warning to XR developers that this is unlikely to be usable on standalone devices, and that we need to come up with a solution for the stereo issue for desktop XR (also for Forward+). Most XR developers for this reason bake their AO.

@BastiaanOlij
Copy link
Contributor

2. screen space effects in VR have a serious problem in the result for the left eye potentially being different from the right eye. This is an issue we have in the forward+ renderer as well. The only way to solve this is to sample both left eye and right eye depth data for both eyes, doubling the overhead in processing this.

Just a quick google to further explain this issue. This is a paper that seems to describe the problem fairly well and talks about a possible solution:
https://pure.tudelft.nl/ws/portalfiles/portal/121484149/3522614.pdf

There were a few other good hits that came up so worth reading up other views on the problem.

Also note that this applies not just to SSAO but to many other screen space effects as well.

@jon1solution
Copy link
Contributor Author

Two more concerns to be aware of on the VR side.

  1. the post process disables a number of optimisations (including foveated rendering) on XR and introduces a fair bit of bandwidth overhead. Likely this will heavily limit the ability to run this on standalone VR headsets and only makes it suitable for desktop VR
  2. screen space effects in VR have a serious problem in the result for the left eye potentially being different from the right eye. This is an issue we have in the forward+ renderer as well. The only way to solve this is to sample both left eye and right eye depth data for both eyes, doubling the overhead in processing this.

edit just to clarify as also mentioned in the rendering meeting. These are issues for XR developers that are known and not limited to this functionality and should not be a reason for us not to move forward with this PR. Its a warning to XR developers that this is unlikely to be usable on standalone devices, and that we need to come up with a solution for the stereo issue for desktop XR (also for Forward+). Most XR developers for this reason bake their AO.

Got it. I am reading that paper, looks like there is a decent bit of work needed to make VR SSAO work, and if even the simple case has too much overhead to be used, there's probably no point pursuing an implementation of the more complex fix.

One thing I am curious about...in my implementation the sampling radius is a function of the depth, so things farther away have a smaller radius, but they would also shift less per eye...possibly there is a magic radius setting where the AO for each eye is nearly identical since the sampled points would be farther than any depthmap discrepancies. Possible?

@jon1solution
Copy link
Contributor Author

The latest commit supports 3 different quality settings (very low and low are combined, then there is medium, then high and ultra are combined).

I also hide the unused SSAO settings (I did it for Compatibility and Mobile, assuming that SSAO on Mobile will be the next PR I tackle).

Thanks for all your help!

@dsnopek
Copy link
Contributor

dsnopek commented Aug 14, 2025

I did some more testing with the latest version with OpenXR on the Meta Quest 3. (I didn't do any WebXR testing this time.)

This is definitely working better than before!

  • The Parameter "env" is null. message is no longer spammed in the editor
  • The stereo effect is no longer broken, and there isn't the weird texture over the left eye
  • One of the OpenGL debug messages is no longer spammed. Previously it would alternate between two different ids giving the message texture object X to bind has a different dimensionality than texture target Y; now it only does this for a single id. So, it seems like one case was fixed?
  • Performance is somewhat better: I'm getting ~36 fps in my demo project now, rather than ~15 fps

However, it's still not perfect:

  • There is still one set of OpenGL errors spammed at runtime:
    E 0:00:03:387   _gl_debug_print: GL ERROR: Source: OpenGL	Type: Error	ID: 2147483647	Severity: High	Message: number of views for the current read FBO is greater than 1
    E 0:00:03:387   _gl_debug_print: GL ERROR: Source: OpenGL	Type: Error	ID: 2147483647	Severity: High	Message: texture object 204 to bind has a different dimensionality than texture target 3553
    
    This leads me to believe there is still something that needs changes for multiview? Maybe something on the C++ side is still using GL_TEXTURE_2D rather than GL_TEXTURE_2D_ARRAY? But I haven't tried to find it, that's just a random guess
  • With MSAA 4x enabled, the SSAO doesn't seem to be rendered. That's why I've got it turned off in all of my tests. I don't know if that's expected or not? I haven't used SSAO much, even in flat screen games

As far as the quality of the SSAO: screen-space effects (even if you manage to make them perform well) tend not to look that great in VR. Little imperfections that you would never notice in a flat screen game become very noticeable in VR, when the screen is a couple centimeters from your eyes.

That said, more options is always nice :-)

@jon1solution
Copy link
Contributor Author

jon1solution commented Aug 14, 2025

Thanks for doing further testing! I am not seeing those errors at all in the "Test Stereo" project, and it seems to be able to run SSAO with MSAA 4x or 8x. I need to figure out how to show those errors on the (lack of) hardware I have here.

EDIT - I assume this was tested on the compiled Godot version, not the full-screen-quad, correct? Was the 36 fps using the low quality mode, and 15 fps using the medium setting?

@dsnopek
Copy link
Contributor

dsnopek commented Aug 14, 2025

EDIT - I assume this was tested on the compiled Godot version, not the full-screen-quad, correct? Was the 36 fps using the low quality mode, and 15 fps using the medium setting?

Yes, Godot compiled with the PR. I used the default settings in all my tests: I only enabled SSAO and disabled MSAA - I didn't change anything else.

@jon1solution
Copy link
Contributor Author

Yes, Godot compiled with the PR. I used the default settings in all my tests: I only enabled SSAO and disabled MSAA - I didn't change anything else.

So, I am seeing errors in the debugger with the TPS Demo project even when I run it on 4.5-beta5 ("Framebuffer bindings are not framebuffer complete", for example).

Could you please see if you get those same errors on a recent build without my SSAO changes?

Copy link
Member

@clayjohn clayjohn left a comment

Choose a reason for hiding this comment

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

Looks great to me!

My only concern is applying SSAO after glow. I think that will, in general look a little awkward, but so would applying glow in sRGB space. So it's really a pick your poison type situation.

Let's go ahead with this as-is and then wait for user feedback and be ready to tweak the behaviour once this gets some testing

@clayjohn clayjohn modified the milestones: 4.x, 4.6 Oct 25, 2025
@clayjohn
Copy link
Member

The final step before merging is to squash all the commits together so that the whole PR only contains 1 big commit with all your changes. We like to merge one commit at a time to keep the git history clean and navigable.

If you don't know how to do that, we have a helpful tutorial in the official documentation https://docs.godotengine.org/en/latest/community/contributing/pr_workflow.html#the-interactive-rebase

@jon1solution
Copy link
Contributor Author

Great! I'll try to fix the code-style issues and squash all commits together tomorrow, Monday at the latest.

@jon1solution jon1solution requested a review from clayjohn November 1, 2025 00:28
@akien-mga akien-mga merged commit 85e47d6 into godotengine:master Nov 1, 2025
20 checks passed
@akien-mga
Copy link
Member

Thanks!

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.

Implement SSAO in the Compatibility rendering method

9 participants