mrdoob / three.js

JavaScript 3D Library.
https://threejs.org/
MIT License
101.78k stars 35.3k forks source link

Inherit group render order #20857

Open Korijn opened 3 years ago

Korijn commented 3 years ago

Is your feature request related to a problem? Please describe.

We frequently work with deep scene graphs featuring lots of 2D objects in the same Z-plane. Render order is our go-to feature to ensure things are drawn on top of each other in the correct order.

I began to notice that our code was getting littered with haphazardly organized render order assignments, and after some digging I found out that render lists sort objects by groupOrder first and renderOrder second. So far, so good. However, an object's groupOrder is apparently determined by its closest ancestor Group, and not by any Groups higher up in the scene graph. That's very surprising behavior!

For example, consider the following tree:

I would expect mesh A to be drawn on top of Mesh B. However, the opposite happens, and Mesh B is drawn on top of mesh A.

This is because currently, only the renderOrder attribute of the closest ancestor Group is actually taken into account when sorting.

Describe the solution you'd like

For renderOrder of all parent nodes (and not just Group objects either, since any Object3D can have children and a renderOrder) to be considered when sorting the render list.

I wouldn't know off the top of my head what would be the shortest route to an implementation though.

Describe alternatives you've considered

Well, currently we are sprinkling our code with (many) additional renderOrder assignments to inherit renderOrder from parent groups manually.

gkjohnson commented 3 years ago

I have a series of PRs that would make controlling group render order a lot easier and more intuitive the first of which is #19526. In short it adds a flag to Group that enables all children to be sorted and rendered together recursively. I would really like to get feedback on that change because I agree -- the current behavior can be very confusing.

Korijn commented 3 years ago

@gkjohnson That's great news, I'm just wondering why the fixation on Group? Shouldn't the behavior generalize across all Object3Ds?

gkjohnson commented 3 years ago

Semantically Group is intended to be a container object under which children are to be added. The renderer also uses it as a hint to know that the object itself is not renderable. Object3D is just a common parent class. Do you have a use case for adding the above behavior to Object3D that otherwise can't be achieved with Group?

Korijn commented 3 years ago

Well, a simple example of a non-Group container is a car Mesh that has four children tire Meshes. But I must admit I can't see a use case for renderOrder in that scenario.

My core argument however is that other Object3Ds than Group are sometimes also containers. Also, just because I can't think of an example, doesn't mean they aren't out there. :) What do you make of that?

Edit: Let me put it differently. From a user standpoint (reading API docs and examples), I can see that we have an Object3D abstraction that implements the concept of a scene graph (transforms, children, etc), and it has a renderOrder property. Just from that bit of information alone I would expect the following tree to work just as I had described earlier:

In this case however, renderOrder is completely ignored by the current implementation of render lists, because these aren't Groups. Which seems silly!

gkjohnson commented 3 years ago

It isn't my decision to organize these things or dictate behavior the classes but either way I don't see a strong case for changing the behavior from operating on Group. I also think whether renderOrder applies to Object3D is an orthogonal problem to addressing the issue of hierarchical render order so it should probably be adjusted in a separate effort if it's going to be changed.

Korijn commented 3 years ago

Ok, I'm fine with that! Thank you for answering my questions 😃

nzjony commented 1 year ago

I ran into this problem today. I have a THREE.Group (which represents a layer) and I assign it some renderOrder. The children is a list of THREE.Groups, each of which represent a building (which is composed of multiple meshes).

However the child THREE.Group's renderOrder overwrites the parent THREE.Group's renderOrder.

So when I want to ensure that a given layer is rendered before another layer, the children group's overwrite it, because of the line: groupOrder = object.renderOrder;, in the WebGLRenderer.js file in the function: function projectObject( object, camera, groupOrder, sortObjects )

The solution I have is to disable sorting, which works as long as the objects are added in the correct order, but it misses some flexibility I would like to have with sorting via renderOrder.

Maybe a short term solution would be to log to the console when a THREE.Group is added to a THREE.Group to warn that changes to the renderOrder don't propagate.