Jump to content
Unity Insider Forum

Position in RenderTexture auf Objektbasis ermitteln?


Hellhound

Recommended Posts

Hallo zusammen,

aktuell render ich ein 3D Objekt in eine Textur (2048x1600) um es als UI Element anzeigen zu können. Hierfür habe ich ein UI Panel (1400x286) an Position 0,0 mit Pivot 0,0 und mit einem RawImage (2048x1600)  als Kind an Position 653,153 mit einem center Pivot (0.5,0.5) . Sowohl das Panel als auch das RawImage haben Ihren Anchor links unten. Soweit so gut, das Objekt wird wie gewünscht, als UI Element mittig auf dem Bildshirm gerendert. Nun besitzt das 3D Objekt diverse leere Objekte als Transformationspunkte um Mountpoints zu definieren. Meine Ziel ist es nun in der UI auf der angezeigten Textur an der entsprechenden Stelle des Mountpoints kleine Sprites anzuzeigen um diesen zu visualisieren.

Wie mache ich das am besten? Üblicherweise kann ich ja eine Position in der Textur per Raycast ermitteln, wenn ich aus der UI komme, z.B. um eine Kollison mit einem Klick zu ermitteln. Nur wie bestimme ich die Position in der Textur auf Basis des Objektes unter Berücksichtigung der UI-Parent Elemente?  Zunächst habe ich folgendes ausprobiert:

public Vector3 GetUIPosition(Vector3 mountpoint)
{
    RectTransform rect = this.GetComponentInParent<RectTransform>();
    Debug.Log("Preview Rectangle " + rect.sizeDelta.ToString());                                                                       

    Vector2 viewPos = camera.WorldToViewportPoint(mountpoint);
    Debug.Log("Viewport pos : " + viewPos.ToString());

    Vector2 localPos = new Vector2(viewPos.x * rect.sizeDelta.x, viewPos.y * rect.sizeDelta.y);
    Debug.Log("Viewport pos local : " + localPos.ToString());
      
    return new Vector3(localPos.x, localPos.y, 1.0f);
}

Das liefert mir jedoch viel zu große Werte. Irgendwie stehe ich grade auf dem Schlauch und bin dankbar für jede Anregung.

Viele Grüße

Hellhound

Link zu diesem Kommentar
Auf anderen Seiten teilen

Also nach meinen Erfahrungen mit der Unity-UI, kann ich nicht empfehlen, die UI und 3D-Objekte innerhalb der Texturen zu mixen. Was ein gangbarer Weg ist, ist die Kopplung eines 3D-Objektes und eines Canvas als Ganzes. Alles andere wird spätestens zu diversen Problemen führen, wenn sich die Auflösung des Spiels verändert (dabei schlägt nämlich der Canvasscaler wieder zu und er verschiebt die Auflösung und die Proportionen seiner UI-Elemente). Dabei ändert sich meist auch die Auflösung und die Proportionen des Canvas in Relation zum Screen. Das führt vermutlich wiederum dazu, daß Ankerpunkte in Relation zu einer Textur des Canvas unterschiedlich berechnet werden müssen und eine korrekte Positionierung aus den Positionen der Objekte der 3D-Szene wird nahezu rechnerisch unmöglich (da man hier nachvollziehen müsste, was der Canvasscaler in Unity intern alles so treibt).

Link zu diesem Kommentar
Auf anderen Seiten teilen

Bedeutet eigentlich nur, daß ich empfehle für die Ankerpunkte an der Waffe jeweils ein eigenes WorldSpace-Canvas zu verwenden. Das Canvas wird dabei einfach an die Position des 3D-Modell ausgerichtet und nicht UI-Elemente innerhalb des Canvas.
Die andere Variante wäre ein Overlay-Canvas  und dann versuchen die Panels an die betreffenden Positionen zu setzen, aber hier muss man eben auf die aktuelle Größe des Canvas achten und dann relativ zu den Canvas Koordinaten berechnen. Gleichzeitig muss man natürlich auch die Screenkoordinaten als Relativkoordinaten bestimmen und dann ins Verhältnis zum Canvas setzen. Nachteil der 1. Variante ist, daß wenn man nicht aufpasst natürlich die UI verdeckt werden kann.
 

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Habe die Worldspace-Canvas Variante ausprobiert. Leider kann ich diese nicht nutzen, da das Objekt das in die Textur gerendert wird, rotiert werden kann. Somit kollidiert es ab einem bestimmten Winkel mit dem Objekt und wird dann wie Du schon vermutet hast, vom Objekt verdeckt. Bleibt somit nur Variante zwei, die habe ich jedoch noch nicht ganz verstanden. Sobald ich ein Overlay-Canvas erstelle wird dieses an den kompletten Camera Viewport angepasst nun verstehe ich nicht ganz, wie ich darin die Panels ausrichten soll, relativ zum Objekt ...

Link zu diesem Kommentar
Auf anderen Seiten teilen

Du hattest bei deiner Lösung auf jeden Fall den Offset des Canvas nicht beachtet. Das Canvas hat seinen Koordinatenursprung in der Mitte, WorldToViewport() unten links.

Hier sind 2 Varianten, kannst du ja mal testen:

using UnityEngine;
 
 /// <summary>
 /// Place an UI element to a world position
 /// </summary>
 [RequireComponent(typeof(RectTransform))]
 public class PlaceUIElementAtWorldPosition : MonoBehaviour
 {
     private RectTransform rectTransform;
     private Vector2 uiOffset;
     
     /// <summary>
     /// Initiate
     /// </summary>
     void Start ()
     {
         // Get the rect transform
         this.rectTransform = GetComponent<RectTransform>();
 
         // Calculate the screen offset
         this.uiOffset = new Vector2((float)Canvas.sizeDelta.x / 2f, (float)Canvas.sizeDelta.y / 2f);
     }
 
     /// <summary>
     /// Move the UI element to the world position
     /// </summary>
     /// <param name="objectTransformPosition"></param>
     public void MoveToClickPoint(Vector3 objectTransformPosition)
     {
         // Get the position on the canvas
         Vector2 ViewportPosition = Camera.main.WorldToViewportPoint(objectTransformPosition);
         Vector2 proportionalPosition = new Vector2(ViewportPosition.x * Canvas.sizeDelta.x, ViewportPosition.y * Canvas.sizeDelta.y);
         
         // Set the position and remove the screen offset
         this.rectTransform.localPosition = proportionalPosition - uiOffset;
     }
 }

Variante 2:

 //this is your object that you want to have the UI element hovering over
 GameObject WorldObject;
 
 //this is the ui element
 RectTransform UI_Element;
 
 //first you need the RectTransform component of your canvas
 RectTransform CanvasRect=Canvas.GetComponent<RectTransform>();
 
 //then you calculate the position of the UI element
 //0,0 for the canvas is at the center of the screen, whereas WorldToViewPortPoint treats the lower left corner as 0,0. Because of this, you need to subtract the height / width of the canvas * 0.5 to get the correct position.
 
 Vector2 ViewportPosition=Cam.WorldToViewportPoint(WorldObject.transform.position);
 Vector2 WorldObject_ScreenPosition=new Vector2(
 ((ViewportPosition.x*CanvasRect.sizeDelta.x)-(CanvasRect.sizeDelta.x*0.5f)),
 ((ViewportPosition.y*CanvasRect.sizeDelta.y)-(CanvasRect.sizeDelta.y*0.5f)));
 
 //now you can set the position of the ui element
 UI_Element.anchoredPosition=WorldObject_ScreenPosition;

 

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ich konnte das Problem lösen. Habe noch einmal auf Deinen ursprünglichen Vorschlag zurückgegriffen und die WorldSpace-Canvas Container genutzt, da ich es mit dem Overlay nicht lösen konnte. Habe im WorldSpace-Canvas für das Image ein Billboard-Rendering eingebaut und den Shader für das Image gemäss dieses Links angepasst. Und siehe da, es klappt. Auch wenn es jetzt organisatorisch etwas unschön ist, das UI-Elemente plötzlich am Objekt sind.

Mountpoints.JPG

Link zu diesem Kommentar
Auf anderen Seiten teilen

Ja, ist doch gut. Vielleicht noch drauf achten, das daß Mipmapping bei den Texturen deaktiviert ist (sollte es nicht von Haus aus schon als Sprite markiert sein, da hier denke ich MM bereits ausgestellt ist). Möchte man performancetechnisch den letzten Rest ausquetschen möchte, dann könnte man noch den Billboardrenderer mit einer Textur verwenden, aber das Handling ist etwas kompliziert. Hat den Vorteil das Billboard wird hier über den Shader gedreht und nicht mehr über ein Skript (ich glaube der Renderer selbst dreht das Billboard nicht, aber der Billboardshader ist in Unity verfügbar):
https://docs.unity3d.com/Manual/class-BillboardRenderer.html

Man weisst dem Billboardrenderer eine Wordposition und ein Asset zu. Das Asset enthält dabei die Definition für die Grafik und den Shader. Das Asset muss man über ein Skript erzeugen (ich habe Code dafür wenn es jemand mal brauchen sollte). Macht aber erst wirklich Sinn, wenn du relativ viel solche kleinen Billboardgrafiken darstellen musst, ansonsten lohnt der Aufwand denke ich nicht.

Link zu diesem Kommentar
Auf anderen Seiten teilen

Archiviert

Dieses Thema ist jetzt archiviert und für weitere Antworten gesperrt.

×
×
  • Neu erstellen...