-
-
Notifications
You must be signed in to change notification settings - Fork 21.5k
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: Transparency pipeline has z-order issues #63675
Comments
Turning on gizmos has no effect on these issues. #62457 |
I considered if this is a duplicate of #54226. It has an appropriate title, but otherwise is a different issue that appears to have been fixed. |
Alright, I've made an MRP using some free assets I found, as I can't distribute mine. Included in Op. Here is the rock with proximity fade and no shadows. It's messed up everywhere it's not fading in with the ground. The whole top is messed up. The mesh has been reimported recently and linked to the included glb (12/19/22) so it's not a mesh issue. On the pine tree model, Alpha shows behind faces in front. But it's not every face. Just random ones. Here 1 & 3 are closest to the camera. Yet 1 properly occludes 2, but 4 incorrectly occludes 3. If I move the camera, 4 also incorrectly occludes 1. This shot is alpha mode, which is all backwards. Same shot in Depth prepass, which exhibits some of the issues as on my models. They have too much transparency as the camera pulls back, the grid shows through, and the textures start to thin out too quickly. On the left the dark textures are getting thin and the light branch shows through easily. On the right, the light textures are thinning and the farther dark branches show through. Alpha scissors shows proper order Sources: |
I started taking a look at this. For now I have a couple of workarounds until I can figure out what exactly is broken. For the tree: One problem with the tree is that all the leaves/branches are in one mesh. So they get drawn in the order specified by the mesh, since transparent objects don't write to depth by default the later branches get drawn over the earlier ones. To fix this some sort of depth prepass is needed (alpha scissor, alpha hash, or depth prepass tested and all work). Also, with TAA enabled, alpha hash looks really good on the tree. The other problem with the tree is that the leaves look too transparent, you can see the grid through them. This is indeed a z-ordering bug and I don't know exactly what is causing it yet. It goes away with alpha hash and alpha scissor. It should go away with depth prepass too, but its not. TODO: figure out why grid is consistently drawing over transparent objects and why depth prepass isn't helping For the rock (proximity fade) When proximity fade is enabled, the rock essentially turns transparent and even worse, the back faces end up drawing over the front faces. In part this is because back face culling is disabled. But even enabling back face culling doesn't fully solve the issue. This is clearly an issue with how "proximity fade" materials get inserted into the pipeline. As a workaround for now, the depth-draw mode for the rock can be set to "Always". |
Alpha hash has been significantly improved and now looks like it could be useful for some things. However nothing else has been resolved as far as I can tell as of 4828ac6. Status of issues:
The only alpha methods with accurate z-order & shadows all the time are alpha hash (very noisy) or alpha scissor (fine for thick broad leaves but looks terrible for fine details like thin foliage (pine needles), hair and eyelashes. It worked much better in Godot 3. I have updated the MRP, given the mesh format has changed. The rock shows issues w/ proximity blend turning inside out and no shadows. The tree uses alpha depth-prepass and shows bg faces through the foreground. Look down into the tree and find areas where you see dark areas visible in front of light areas or the opposite. Turn on alpha scissoring and you'll see how incorrect it is. See the pictures below. Those dark areas especially at the bottom of the image shouldn't be so visible. It's very obvious when the camera is moving. It's also obvious on better quality assets with finer pine needles that we're using but can't share. Compare w/ alpha scissors |
Taking another look at this in Beta 11 and I have a few more observations The RockThe biggest difference with the rock in Beta 11 and 3.5.1 is that for some reason in Beta 11 the material imports with backface culling disabled. As a result, you get way more z-order issues than you do in 3.5.1. Beta 11: default Beta 11: back face culling enabled In both cases you need to set the depth draw mode to "always" to get rid of the artifacts (depth draw alpha prepass isn't enough because the alpha value is dependent on the value in the depth buffer and the depth prepass is the pass that creates the depth buffer). There isn't anything actionable with respect to the rock. Without seeing the original file for your tree trunk file I can't say if there is something else contributing to the problem in that case. The TreeI look a closer look at the gizmos drawing through the tree. This is a z-ordering issue came from the tree being so close to the center of the editor combined with how sorting worked in earlier betas. Since the gizmo's have a massive bounding box they used to be sorted as if they were very far away from the origin (sorting was based on the distance to the nearest face of the bounding box along the camera's direction vector). Now, sorting with a perspective camera sorts based on the distance between the camera origin and the center of the object's bounding box #69998. Things look much better now and you don't see the editor gizmos through the tree except for where the tree is actually transparent. I can't see anything actionable with respect to the tree either. I note you listed concerns in your last comment and I respond to each one here:
Like in 3.5.1, depth draw needs to be set to always for proximity fade materials to contribute to the depth buffer and shadows. This should be better documented
This is expected. Transparent meshes draw triangles in the order they are presented as sorting happens on a per-mesh basis. To get proper depth occlusion within a mesh you need to use some sort of depth buffer (depth prepass alpha or depth draw always. In this case depth prepass alpha is the only feasible option. Alternatively you can avoid the transparent pipeline altogether and use alpha scissor or alpha hash). Older games used to pre-sort the triangles in the source mesh to minimize this kind of artifact. For things like trees, they would sort the triangles from top to bottom to get as close to proper z ordering as possible (as trees would normally be view from below). In modern engines we rely more on the depth buffer.
This artifact is unfortunate but not something we can avoid. It is especially noticeably on the tree mesh because the needles on the texture are almost never opaque (especially with mipmapping). If you look at the tree using the normal buffer view mode (from the perspective menu) you can see that very little of the needles texture is actually opaque, which means that it suffers from the same sorting issues as the regular Alpha mode. Ideally for this kind of mesh you would either use alpha scissor with one of the alpha antialiasing modes (AKA alpha to coverage. Although I think this is broken now as the results are not impressive) or you would use alpha hash with TAA.
Thanks to Calinou for tracking down the issue and bisecting, I was able to get alpha to coverage working again tonight in #71261 Using alpha scissor with alpha to coverage should give you pretty good results. I include screenshots of the tree model in the PR |
This is an issue in the glTF importer, as Godot follows the glTF specification and disables backface culling to follow Blender's exported value. We should probably ignore this value and always use backface culling by default (but have an import option to not apply this overrride). This is also important to do for performance reasons, especially on the mobile backend which doesn't use a depth prepass.
See #64501. |
Rock:
Trees:
AS/AH are too coarse for thin foliage and hair. AS looks good up close, but bad far away. Alpha+depth looks good far away but bad up close. |
Objects use alpha blending with proximity fade, this would require a dithering-based approach to proximity fade. In general, we should probably add a transparency mode that forces the use of dithering instead of alpha-blending (or make the Alpha Scissor/Alpha Hash transparency modes do this automatically when selected at the same time as proximity fade). This is also worth considering for visibility range alpha fade and GeometryInstance3D In short, these properties should probably not make materials transparent unless asked to. Instead, they should dithering by default to improve performance and avoid transparency sorting issues. If you still need alpha blending, you'd manually change the transparency mode to Alpha Blend instead, while keeping proximity fade or visibility range fade enabled.1 Footnotes
|
Can't comment on the rock, but I am fairly certain that the tree alpha problems are caused by the mipmaps. My best guess is that godot4 uses a different algorithm to create the mipmaps, which combined with your asset's very thin needles results in too transparent trees when compared to godot3. There are several solutions to this problem. I personally would switch to sdf alpha, which would give you more control and result in crisp and sharp edges. However, that would obviously require you to touch up all your assets so I can understand if that's not an option for you. Alternatively, you could edit the mips. I know that artists sometimes bring the mips back into PS and scale/remap them there. I would recommend you look into the dds file format, thats what you want to use if you want to have manual control over the mip levels. Not sure how relevant of a problem this is, but it might also be possible to add an alternative mip creation algorithm to the importer. In theory, it should be trivial to look at the ratio of opaque/transparent pixels in the base level and then automatically modify all the other levels to match that (as closely as possible). |
Playing with the alpha scissor value in the material properties can also help alleviate issues with objects becoming too opaque/too transparent in the distance (only when using alpha scissor transparency, not alpha blending). |
@dame-ng it is hard to tell what the issue is from your screenshots but I don't think it is the same as this issue. For your use case you should look into using |
There's a pull request readding CAS for 4.x, but it needs further work to be completed (which I can't do as I don't know how to do it).
Enable anisotropic filtering on the materials, and increase the anisotropic filter level to 16× in the advanced Project Settings. You can also use a negative texture LOD bias in the project settings (but don't go too low to prevent textures from looking grainy at a distance). If you really want sharpening right now, you can enable FSR 1.0 and set the 3D resolution scale to 99% instead of 95% – it should do a better job at looking closer to native resolution. |
Even if it is a different issue I still want interpenetrating geometry to look right. Is that possible? I'm ready to pay the performance cost.
Thank you, I'll look into it.
I already did. It is 16x and material property is set on the screenshot. The bias setting doesn't change anything for the close by textures because it already uses the largest mip, I believe. Bluriness is expected anyway, as GPUs don't do Cubic or Lanczos filtering unfortunately.
I tried both and found that 95% looks basically the same, but the performance doesn't take a hit this way. Edges are ruined anyway.
I'm not ready to plunge into Godot development yet, and I kinda want to just develop games not engines. So I hope someone else would pick it up. |
If Either way, OIT is really meant to be used for professional visualization or offline rendering use cases, rather than games. Most modern AAA games use a jittered alpha hash pattern that changes every frame with TAA, as that often looks good enough while being cheap. |
@Calinou That's the kind of answer I wanted to hear. I just find it weird it is such a hard problem, as it was trivial in the past (in the times before programmable shaders), and I always thought the problem was in deferred rendering. So I hoped clustered forward will not suffer from this issue. In a hindsight deferred still used forward rendering for transparency, I guess. EDIT: yeah, it works, so if I limit the camera movement and avoid overlapping triangles in my transparent meshes I might manage it. I've been mostly negative so far, but really Godot is an awesome engine that does a lot of things right, and I'm having a blast playing with it, so thank you for your hard work! EDIT2: Actually, bias helps for when the camera is in actual playing position, and as the camera is top down, distant textures are not a problem. I apologize and thank you for the tip. |
Alpha mode ignores Z ordering. Use alpha depth pre-pass, or alpha scissor. |
When i put 3d sprites with alpha on top of each other, with different positions, but close to each other, depending on the angle I'm viewing the one that should be behind actually appears on top, blocking the vision of the one that should be on top. Only possible way to solve is using the alpha scissor, then it works properly, but i do need the sprites to have transparency. Using prepass works but not perfectly, some smaller sprites will appear totally grainy being barely possible to even tell they are there. |
To expand further on what @HKunogi said, The issue is related to Z clipping in 3D space when using Z indices with three decimal places. Specifically, elements with Z values such as 0.001 experience severe clipping, whereas elements with Z values such as 0.01 do not, aligning with the engine's default setting of 0.01. This problem causes elements that are positionally in front of the frame to disappear unexpectedly due to incorrect transparent ordering. Reducing the precision to two decimal places resolves the clipping issue, but this workaround is impractical for scaling our numerical values accurately. The engine should handle Z indices with three decimal places without causing clipping, ensuring that all elements maintain their intended visibility in the 3D space. This issue and #43253 as well as #54226 are related. To put it simply, when objects are too close to each other on the Z-axis, it causes some stuff to disappear. We didn't have this issue in Unity but we are trying Godot for the first time and we want to stay here. |
Can you upload a minimal reproduction project? |
Yes, When you open the game and start moving the camera around, you'll notice that elements in the foreground sometimes appear and disappear, even though they should remain visible. This issue persists even at a near clipping plane distance of 0.01, and I’ve tested it thoroughly. It only stops happening at very large sizes, but even at a clipping distance of 1.00, the problem persists. Making the game that large is not feasible for us, as it would introduce new bugs. |
@shadowfox87 You might want to try 4.3.beta1 after making a backup of your project, as it comes with reverse Z which greatly improves the usable depth buffer precision. |
We are already on the latest version of this branch. We compile the source from the master branch. We are not using custom shaders. The reverse Z applies for shaders. The problem remains. |
@shadowfox87 In your MRP, it seems setting Render Priority to Transparent objects can't Z-fight with other transparent objects, so this is not a depth buffer precision issue (because they don't write to depth in the first place). |
It would work on that case, but, lets say that this MRP is a single object and on the scene we would have many of that object (which is a combination of those 3 transparent textures) and they would be close to each other, one in front of the other, in that case, the one with render priority 1 of the object behind, would be rendered on top of the lower priority part of the object that is in front, which shouldn't be the case. |
Here, I've attached another MRP where the changing the Rending Priority to 1 will not work. What Kunogi said above is correct. |
Hey, just wanted to say that I've been playing with the project. Let's go back to two comments.
I believe Sorting Offset is indeed the correct answer. The main challenge it has, is it is unpredictable unless all sprites have the same position. However, it has a checkbox that allows you to use the AABB instead of the object center. So, for all the sprites at 0,0,0 the sorting offset will just work. I would suggest small increments like even 0.001, 0.002, 0.003 etc.
Here is the scene where I have done this for @shadowfox87's MRP and I can confirm it works correctly if the AABB calculations are done right. As for the overarching issue... this issue itself has been a little confused with lots of comments, and it's not asking for anything clearly defined. I think at least from reading the recent comments re. Label3D and Sprite3D, what people want is a sort of "grouping" node, some sort of mode for in-order drawing of things like sprites, labels, within 3D. This is not a feature Godot has ever had in 3D, but perhaps would be worthy of a proposal if there isn't already one. It would make 3D / 2.5D game programming very familiar to people who have made Godot 2D games in the past, which do use in-order drawing. The other thing is even for the Sorting Offset approach above, it seems difficult to work with once you have objects with different centers (and need to slide the AABB around). Maybe there would be a way to override that some other way, like specify a Node3D to use as the origin point (and then VisualInstance3D will deal with the complexities around calculating it.) I think it would be possible to make a script that looks through the tree and calculates the correct sorting offsets and AABBs. If there's interest, I could try and jot down the algorithm describe above into a script that does it on As for other proposed solutions...Rendering Priority works if you have only one or a small number (if you have a script adjust them up and down. not easy). However, I agree that Rendering Priority fundamentally does not work if you want to instantiate more than one: you could adjust all the priorities of everything in the scene to make it work.... until you have more than 32 of them because godot only allows a max of 127 rendering priority.Viewports would allow you to use the 2D renderer, but then you're in a constant battle with viewport size / resolution and if you have too many large viewports you'll start to hit GPU pixel fill limits especially if you want to support mobile. |
@lyuma Thank you for your thorough reply. Yes, you are correct. Although you solved the MRP using custom AABB, we have to recalculate all this for our actual project which is much more complex since it is not native to Godot in 3d. In Unity, this is native so we didn't have to do anything. As you said, we need a "grouping" mode. Should this require another issue to be opened up to propose this or is it already a feature request given this discussion? |
Thanks for the in depth response, indeed i could solve some issues with AABB math, by extending it with some calculations, for simple things, but more complex situations, like the ones described can't be solved properly when taking into account performance and scalability for the objects on a dynamic game. What you said is correct, in that, having a grouping object, that ensures that objects inside that group are drawn in the order of the index they are on their parent, would solve the issue, unity does that actually, if i have a parent object X, and it has children Y and Z, with Y being index 0 and Z index 1, it will draw the Y and then afterwards the Z on top of it, but from my tests, godot does not do that (at least on 3D?), having somehow make it do that, would definitely solve that Z sorting issues for all cases where they are handled by the game maker. I would say that simply adding a bool property to the Node3D which says "Draw children in sibling index order" or whatever naming would fit, would do the job, wouldn't even need a new entire node type, or even an enum, with options to also treat it related to parent (like draw parent on top of children, or draw children from last to first, etc, just so theres more sugar options to use). |
This comment was marked as resolved.
This comment was marked as resolved.
@champbob You are running into the standard difficulty with transparency sorting. What you see isn't a bug, but is instead a general limitation with real time 3D rendering. Image from OpenGL wiki https://www.khronos.org/opengl/wiki/Transparency_Sorting Take the above example, If A is the camera, what object should be drawn in front, B or C? Intuitively, it should be C. However, when sorting the two objects, B will come out in front because the center of B is closer to the camera than the center of C. This is what is happening in your example. You will run into this problem whenever you have large transparent objects near other transparent objects that require depth sorting. This is why you rarely see large semi-transparent surfaces in video games. We have some tools to help deal with this and they are explained in detail in the docs. |
@clayjohn Ah, thanks for the explanation! Apologies for misinterpreting my issue! |
The Mobile rendering method doesn't use a depth prepass for performance reasons, so it can't work there. |
Godot version
4.0alpha13
System information
Win 10/64 NVIDIA GeForce GTX 1060/PCIe/SSE2
Issue description
Summary of sub issues:
Alpha scissoring z-ordering works fine, as documented in #56008. But is not desirable on certain assets like pine trees as looks bad far away.
Proximity Fade
Here is proximity fade on an otherwise opaque object. Has no problem w/ z-ordering in GD3. All the normals are messed up but that's noted in #63550. Both sides in GD4.
Alpha options
On my transparent foliage, I compared the alpha options.
Alpha Scissors
This has proper z-ordering, however it's a low quality alpha. It looks fine close up, but it's ugly from a distance. Compare GD4 alpha scissors up close, far, vs true Alpha in GD3.
Alpha
Alpha has potential. I would use it if these two issues were fixed. Also notice no shadows documented in #61730.
AlphaHash
It's noisy, and has issues with some faces turning very light against the sky. It does not appear to have z-order issues, but I don't know what I'd use this for.
DepthPrePass
There are issues with having too much transparency at times, and z-order at others. All those light green branches shouldn't be visible here:
Compare with alpha scissors
Overall it doesn't look good. Alpha depth pre pass also didn't look great in GD3, but it was better than alpha or depthprepass in GD4. On the left in GD4, you can see the behind light branches popping in front of the dark front branches. On the right in GD3, you can see the low quality alpha on many of the foreground branches. The alpha really gets chopped up when layered. However the z-order is reasonable. Even in motion, I don't see any background parts obviously rendering in front.
Video
Z-ordering is difficult to see in pictures, so here is video:
https://youtu.be/4_GBr-Pzmk8
Steps to reproduce
n/a
Minimal reproduction project
test_zorder.zip
updated 12/19/22
The text was updated successfully, but these errors were encountered: