-
-
Notifications
You must be signed in to change notification settings - Fork 21.4k
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
Comments
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. SSR2022-03-12.23-55-18.mp4SDFGI2022-03-13.00-20-12.mp4
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:
before setting 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. |
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. |
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. |
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! |
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:
This shader on a few planes of grass close to the camera tanks performance:
Add this to the end of the fragment shader:
And performance improves a lot:
Shouldn't this happen automatically?
Steps to reproduce
Open the attached project.
Minimal reproduction project
TransparencyTests.zip
The text was updated successfully, but these errors were encountered: