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

Vulkan: depth_prepass_alpha shaders don't discard 0 alpha parts #59015

Closed
Arnklit opened this issue Mar 11, 2022 · 4 comments
Closed

Vulkan: depth_prepass_alpha shaders don't discard 0 alpha parts #59015

Arnklit opened this issue Mar 11, 2022 · 4 comments

Comments

@Arnklit
Copy link
Contributor

Arnklit commented Mar 11, 2022

Godot version

4.0.alpha4

System information

Windows 10, GLES3, GTX 980Ti

Issue description

Shouldn't Godot discard no alpha parts of a material automatically?

Currently it doesn't and it comes at a big performance cost for no benefit as far as I can see.

Shader Example:

shader_type spatial;
render_mode depth_draw_opaque, cull_disabled, depth_prepass_alpha;
uniform vec4 albedo : hint_color;
uniform sampler2D texture_albedo : hint_albedo,filter_linear_mipmap,repeat_enable;

void fragment() {
	vec4 albedo_tex = texture(texture_albedo, UV);
	ALBEDO = albedo.rgb * albedo_tex.rgb;
	ALPHA *= albedo.a * albedo_tex.a;
}

This shader on a few planes of grass close to the camera tanks performance:

image

Add this to the end of the fragment shader:

if (ALPHA == 0.00)
	discard;

And performance improves a lot:

image

Shouldn't this happen automatically?

Steps to reproduce

Open the attached project.

Minimal reproduction project

TransparencyTests.zip

@Calinou Calinou changed the title depth_prepass_alpha shaders don't discard 0 alpha parts Vulkan: depth_prepass_alpha shaders don't discard 0 alpha parts Mar 11, 2022
@lewiji
Copy link
Contributor

lewiji commented Mar 13, 2022

I've been porting a Godot 3 project to 4 which uses a lot of 3d-in-2d things - mostly flat geometry (planes or quads), and AnimatedSprite3D for animations. Couldn't get reflections working with the sprites, not with SSR, nor SDFGI, but there were some quite distinct behaviours that might help diagnose what's going on here.

Set up the test scene shown in the attached videos, these are all AnimatedSprite3D nodes with either a StandardMaterial or shader applied. The videos only shows SS reflections, and SDFGI. but I also tested out directional light shadows after capturing these and didn't get a video, but have written the findings below.

The first video shows the current StandardMaterial options for this with SS reflections enabled. The second shows SDFGI, with a 4th sprite set up to discard pixels where alpha is 0 in the albedo texture, which works perfectly. The 4th sprite doesn't set ALPHA at all, whereas the others, when converted from StandardMaterials to shaders, do set ALPHA, and this breaks shadows/reflections in any depth draw mode.

SSR

2022-03-12.23-55-18.mp4

SDFGI

2022-03-13.00-20-12.mp4
  • If I make a StandardMaterial with transparency set to Alpha Scissor, I get a transparent looking sprite, but I get no reflections or shadows.

  • StandardMaterial with transparency set to Depth Pre-Pass, I get a transparent looking sprite, I get shadows,

    • SS reflection, it kind of works a bit, but it's faint and has no colour at all, just a greyish blob.
    • SDFGI, the reflection has colour, but though the "real" sprite looks transparent, the reflection has the discarded pixels reflected as a white background (I didn't get video of this)
  • If I make a shader which ever sets ALPHA (even to 1.0), I get no reflections or shadows with any depth draw mode, even with manual discard based on ALPHA like you wrote above, or setting the alpha scissor threshold.

  • If I never set ALPHA at all, and manually discard pixels by sampling the texture rather than the ALPHA as above, I get a transparent sprite with shadow, and both SSR and SDFGI work perfectly:

if (albedo_tex.a == 0.0)
        discard;

Out of curiosity, I set up a StandardMaterial with alpha scissor enabled (which I also expected would discard the pixels like this), and converted it to a shader in the right click menu of the material, it adds the line:

ALPHA *= albedo.a * albedo_tex.a;

before setting ALPHA_SCISSOR_THRESHOLD. If I remove that line, or change it to ALPHA = 1.0, then the alpha scissor does nothing, no pixels get discarded, and I get no shadows or reflections.

I will try and find out if the equivalent is true for Godot 3, but it's getting late now. I definitely had SS reflections working in my godot 3 project, but the shader in that is pretty complicated and can't remember off the top of my head what depth mode it's using.

Will post a repro project if interested, but stupidly I started testing this with assets I can't redistribute, so will have to change those first.

@clayjohn
Copy link
Member

Shouldn't this happen automatically?

I need to double check, but I think discard breaks the calculation of pixel derivatives (as the GPU relies on neighbouring pixels). So this optimization may only be helpful for simple objects. Not that I am downplaying its benefit, it seems really helpful for vegetation like grass and bushes and stuff.

@lewiji
Copy link
Contributor

lewiji commented Mar 14, 2022

pixel derivatives

What would be the impact of losing these? Would they only be lost for the discarded fragments (and/or their original neighbours)?

It seems that there is currently no option in a StandardMaterial3D to get proper reflections with a texture that has transparency in some form, and at least in the screen space reflections generated by setting prepass alpha mode, most of the colour pixel data is lost (rendering a greyish blob as the reflection), whereas doing the manual discard "optimisation" in a shader gives full colour, full depth reflections.

So it would seem to me that, at least for reflections/sdfgi, more pixel data is retained/available when doing the discard optimisation vs any of the options currently available in the StandardMaterial3D. But I suppose these effects may not rely on these pixel derivatives.

I think part of the issue vs 3.x is that alpha scissor and depth prepass are now bundled under one option in godot 4's StandardMaterial3D, but for cases such as these, both options are wanted - scissoring alpha and rendering in opaque mode, while also doing alpha prepass.

The thing I don't quite get is why setting ALPHA in the shader to 1.0, using opaque depth mode, discarding transparent fragments, and using depth prepass doesn't seem to give any reflection at all, as if the depth isn't being calculated - until you remove the assignment to ALPHA, which it seems the StandardMaterial3D will always do.

@clayjohn
Copy link
Member

Thinking more about this potential optimization. It works well in this situation because the texture is essentially a flat colour. But in more complex scenarios the improper pixel derivatives will create visible artifacts at the edge of the visible texture caused by a failure to calculate the proper mipmap.

For something like a built-in vegetation shader you would handle this by adding a discard clause like in the OP, but you would also switch to using either manual LOD, or manually calculate LOD yourself.

The other big downside is that discard disables the early-z depth test and totally messes with TBDR optimizations in mobile GPUs. In a simple example the impact would not be visible, but on a more complex project (where you actually benefit from depth-testing) this would be detrimental to performance (of course, this depends on shader complexity).

The "proper" solution here is to actually not use quads for the grass, but create a simple mesh that roughly follows the contours of the texture so that you minimize pixel overdraw, but can leave the shader itself alone.

I'm closing for now as this is a performance optimization that I do not think is worth making. I hope the discussion is interesting and helpful for users down the road though!

@clayjohn clayjohn closed this as not planned Won't fix, can't repro, duplicate, stale Nov 13, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants