Skip to content
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

Advanced render items sorting / customizable render lists #13732

Closed
coder89 opened this issue Mar 30, 2018 · 8 comments
Closed

Advanced render items sorting / customizable render lists #13732

coder89 opened this issue Mar 30, 2018 · 8 comments

Comments

@coder89
Copy link

coder89 commented Mar 30, 2018

Hi,

In my current project I have to implement some complex 2D objects (multilayered GUI) and those will be anchored in 3D space.

I have noticed lots of artifacts on those planar 2D objects caused by z-index sorting of 2D layers in the same GUI object. There are few ways to solve it:

  • disable sorting and use global THREE.Object3D.renderOrder to sort items - this is not the best option as I would need to keep track of THREE.Object3D.renderOrder globally and update it on many objects if only one item gets inserted in the middle.
  • render to off-screen texture first - could work but my GUI is quite dynamic and I gain no perf when doing so and it requires me to update render targets on window / GUI resize.
  • apply some small Z epsilon value to separate layers so depth buffer works fine - tried that but this doesn't work 100%. I don't want to limit myself on how far/close to the object the camera can be.
  • Create multiple scenes and do few separate render passes - won't work for my project as it would make my project non-extensible or at least it would make it very hard.

None of the above are good enough nor easy to implement. IMO the cleanest solution to the problem (which I implemented and tested) was to modify the way WebGLRendererList sorts items by adding possibility to group render items into some kind of layers and then play with rather "local" than "global" THREE.Object3D.renderOrder property.

The easiest implementation of this feature would look like this:

Object3D.js

class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}

RenderItem.js

class Object3D {
  // Group render items by this value
  renderOrderGroup: number;
}

WebGLRendererLists.js

// Render item would look something like this
renderItem = {
   id: object.id,
   object: object,
   geometry: geometry,
   material: material,
   program: material.program,
   renderOrder: object.renderOrder,
   renderOrderGroup: object.renderOrderGroup,
   z: z,
   group: group
};

// Sorting extended by this
function painterSortStable(a: RenderItem, b: RenderItem) {
    if (a.renderOrderGroup !== b.renderOrderGroup) {
        return (a.renderOrderGroup || Number.MIN_SAFE_INTEGER) - (b.renderOrderGroup || Number.MIN_SAFE_INTEGER);
    } 
    // ... followed by rest of the sorting comparisons
}

Being able to group items with layers is a nice and clean way to solve the limitation of ThreeJS (big one in my project at least) when working with GUI/2D objects embedded in 3D space.

Another way (even more extensible IMO) would be to add support for customizing sorting logics so app can do whatever crazy stuff they want (i.e. via data stored in THREE.Object3D.userData property).
One would simply pass custom sorter into the THREE.WebGLRendererLists or even pass entirely new implementation to the THREE.WebGLRenderer.renderLists property.

I could make a PR with either of the proposed changes if anyone thinks it is a good idea. Otherwise I will stick with my little hack where I override the prototype of the WebGLRendererLists - ugly but works for me now. :)

Thanks,
Lukasz

@coder89 coder89 changed the title Feature request: Advanced render items sorting / customizable render lists [Feature request & implementation proposal] Advanced render items sorting / customizable render lists Mar 30, 2018
@donmccurdy
Copy link
Collaborator

donmccurdy commented Mar 30, 2018

disable sorting and use global THREE.Object3D.renderOrder to sort items - this is not the best option as I would need to keep track of THREE.Object3D.renderOrder globally and update it on many objects if only one item gets inserted in the middle.

I'm not sure I understand this concern — .renderOrder values do not need to be contiguous, just comparable. Could you use increments of ~1000 to separate groups using the .renderOrder property?

@Mugen87
Copy link
Collaborator

Mugen87 commented Mar 30, 2018

You also try to set .depthWrite to false of the respective material. Depending on your use case, this might be an option to solve z-fighting.

@coder89
Copy link
Author

coder89 commented Mar 30, 2018

@Mugen87 I forgot to mention this -> all of the layers (except the last one on the top) have depthWrite set to false.

@donmccurdy Lets say I implement a surface that will render list of items/images on the surface in the order of insertion. I would like to have many of those layers and would prefer not to set renderOrder externally (i.e. by the object which creates those surfaces) to simulate grouping. It's about code readability and encapsulation of the rendering logic. The surface creator doesn't need to know that this object creates <1000 layers and that it actually depends on the rendering order. If I would like to ship my components as a library then this would could become an API design weakness.

@Mugen87 Mugen87 changed the title [Feature request & implementation proposal] Advanced render items sorting / customizable render lists Advanced render items sorting / customizable render lists Apr 3, 2018
@Makio64
Copy link
Contributor

Makio64 commented Apr 6, 2018

@coder89 For the 'readability and encapsulation' of the rendering logic you should add a level of abstraction to your gui managing internaly the renderOrder via a system of DisplayObject / DisplayObjectContainer => parse every frame and setting the right renderOrder.

Keep it independent from threejs will make the code much easier and reusable.

@mrdoob
Copy link
Owner

mrdoob commented Apr 6, 2018

Any chance you can create a jsfiddle to visualise the issue?
From the top of my head, 1000 layers/objects is not a good thing.

@TerenceZ
Copy link

Recently, I've encountered the render order problem. I have multiple layers (just like the layers in AE / PS) in the scene, and if their depths (distances to camera) are the same, the later added layer should be render on top of the previous one, otherwise, just act as the default render order sort strategy in Three.js.

Of course, I can use a manager to handle renderOrder and update them in every frame. But this is not a good idea. Because if I use renderOrder, it means the sortObjects should be true, and projectObject will calculate objects' projected z component, but it's calculated in my renderOrder manager (duplicated computation, and to compute z more efficiently, we should filter objects that need computation, which is one part of works in projectObject).

If I don't use set sortObjects, I have to sort objects by my self, which means I should implement most logic of projectObject.

Another simpler way is to hack in render list's sort function to compare userData.someUserRenderOrder if render objects' z not equal.

Based on the above cases, if the feature to customize render sort function is added, no hacks are needed and keep things simple.

@mrdoob
Copy link
Owner

mrdoob commented Sep 24, 2019

So the solution here is being able to do renderer.setCustomSort( mySort )?

@Mugen87
Copy link
Collaborator

Mugen87 commented Feb 11, 2020

#16909 added support for custom sort functions.

@Mugen87 Mugen87 closed this as completed Feb 11, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants