felixmariotto / three-mesh-ui

⏹ Make VR user interfaces for Three.js
https://felixmariotto.github.io/three-mesh-ui/#basic_setup
MIT License
1.26k stars 134 forks source link

rendering ui elements double sided #233

Closed hschwane closed 1 year ago

hschwane commented 1 year ago

I recently switched to three mesh UI as the main way of generating 3d UI elements for our application (after having problems with the three.js CSS3D renderer). Everything is working well so far and I read Typescript support will be improved in version 7 so I am looking forward to that.

I have one Feature request:

Most of my UI elements float freely in 3d space, and they disappear if the user looks at them from the back side. I found #65 and am currently using the approach suggested there (manually creating two of every UI element). Many of my UI elements are dynamic (changing while the app runs), however, and some can be a bit complex. It is a rather annoying to have to manage and synchronize two identical elements for every piece of ui I use. It also leads to twice as much update and rendering logic that needs to be run by the library.

I think it would be very helpful if the library had an option to render ui elements double sided, so one does not need to do that manually.

swingingtom commented 1 year ago

Hi @hschwane , thanks for your interest.

If you only want doubleSide on Block/Box/Frame

v6.x.x

From frame material

import {DoubleSide} from "three";
myBlock.frame.material.side = DoubleSide;

v7.x.x

it is "fully" implemented as property

import {DoubleSide} from "three";

// during creation
new ThreeMeshUI.Block({ backgroundSide: DoubleSide });

// by set()
myBlock.set({ backgroundSide: DoubleSide });

// by getter
myBlock.backgroundSide = DoubleSide;

If you only want doubleSide on Text

v7.x.x

Same as above, replace background by font

import {DoubleSide} from "three";
new ThreeMeshUI.Text({ fontSide: DoubleSide });
myText.set({ fontSide: DoubleSide });
myText.fontSide= DoubleSide;

v6.x.x

A bit more difficult, as meshes for text are not directly available after object creation. We need to wait to be built, and redo each time it changes.

// listen for mesh creation/update
myText.onAfterUpdate = () => {
    if (myText.children.length > 0) {
      myText.children[0].material.side = DoubleSide;
    }
  };

And of course, you are welcome to participate into the typescript support in 7.x.x

swingingtom commented 1 year ago

And if you want both doublesided, you would probably need to set depthWrite false on the backgroundMaterial. Which may cause other issues in your rendering setup....

swingingtom commented 1 year ago

@hschwane feel free to reopen if it didnt fixed your issue

hschwane commented 1 year ago

@swingingtom sorry for not getting back to you earlier. I tried what you suggested and it works, but only solves my problem partially. I managed to enable the backside for the frame as well as text, which works well on transparent frames (where I only use the frame to create a border). For opaque backgrounds using depthWrite false is not really a good solution as other object can also be rendered on to of the frame (even if they should be behind it). Also, if the text has a notable z-offset from the frame looking at it from the backside looks wired. On top of that, for our application we want the text to be readable normally from the backside (so it is not mirrored). So only disabling culling won't do the trick for us. For now we stay with using two sets of ui components. Maybe when implementing the DoubleSided Property you mentioned above you can come up with something to work around those issues during rendering?

swingingtom commented 1 year ago

Maybe when implementing the DoubleSided Property you mentioned above you can come up with something to work around those issues during rendering?

Not really. On 7.x.x implementations of properties like .side or .renderOrder only act as mediation over Threejs object properties.

However, on 7.x.x Behaviors can now be used, implemented, and also shared from three-mesh-ui examples folder : https://github.com/felixmariotto/three-mesh-ui/wiki/Behaviors
That would be nice to have a AlwaysLTR behavior that ensure that texts are always rendered LeftToRight by camera. The same could be made for inverting z-offset too.

What do you think?

hschwane commented 1 year ago

sure, if behaviours allow that kind of stuff, it will be a good solution. Especially as I suspect this is a somewhat unusual requirement. Looking forward to a 7.x release already :D