godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
89.95k stars 21.07k forks source link

Vulkan: Transparency pipeline has z-order issues #63675

Open TokisanGames opened 2 years ago

TokisanGames commented 2 years ago

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.

image

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.

image

Alpha

Alpha has potential. I would use it if these two issues were fixed. Also notice no shadows documented in #61730.

  1. It's too transparent. You can see the grid right through it. Compare with gd3 alpha above. The pine needles look like a ghost tree.

image

  1. Some faces have the incorrect z-order, showing transparent objects that are behind it, in front. Look at the grid showing through the bottom of the image, too transparent. Another alpha z-order example is coming up below in the depthprepass section, and see the video at the bottom for more egregious examples.

image

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.

image

image

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:

image

Compare with alpha scissors image

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.

image

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

TokisanGames commented 2 years ago

Turning on gizmos has no effect on these issues. #62457

TokisanGames commented 2 years ago

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.

TokisanGames commented 2 years ago

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.

image

On the pine tree model, Alpha shows behind faces in front.

image

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.

image

This shot is alpha mode, which is all backwards.

image

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.

image

Alpha scissors shows proper order

image

Sources: https://sketchfab.com/3d-models/rock-free-56592b65d43d4be1b9291fdefb979032 https://sketchfab.com/3d-models/pine-tree-d45218a3fab349e5b1de040f29e7b6f9

clayjohn commented 1 year ago

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".

0stamina commented 1 year ago

As of Beta 8 the issue seems to be resolved for materials within the same mesh, but for whatever reason, in my current project separate meshes are being rendered in the same order regardless of the actual depth. This may also have something to do with the grid always rendering in front of a transparent material.

image image The alpha of the red cylinder is 0.9, setting it 1.0 removes any ordering issues.

It seems like the z-ordering for transparent materials somehow negates the z-ordering of the individual meshes. I'm not familiar with the Godot source code so I'm hoping someone more knowledgeable than me will be able to fix it using this information.

TokisanGames commented 1 year ago

As of Beta 8 the issue seems to be resolved for materials within the same mesh,

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 4828ac6b9.

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.

image

Compare w/ alpha scissors

image

clayjohn commented 1 year ago

Taking another look at this in Beta 11 and I have a few more observations

The Rock

The 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.

3.5.1 Screenshot from 2023-01-11 13-48-26

Beta 11: default

Screenshot from 2023-01-11 13-52-28

Beta 11: back face culling enabled

Screenshot from 2023-01-11 13-52-36

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 Tree

I 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 https://github.com/godotengine/godot/pull/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:

Proximity fade still turns objects inside out and no shadows

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

Alpha still has no z-order or shadows

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.

Alpha depth prepass has shadows, but still has artifacts w/ being too transparent and back faces showing in front at some times under certain conditions, eg dark bg light fg or the opposite (which may not be z-order).

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.

~Unfortunately it seems that both TAA and alpha to coverage need work to be production ready. Hopefully we will be able to fix them soon.~

Thanks to Calinou for tracking down the issue and bisecting, I was able to get alpha to coverage working again tonight in https://github.com/godotengine/godot/pull/71261 Using alpha scissor with alpha to coverage should give you pretty good results. I include screenshots of the tree model in the PR

Calinou commented 1 year ago

The 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.

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.

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.

See https://github.com/godotengine/godot/issues/64501.

TokisanGames commented 1 year ago

Rock:

Trees:

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.

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.

image

Calinou commented 1 year ago

There are no shadows w/ proximity fade.

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 transparency properties.

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]

[^1]: This means the current approach of only enabling alpha blending during the LOD transition would no longer be possible, but I wonder whether it's actually a good approach to use. This will cause transparency sorting issues to occur only within the LOD transition, which greatly increases the chance of visible pop-in and decreases performance during the LOD transition. Source 1 games use this approach to LOD for world models and it's rarely pleasant to look at.

Ansraer commented 1 year ago

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). However, I haven't looked at the asset importer in Godot before, so I have no clue how difficult it would be to implement this and whether or not it would be out of scope.

Calinou commented 1 year ago

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).

clayjohn commented 1 year ago

@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 sorting_offset

Calinou commented 1 year ago

And on a side note sharpening is very much needed.

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).

As is textures are painfully blurry. On the right side I'm using FSR at 95% to mitigate it for the time being, but that kills edges, of course. And implementing it on the user side is costly (and motion vectors are not generated for moving textures, so no TAA (though I would prefer SMAA, I'm really hoping it won't be forgotten)). Picture clarity is very important, at least to me.

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.

ghost commented 1 year ago

it is hard to tell what the issue is from your screenshots but I don't think it is the same as this issue

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. The issue happens when I change the camera to a more acute angle. I could make a video if that helps. EDIT: When camera is rotated the origin of the object behind gets closer to the camera and, I guess, that causes it to render in front, and its edges become jagged, but only in the area that is overlapping.

For your use case you should look into using sorting_offset

Thank you, I'll look into it.

Enable anisotropic filtering on the materials, and increase the anisotropic filter level to 16×

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.

If you really want sharpening right now, you can enable FSR 1.0 and set the 3D resolution scale to 99% instead of 95%

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.

There's a https://github.com/godotengine/godot/pull/47401, but it needs further work

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.

Calinou commented 1 year ago

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.

If sorting_offset doesn't work for your use case, this needs support for order-independent transparency, which is very expensive and not planned for 4.0 (or even 4.1).

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.

ghost commented 1 year ago

@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. In any way alpha hash won't cut it for me, and going pure 2d has its own issues and limitations that I don't want to deal with. I hope sorting_offset and some camera magic would do the trick.

EDIT: yeah, it works, so if I limit the camera movement and avoid overlapping triangles in my transparent meshes I might manage it. I understand things I'm trying to do are not common, but if it were I wouldn't be here. Also about OIT, AAA games usually have very complex meshes, large textures and a lot of overdraw, in my case I could probably get away with it. My project is long term and in a very early stage, so Godot 4 has a lot of time to mature. Really, I just wanted to address my concerns so that I can stop worrying about it and focus on the fun stuff.

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. Might be a bit too sharp/grainy though, so sharpening filter is still desired.

TokisanGames commented 4 months ago

Alpha mode ignores Z ordering. Use alpha depth pre-pass, or alpha scissor.

HKunogi commented 4 months ago

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.

shadowfox87 commented 4 months ago

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 https://github.com/godotengine/godot/issues/43253 as well as https://github.com/godotengine/godot/issues/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.

Calinou commented 4 months ago

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,

Can you upload a minimal reproduction project?

shadowfox87 commented 4 months ago

Yes, alphaclip3d.zip

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. @Calinou

Calinou commented 4 months ago

@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.

shadowfox87 commented 4 months ago

@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.

Calinou commented 4 months ago

@shadowfox87 In your MRP, it seems setting Render Priority to 1 on the sword sprite fixes the sorting issue. You can also use Sorting > Offset in the VisualInstance3D section of the inspector if you need more control.

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).

HKunogi commented 4 months ago

@shadowfox87 In your MRP, it seems setting Render Priority to 1 on the sword sprite fixes the sorting issue. You can also use Sorting > Offset in the VisualInstance3D section of the inspector if you need more control.

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. A simple example, if you make an card with several transparent sprites to build it up, then you stack several of them, close to each other, if you use render priority on certain parts of it, then it would render on top of cards that are in front of those cards on the stack, making impossible to stack them or put one in front of the other properly. If i remove the alpha by using alpha scissor or any of those tricks, then it starts to work perfectly fine, but then it deteriorates the quality of text and also makes impossible to have translucent areas on the design.

shadowfox87 commented 4 months ago

Here, I've attached another MRP where the changing the Rending Priority to 1 will not work. What Kunogi said above is correct. alphaclip3d.zip

lyuma commented 4 months ago

Hey, just wanted to say that I've been playing with the project.

Let's go back to two comments.

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 sorting_offset You can also use Sorting > Offset in the VisualInstance3D section of the inspector if you need more control.

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. For sprites not at 0,0,0 like the swords icon, we can solve it by using the Custom AABB together with the Use AABB Center. sorting_offset_approach There are three main challenges with using Custom AABB:

  1. You'll need to calculate extents that are big enough to cover the entire rendered area of the sprite, from that center point we are using for sorting. For dynamic Label3Ds, make sure to make it large enough to cover whatever long text string might show up.
  2. Godot's AABB inspector uses origin / extents, not center / extents. This means whenever you change extents, you should use -Extent/2 for the origin point. (the negative corner of the bounding box).
  3. We want the AABB to be centered, so once you have calculated -Extent/2, you need to subtract the node's position relative to the center point. This can get complicated if it is nested a few nodes down.

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. alphaclip3d_issue63675_sorting_order.zip

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 _ready... I don't know how to keep it in sync all the time without a performance cost, but maybe it can have a manual update function that re-calculates the sorting offsets if needed.

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.
shadowfox87 commented 4 months ago

@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?

HKunogi commented 4 months ago

Hey, just wanted to say that I've been playing with the project.

Let's go back to two comments.

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 sorting_offset You can also use Sorting > Offset in the VisualInstance3D section of the inspector if you need more control.

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. For sprites not at 0,0,0 like the swords icon, we can solve it by using the Custom AABB together with the Use AABB Center. sorting_offset_approach There are three main challenges with using Custom AABB:

  1. You'll need to calculate extents that are big enough to cover the entire rendered area of the sprite, from that center point we are using for sorting. For dynamic Label3Ds, make sure to make it large enough to cover whatever long text string might show up.
  2. Godot's AABB inspector uses origin / extents, not center / extents. This means whenever you change extents, you should use -Extent/2 for the origin point. (the negative corner of the bounding box).
  3. We want the AABB to be centered, so once you have calculated -Extent/2, you need to subtract the node's position relative to the center point. This can get complicated if it is nested a few nodes down.

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. alphaclip3d_issue63675_sorting_order.zip

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 _ready... I don't know how to keep it in sync all the time without a performance cost, but maybe it can have a manual update function that re-calculates the sorting offsets if needed.

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.

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).

champbob commented 3 months ago

Running into Z order issues myself, and I set up a simple project to try to understand what was going on before finally running into this thread. image Godot Z Depth Transparency Shader Test.zip

clayjohn commented 3 months ago

@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. https://docs.godotengine.org/en/latest/tutorials/3d/3d_rendering_limitations.html#transparency-sorting

champbob commented 3 months ago

@clayjohn Ah, thanks for the explanation! Apologies for misinterpreting my issue!

dhoverml commented 3 months ago

@clayjohn

I have a scene with two quads, slightly offset. Both quads have a material with these options set: transparency = Depth Pre-Pass shading_mode = Unshaded

On Forward+, there is no bug. On Mobile, when viewed where the center of the quad in the back is closer to the camera, the quad in the back renders on top of the quad in front (see screenshots below). This is likely due to the explanation you gave above, though I would've expected this for "transparency = Alpha", not for "transparency = Depth Pre-Pass". quads_good quads_bad

Is "'transparency = Depth Pre-Pass' is broken on Mobile" a separate bug? I couldn't find any related documentation.

Calinou commented 3 months ago

Is "'transparency = Depth Pre-Pass' is broken on Mobile" a separate bug? I couldn't find any related documentation.

The Mobile rendering method doesn't use a depth prepass for performance reasons, so it can't work there.

clayjohn commented 3 months ago

@dhoverml You are actually running into a different bug. Its already reported here: https://github.com/godotengine/godot/issues/92292. The depth-prepass transparency option is broken in the Mobile and Compatibility backends.