godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.13k stars 69 forks source link

Sprite3D: Z-index, sorting layers and sorting groups #3986

Open neikeq opened 2 years ago

neikeq commented 2 years ago

Describe the project you are working on

A side-scrolling 3D game that's mostly 2D sprites but also has some 3D objects.

Describe the problem or limitation you are having in your project

There's no way to properly control the order in which sprites render in 3D. This often results in issues like a background sprite rendering over a foreground sprite, or Z-fighting of sprites in the same Z coordinate.

These are suggestions I found in other issues, and here I explain why they don't solve this issue:

Describe the feature / enhancement and how it helps to overcome the problem or limitation

What are Sorting Groups?

Some games have single multi-sprite entities. Check the following example:

Screenshot from 2022-02-17 04-09-33

This very handsome guy is made out of 4 sprites: head shape, 2 eyes, and mouth. They all have the same Z coordinate, so in order for the eyes and mouth to render in front of the head shape, we increment their Z-index (or Render Priority).

Now, if we instantiate this entity twice and make them overlap, instead of one of the entities rendering entirely in front of the other one, only the eyes and mouth do:

Screenshot from 2022-02-17 04-10-26

The same happens in the 2D engine if you use Z-index for this. However, there you can use the node order in the scene tree instead of Z-index (you use Z-index in world, but the scene tree order inside individual entities), so this is not really a problem there.

We can solve this in 3D with Sorting Groups. Think of them as dynamic nested Z-index/sorting layers. You can add a SortingGroup node to your scene (like you would do with the YSort node):

- Entity
    - SortingGroup
        - Sprites...
    - PhysicsBody

The SortingGroup node has its own Z-index property to determine its rendering order. The Z-index of all children sprites applies only within that SortingGroup. This way, if there are two instances of this entity, all the sprites in the Sorting Group of one of the entities will render before any of the other one.

In brief, Sorting Groups allow you to create a whole character made out of multiple sprites and to sort it as a single entity.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

Z-index

Same as 2D nodes. A property in Sprite3D and AnimatedSprite3D. Maybe a material property? (for third-party sprite implementations).

The range has to be larger than Render Priority. 255 is too little. 8192 like in 2D is acceptable, if it goes along Sorting Layers, otherwise 65535 would be better (like in Unity).

Sorting Groups

A node, similar to YSort. It has a Z-index property. Sorting Groups can be nested within other sorting groups.

For a more detailed explanation, see the manual page for Sorting Groups in Unity.

Sorting Layers

Like I said, this is optional and there's already a specific proposal for it: #3971.

A dropdown property that accompanies Z-index, similar to Unity:

154395368-fcc7f8b8-2794-48f3-8c8b-c857bfae114f

There can be up to 32 sorting layers, and they can be re-arranged:

https://user-images.githubusercontent.com/7718100/154410030-941e9a06-e2af-4bcc-99b7-392bc1ced28a.mp4

Reference links

Since this is heavily inspired from Unity, here are some reference links:

If this enhancement will not be used often, can it be worked around with a few lines of script?

Not from what I know.

Is there a reason why this should be core and not an add-on in the asset library?

I suppose this needs to be implemented in the render pipeline.

golddotasksquestions commented 2 years ago

It seems to me you bring a Unity mindset to Godot here. These are two very different traditions. Godot already has a 2D engine, which at least in my experience already covers all 2D sorting issues.

The Unity reference examples are unfortunately not good at all, since they are focusing on "2D" games made in Unity. These sort of games you would typically in Godot distinct 2D environment, not in 3D.

For example, using the 3D engine for the first link you referenced would definitely not be the first choice. This is a game which could possibly benefit a lot from being done in 2D. I dare to say 99% of Godot users who know Godot would create this in 2D. The same goes for the other reference video you shared. A sidescroller like this would definitely be a 2D engine choice if made in Godot.

In Godots 2D as you mentioned sorting is not really an issue at all. Ysort and the scene hierarchy alone can handle all that easily. The 2D "Sorting Layers" proposal https://github.com/godotengine/godot-proposals/issues/3971 you referenced unfortunately apparently comes from a similar mindset, asking for Unity features without having fully understood how the Godot 2D approach really works.

But let's talk about 3D games. After all you are proposing an enrichment to Sprite3D, which is a 3D node.

If you would want to use composite Sprites for a Sprites3D texture in a 3D environment, you would currently simply add them as children to a Viewport, then use a ViewportTexture for your Sprite3D.

image

This is done in seconds and offers you the whole wealth of 2D features already available in Godot's 2D rendering engine. All of Godots 2D automatic sorting solutions already included. Yes, Viewports are annoyingly buggy sometimes. But why not fix these issues rather than add yet another different, but much more limited workflow?

Zireael07 commented 2 years ago

This is a neat workaround, but note that Viewports got significantly costlier in Godot 4.

golddotasksquestions commented 2 years ago

@Zireael07 I was testing this in Godot4 alpha2, but SubViewports seem to be currently completely broken. image

I also tried to do the same with CanvasGroups, which could be less costly (?), but either that's not possible or I could not figure out how.

neikeq commented 2 years ago

@golddotasksquestions The links I referenced are not examples of games that require this feature, they're videos explaining Sorting Groups and Sorting Layers. The game I mentioned at the start is a game that's mainly sprite based, but also renders some 3D objects, hence why it can't be done in the 2D engine. The 3D objects need to render with perspective alongside the sprites, hence why this can't be a viewport in 2D. Think of games like Rayman Legends (there's 3D bosses) and Ori (the water).

The viewport solution only solves the sorting groups problem, but all the other problems are still there. Besides, I'm afraid having a lot of viewports could have a very negative effect on performance (and I haven't really had a good experience getting the results I want with 2D in 3D viewports tbh).

Regarding Sorting Layers, it depends whether it's for the 2D or 3D engine. It's true 2D has CanvasLayers, yet I would still consider this feature beneficial. This proposal is about 3D though, and there are no CanvasLayers in 3D. Using viewports for this is not viable because you would need a lot of them to keep depth including separate viewports for sprites rotated on the Y-axis.

aaronfranke commented 2 years ago

@neikeq Can you explain why you can't use the Z position to sort things by Z index? I don't understand why we need to have support for multiple Sprite3D objects in the same exact position and allow changing the rendering order of them. You could just change the position of one of the objects by 0.0001 on the Z axis.

neikeq commented 2 years ago

There's transparency sorting issues when sorting based on depth.

lentsius-bark commented 2 years ago

@aaronfranke as an avid user who tries pushing as much diegetic/spatial UI into my games (which are always 3d) as possible, one common use case in which i would welcome z sorting on 3d sprites is when:

in such a scenario, offsetting the sprite3d works only until the camera hasn't rotated enough for their pivots to switch rendering priority, and the user has to create a script that rotates their parent based on camera. The workaround naturally works and there are others, though being able to sort them in a different way would certainly speed things up and make them easier, especially given that using a sprite3d is a useful way when going for a diegetic/spatial ui in your 3d game.

(note: I abuse sprite3Ds / animatedSprite3Ds for this :P, panels, damage numbers, names, health/armor/energy bars, selection boxes)

OhiraKyou commented 1 year ago

This comment was originally posted to #7714. Copied it here for visibility in it's most relevant context.

Explicit sorting groups are important for environmental art as well.

In Unity, there is a SortingGroup component that can be used to force a group of objects to be sorted together as if they were one object, with an explicit sorting order integer (which can be negative) that is relative to its parent sorting. By nesting these, an explicit sorting hierarchy can be constructed.

I often use this to create water surfaces from layers of modular effects rather than monolithic shaders. The water surface, as a whole, is given a sorting group with a default sorting order (0) so that it is sorted along with the rest of the scene geometry as if it were a single object. Then, within that water surface object, each layer (child object) is given its own sorting group to make the internal layer sorting order explicit.

Example water layers list:

I also explicitly sort water underside layers after the underwater light rays so that the rays are correctly rendered before the topside layers but after the underside layers (underside > rays > topside). This ensures correct sorting when viewed from underwater.

Example layered water screenshot from my Unity project:

2023-06-08  Nytro - Layered Water Surface Effects - 2 (WIP)