Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Convert from world space coordinate to pixel of render texture

Discussion in 'General Graphics' started by tgaldi, Oct 28, 2015.

  1. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    Hey,

    I have a scene with a two camera setup. The main camera is perspective, and displays a UI that includes a panel with a render texture (render texture does not take up the entire UI). The Render Texture is created by another camera, which is orthographic, looking down -y, and provides a god's eye view of the world. I have objects in the world and need to take their world position and translate them into screen space positions of the first camera to use for UI buttons, parented to the render texture panel (so that the button positions match the world positions of the objects within the render texture).

    Currently I've done:

    Vector3 ConvertWorldToScreen( Vector3 positionIn ) {

    RectTransform rectTrans = this.GetComponentInParent<RectTransform>();
    Vector3 screenCoord = orthoCam.WorldToScreenPoint( positionIn );
    Vector3 worldCoord = RectTransformUtility.PixelAdjustPoint( screenCoord, rectTransform.transform, rectTrans.GetComponentInParent<Canvas>() );

    Vector2 offset = rectTrans.offsetMin;
    return new Vector3( offset.x + worldCoord.x, offset.y + worldCoord.y,1f );​
    }

    This seems to work but the points are off a bit. I believe they need to be scaled by the same amount the render texture is scaled across the rectangle panel? Not sure if that is correct or how to perform the scaling.

    Thanks for any assistance.
     
  2. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    I've tried scaling by the render texture size:

    Vector3 ConvertWorldToScreen( Vector3 positionIn ) {

    Texture tex = this.GetComponentInParent<RawImage>();
    RectTransform rectTrans = this.GetComponentInParent<RectTransform>();
    Rect rect = rectTrans.rect;

    Vector3 screenCoord = orthoCam.WorldToScreenPoint( positionIn );
    Vector2 scalePoint = new Vector2( ( screenCoord.x * tex.width ) / rec.width, ( screenCoord.y * tex.height ) / rec.height );
    Vector2 offset = rectTrans.offsetMin;
    return new Vector3( offset.x + scalePoint.x, offset.y + scalePoint.y,1f );​
    }

    But the location and scaling of the icons from the correct pixel locations is still off.
     
  3. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    I think I'm close:

    Vector3 ConvertWorldToScreen( Vector3 positionIn ) {

    RectTransform rectTrans = this.GetComponentInParent<RectTransform>();

    Vector2 viewPos = orthercam.WorldToViewportPoint( positionIn );
    Vector2 localPos = new Vector2( viewPos.x * rectTrans.sizeDelta.x ), ( viewPos.y * rectTrans.sizeDelta.y ) );
    Vector3 worldPos = rectTrans.TransformPoint( localPos );

    return new Vector3( worldPos.x, worldPos.y, 1f );​
    }

    The positions in relation to each other (local positions) are correct, and they are all in the correct x position in relation to the render texture / rectangle panel (world position). The problem I'm having is that the y positions (world positions) are too high, in that they should be near the bottom of the panel but are at the top and above it.

    I'm kinda flying by the seat of my pants here, so any input is welcome.

    Thanks.
     
    Last edited: Oct 29, 2015
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
  5. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
  6. tgaldi

    tgaldi

    Joined:
    Oct 28, 2015
    Posts:
    102
    The solution is to subtract the sizeDelta from the final y coordinate:

    Vector3 ConvertWorldToScreen( Vector3 positionIn ) {

    RectTransform rectTrans = this.GetComponentInParent<RectTransform>();​

    Vector2 viewPos = orthercam.WorldToViewportPoint( positionIn );
    Vector2 localPos = new Vector2( viewPos.x * rectTrans.sizeDelta.x ), ( viewPos.y * rectTrans.sizeDelta.y ) );
    Vector3 worldPos = rectTrans.TransformPoint( localPos );

    return new Vector3( worldPos.x, worldPos.y - rectTrans.sizeDelta.y, 1f );​
    }

    This threw me off because they still will not line up if you don't have maximize on play enabled, or if your screen resolution is different from your canvas rez. I'm assuming the real problem has to do with aspect ratio, but I don't have time to delve any deeper. If someone wants to enlighten me on why this solution works, I'd be interested to know.

    Hope this helps anyone in the future.
     
    HenryChinaski likes this.
  7. WarArm

    WarArm

    Joined:
    Dec 4, 2015
    Posts:
    4
    Hello, so this solution doesn't work if you don't habe maximize on play enabled right or if your screen resolution is different from your canvas resolution, right?
     
  8. HiziProjects

    HiziProjects

    Joined:
    Nov 29, 2013
    Posts:
    4
    Good day

    I'm from the future, year 2018. Faced a similar issue, and well.. I wanted it to work with any resolution, and figured a solution that somehow works based on your code.

    I figured I'd share it.

    public Vector3 ConvertWorldToScreen (Vector3 positionIn)
    {
    RectTransform rectTrans = this.GetComponentInParent<RectTransform>(); //RenderTextHolder

    Vector2 viewPos = otherCam.WorldToViewportPoint (positionIn);
    Vector2 localPos = new Vector2 (viewPos.x * rectTrans.sizeDelta.x, viewPos.y * rectTrans.sizeDelta.y);
    Vector3 worldPos = rectTrans.TransformPoint (localPos);
    float scalerRatio = (1 / this.transform.lossyScale.x) * 2; //Implying all x y z are the same for the lossy scale

    return new Vector3 (worldPos.x - rectTrans.sizeDelta.x / scalerRatio, worldPos.y - rectTrans.sizeDelta.y / scalerRatio, 1f);
    }

    Thankies for sharing your code in the first place, and hope that'll save the life of.. somebody
     
  9. TJaenichen

    TJaenichen

    Joined:
    Oct 12, 2014
    Posts:
    29
    Thanks!
     
  10. icepower2012

    icepower2012

    Joined:
    Jan 14, 2015
    Posts:
    7
    Thank you very much! I searched a lot answers but not work. You Save My Life! I think Unity should change the WorldToScreenPoint to your ConvertWorldToScreen solution.
     
  11. SomeHumbleOnion

    SomeHumbleOnion

    Joined:
    Jan 18, 2021
    Posts:
    28
    Thanks so much for this it's been a huge help! One thing I had trouble with still was getting the UI to work with different screen sizes. To fix this, I took the scaleFactor of the parent canvas and multipled that to the scalerRatio which seems to do the trick! I've added my version below, hope this helps someone!


    public Canvas canvasTransform;

    private void Start()
    {

    scaleFactor = canvasTransform.scaleFactor;
    }

    public Vector3 ConvertWorldToScreen(Vector3 positionIn)
    {
    Vector3 viewPos = cam.WorldToViewportPoint(positionIn + offset);
    Vector2 localPos = new Vector2(viewPos.x * rectTrans.rect.width, viewPos.y * rectTrans.rect.height);
    Vector3 worldPos = rectTrans.TransformPoint(localPos);
    float scalerRatio = ((1 / transform.lossyScale.x) * 2) * scaleFactor;

    return new Vector3(worldPos.x - rectTrans.rect.width / scalerRatio, worldPos.y - rectTrans.rect.height / scalerRatio, 1f);
    }
     
    Ex6tra likes this.