Rendering multiple elements in world space

Hello,

I’m using UI Toolkit to style and generate in real time a simple description box that appears next to an object when the player is close enough.

For this purpose I set the panel settings to output to a render texture and I’ve plastered the texture on a quad. It all works nicely, for as long as there’s only one object of course, as the render texture is common among all.

What would be the right way to achieve this? For what it’s worth, I don’t need any interaction, I’m using UI Toolkit only because I can style many popups quickly.

Thank you

First idea, create a PanelSetting asset for each box that you need. You can instantiate PanelSettings assets at runtime using the ScriptableObject.CreateInstance() method, or clone an existing PanelSettings with ScriptableObject.Instantiate().

However, since the content is static, you could probably render them once and copy the RenderTexture to a Texture2D for future use. The copy will require a Texture2D.ReadPixels(), which is slow. So you may want to do that once at the beginning of the game if doing it “live” causes stutters.

We will eventually provide a real world-space solution that won’t require intermediate render textures, so this will simplify the workflow once that’s out.

1 Like

Thanks @mcoted3d rendering the description and then copying to a texture seems like a good solution.
2 more points if you don’t mind, probably silly but I’m very new to this:

Is there a specific point or function I can call that renders the UI to the render texture? Or in other words: how do I render to the rendertexture and then just move on to the next object? Currently my code - checks removed for brevity - is as simple as this:

   void Start()
    {
        root = GetComponent<UIDocument>().rootVisualElement;
        objectLabel = root.Q<Label>("Label");
        objectDescription = root.Q<Label>("Description");

        objectLabel.text = properties.objectName;
        objectDescription.text = properties.objectDescription;
        root.style.display = DisplayStyle.Flex;
    }

And the last one: is there a way obtain the size of the bounding rectangle, for example in pixels, of the rendered texture (since it would be different depending on the name of the object and its description)?

Thank you

That’s a good question, there’s no way at the moment to force a panel to repaint itself. The panel will render itself as part of the normal rendering pipeline. Your best bet is probably to wait for the end of the frame and grab the content. Then you can process another element on the next frame:
https://docs.unity3d.com/ScriptReference/WaitForEndOfFrame.html

You can use the size of the RenderTexture, if it is appropriate. If you want to use the size of the VisualElement, you can probably use the size of the layout rect:
https://docs.unity3d.com/ScriptReference/UIElements.VisualElement-layout.html

I was thinking about this, another solution would be to render everything you need in a single RenderTexture, tiled side-by-side. Then you could extract multiple Texture2D from that RenderTexture by passing a different source rect for the Texture2D.ReadPIxels() call.

This would be a bit more work to setup, but you could potentially prepare all of your textures in a single frame.

1 Like

I managed to do a first implementation that seems to work quite alright. The approach seems a bit weird but for now it works and I don’t have too many objects so it doesn’t really add any delay (and it’s only run once).

@mcoted3d thanks for your precious suggestions and for taking the time to answer me.

1 Like