Search Unity

World Space UI scale to fit screen

Discussion in 'UGUI & TextMesh Pro' started by ldb, Dec 10, 2014.

  1. ldb

    ldb

    Joined:
    Apr 30, 2013
    Posts:
    40
    Hi,

    I'm creating a UI for the Rift, so as far as I know this means Screen Space is out and I must use World Space canvas.

    I want to have a dark, slightly transparent background overlay the screen when menu is displayed. But I can't get my canvas to fit the screen properly. I'm currently setting RectTransforms sizeDelta property to match Screen.width/Screen.height, but this doesn't seem to work. I've tried placing the canvas in front of camera , and played with various scale and pos Z values. I'll get it to fit at one resolution, but then it displays completely differently at other res'. As you can see from screen cap, canvas size matches reported screen size in stats but doesn't fit the screen properly.

    How can I get World Space Canvas to fit screen?

    Thanks

    screencap.jpg
     
    tangwentian likes this.
  2. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    why does it need to be world? try camera or overlay? those both stretch to screen automatically
     

    Attached Files:

  3. ldb

    ldb

    Joined:
    Apr 30, 2013
    Posts:
    40
    It's for the rift. Setting it to Screen Space will only render to one camera. Afaik I need to use World Space so both cameras see it. Of course I may be wrong... maybe there is some way of getting Screen Space to work with the dual camera setup of the rift?
     
  4. rakkarage

    rakkarage

    Joined:
    Feb 3, 2014
    Posts:
    683
    but you can set which camera
    upload_2014-12-10_12-28-59.png
     
    Shadoninja likes this.
  5. ldb

    ldb

    Joined:
    Apr 30, 2013
    Posts:
    40
    Yes I know, but it only lets you set a single camera. The rift uses two cameras. One for each eye. Everything I've tried with Screen Space only displays in one of the cameras, so only shows in one eye.
     
  6. XKnightmare

    XKnightmare

    Joined:
    Sep 1, 2014
    Posts:
    5
    I would recommend writing code for generation of a UI for both screens and two canvases then. Just write actions and listeners that interact with both cameras' canvases via the event system. The canvas event system is very robust and allows this functionality.
     
  7. phil-Unity

    phil-Unity

    Unity UI Lead Developer

    Joined:
    Nov 23, 2012
    Posts:
    1,226
    So there are two options for you

    1) would be the way you're trying to go about it now with the world canvas and getting it sized to screen. to do that you need to calculate the frustum size at a given distance from the camera. Mainly
    Code (CSharp):
    1. float camHeight;
    2.             if (m_Camera->GetOrthographic ())
    3.                 camHeight = m_Camera->GetOrthographicSize () * 2;
    4.             else
    5.                 camHeight = 2.0 * m_PlaneDistance * tan(Deg2Rad (m_Camera->GetFov () * 0.5));
    6.                
    7.             scale = (camHeight / screenSize.y) * m_ScaleFactor;
    **NOTE** this is c++ code it will need to be converted to C#.

    2) if you can wait for one of the next beta's where i've fixed the Camera.Render() issue not having the canvas. Once that fix it public you can then wait for end of frame, switch the canvas's camera to the other camera, call render, switch back to original camera.
     
    kcastagnini, Hamsly and tangwentian like this.
  8. hansadler

    hansadler

    Joined:
    May 12, 2014
    Posts:
    50
    Hi, this might help you. I got this from some other forum post a few weeks ago. If you setup your canvas as screen space in the editor, then when the app starts set it to world space, it will keep the same scale and position and fill the screen exactly. It works great for me.

    canvas.renderMode = RenderMode.WorldSpace;

    I use this because I want to be able to move the camera around to show the game, then show the UI, etc. Works really well.
     
    Mihai_I, maiarahmed, Lngmngx and 10 others like this.
  9. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480

    The C# if any one is looking for it
    2.0f * distanceToMain * Mathf.Tan(Mathf.Deg2Rad * (Camera.fieldOfView * 0.5f))
     
  10. alex4unity

    alex4unity

    Joined:
    Apr 28, 2013
    Posts:
    27
    Is this up2date? Cos I need a canvas in world space fit to the view of a cardboard camera! Can I use this code for this? The unity dev mentioned smth about waiting for another beta but its a while ago already...
     
  11. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    I still use the same block of code in my projects no issues of note with the current unity main build or the current beta build.
     
  12. xAdamQ

    xAdamQ

    Joined:
    Jul 23, 2016
    Posts:
    53
    Sorry for my stupidness,
    But I can't understand what distanceToMain refer to ...
    I want to know what I can do with this code to fit world canvas to the screen
     
  13. parthdarji

    parthdarji

    Joined:
    Jun 30, 2012
    Posts:
    14
    distanceToMain is the distance of the canvas from the main camera.
    Code (CSharp):
    1. distanceToMain = Vector3.Distance(mainCameraTransform.position, canvasTransform.position)
    /Parth
     
  14. eobet

    eobet

    Joined:
    May 2, 2014
    Posts:
    176
    I'm a complete newbie when it comes to this, so I'm wondering if any kind soul would care to paste a complete example on how to fit a world space canvas inside a camera?

    I do not understand why you'd only need "camHeight", as aspect ratios should be different with various Screen.width and Screen.height combinations (which is what you use to see the size of the main camera, I hope).
     
    Last edited: Oct 29, 2017
  15. Ommageden

    Ommageden

    Joined:
    Sep 24, 2020
    Posts:
    7
    I know this is old but its the first google option. Heres what I did:

    Code (CSharp):
    1. playerGameController.playerUIGameObject.GetComponent<Canvas>().renderMode = RenderMode.WorldSpace;
    2.             UnityEngine.XR.Management.XRGeneralSettings.Instance.Manager.InitializeLoaderSync();
    3.             UnityEngine.XR.Management.XRGeneralSettings.Instance.Manager.StartSubsystems();
    4.  
    5. //main code starts below
    6. //resets position of worldspace camera after switch to be in front of our face as origin
    7.             playerGameController.playerUIGameObject.transform.localPosition = new Vector3(0, 0, 0);
    8. //below sets the distance from face
    9.             playerGameController.playerUIGameObject.transform.localPosition += new Vector3(0, 0, 0.5f);
    10.             playerGameController.playerUIGameObject.transform.localScale *= 2.0f * playerGameController.playerUIGameObject.transform.localPosition.magnitude * Mathf.Tan(Mathf.Deg2Rad * (playerGameController.playerCamera.fieldOfView * 0.5f));
    11.             playerGameController.playerUIGameObject.transform.localScale /= playerGameController.playerCamera.scaledPixelHeight;
    12.             playerGameController.playerUIGameObject.transform.localScale *= VRUIScaleFactor;
    13.  
    14.             playerGameController.playerUIGameObject.transform.localRotation = Quaternion.identity;
    Where
    playerGameController.playerUIGameObject 
    is the worldspace canvas' gameobject.
    It is a child of the players camera which is why
    playerGameController.playerUIGameObject.transform.localPosition.magnitude
    is used. Scaled pixel height is what the other poster had when doing
    screenSize.y
    above in C++.
    VRUIScale
    Factor is simple a variable I set to fine tune the size to my desired settings.

    The few lines are me changing the canvas to world space and switching dynamically to VR.
     
  16. Sukirii

    Sukirii

    Joined:
    Oct 14, 2020
    Posts:
    15
    doesn't work for me with 2019 version, canvas just offset to upper right corner
     
  17. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    necro revival, but indeed this doesnt take aspect size into account. Currently searching for this exact behaviour, where I have a moving non-orthographic camera and need some world space UI to keep its relative scale. The above code works, though when my camera gets further away the UI gets slightly bigger, thus its not perfect. I assume aspect ratio is the problem here.

    My code so far:
    Code (CSharp):
    1.         float _scaleFactor = 0.1f;
    2.  
    3.         private void Scale()
    4.         {
    5.             if (_mainCamera)
    6.             {
    7.                 float camHeight;
    8.                 if (_mainCamera.orthographic)
    9.                 {
    10.                     camHeight = _mainCamera.orthographicSize * 2;
    11.                 }
    12.                 else
    13.                 {
    14.                     float distanceToCamera = Vector3.Distance(_mainCamera.transform.position, transform.position);
    15.                     camHeight = 2.0f * distanceToCamera * Mathf.Tan(Mathf.Deg2Rad * (_mainCamera.fieldOfView * 0.5f));
    16.                 }
    17.                 float scale = (camHeight / Screen.width) * _scaleFactor;
    18.                 transform.localScale = new Vector3(scale, scale, scale);
    19.             }
    20.         }

    Edit:
    Actually, the Screen.width part of things messed it up for me. I wanted to have the UI stay the same size, no matter the aspect ratio. Instead of doing:
    Code (CSharp):
    1. float scale = (camHeight / Screen.width) * _scaleFactor;
    ...I just removed the division by Screen.width, leaving me with this:
    Code (CSharp):
    1. float scale = camHeight * _scaleFactor;
    This still causes my UI to get slightly bigger when my camera is far away, but I can't seem to find out why that's the case.
     
    Last edited: Jan 18, 2022
  18. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    @Fressbrett

    Disclamer ... this is just as I understand it and is meant to add to the conversation nothing more.

    Some additional info based on assumptions of what issue your reporting

    1st the question as to why we only care about height
    FOV is an angle representing the verticle field of view so we calcualte the verticle

    The scale is the same on all axis so

    var scale = Vector3.one * (camHeight / Screen.width) * _scaleFactor;

    Would give you a Vector3 scale

    Thus if you wanted a world canvas to fit a 16:9 screen perfectly then you would set its pixle resolution to be some multiple of 16:9 i.e. 1920 by 1080 for example and then when you set its scale not its reoslution it will scale to keep that fit.

    So if your needing to fit the screen then you need to change scale by distance from camera and resolution to match screen resolution.

    2nd you said it gets a little bigger as it gets further away ... this is either due to projection or rounding.

    What I think you might be seeing in terms of width is due to projection

    Having a wider screen like my 32:9 vs a 21:9 vs a 16:9 etc. will cause distortion as you approch the left and right extent of the screen this is due to the projection.

    In short a perspective camera is applying a projection matrix that causes a distortion toward the left and right edges of the screen. This effect gives you a since of depth and angle.

    If you want to see this in its extreem just turn the FOV up really hgih ... put a cube in the middle of the screen and now rotate the camera on its Y axis so your looking left and right ... you will notice that as the box moves toward the edge of the screne even though its distance from the camera remains a constant that it appears to stretch.

    As to the rounding point ... your starting at 0.1 float gets sloppy with very small and very large numbers so if your scale starts to get into 0.000001 youll notice it switches to notation in display and starts to lose some precision as you keep goign ... same issue that makes cameras shake further from origin (0,0,0) and that make shaddows flicker fruther from origin (0,0,0)

    If I assumed what you where saying wrong sorry.
    The math provided is correct in that it find the relative scaler that is the apparent difference in size of an object based on its distance from the screen and the FOV of the camera. Things that will make it less than perfect are as I noted being off center of the screen and having a higher FOV, and I suppose at extream values (very small scale or very large scale) floating point could be an issue
     
    Fressbrett likes this.
  19. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    Today I wanted to get back to this weird problem of my world-space-canvas appearing bigger when closer the the screen edge. I tested out multiple FOVs, and it actually appears to be a projection-distortion problem - the formula seems to be correct. The scale adjusts accordingly as it should depending on camera distance, but the wider the FOV, the bigger the canvas appears when closer to the screen-edge.

    As of yet I haven't found a workaround to compensate for this scale-distortion. It would be nice if I could calculate a distortion factor based on my screen position and the FOV, which I could then apply to my relative scaling logic. I will post an update if I find anything.
     
  20. Loden_Heathen

    Loden_Heathen

    Joined:
    Sep 1, 2012
    Posts:
    480
    You can get at the camera projection matrix (Camera.projectionMatrix) but I am not sure what use that will be for you ... I would need to understand your use case better.

    If you want a "world space" UI then you want it to respond to the projection matrix that is the distortion as it approaches the edge of the screen is desired.

    If you do not want that then I dont think you want a "world space" UI I think you want a "screen space" UI that simply locates its self over top the world object ... that is a MUCH simpler question ...

    Point in case fighter jet HUD, you know the lock-on icons that appear over targets ... those are Screen Space elements that are simply located to be over top the world space target. Thus they do not distort based on the screen and they maintain a constant size on screen
    Same applies for health bars and similar in most games they are actually screen space elements that are simply mapping there position to the screen space projection of some world space object.

    Now there is another style where you have a truly world space UI that is, it is effected by projection and thus has a since of "depth" and very often we will scale these objects up and down so they remain readable regardless of depth. This however doesn't attempt to remove the distortion of projection only to scale up and down such that the world space UI element often a chat bubble or similar remains readable even at distance. With this sort of UI element we often do have it scale up and down so as to not negate the since of depth we simply clamp it at a max up and a min down to insure it remains readable regardless of great distance and that it doesn't flood the screen when right up on the camera.

    PS:
    If you wanted to create a world space UI that appeared like a screen space UI it would have to be a curved UI such that it wrapped around the camera to be "perfect". Again this is odd but as I recall there is an asset that does that on the asset store. A flat UI will always present a distortion as the FOV is projecting that curved field of view to give a since of depth and angle.
     
    Last edited: Jun 30, 2022
    Fressbrett likes this.
  21. Fressbrett

    Fressbrett

    Joined:
    Apr 11, 2018
    Posts:
    97
    You make a good point, I could just compute the corresponding screen space position for my world space canvas and display it on a screen space canvas. Far simpler solution than trying to compensate for projection distortion, which is, as you said, often even wanted.
     
  22. realfancy

    realfancy

    Joined:
    Oct 29, 2018
    Posts:
    1
  23. maiarahmed

    maiarahmed

    Joined:
    Jun 13, 2022
    Posts:
    1
    Thanks a million for this!! :)