3D plane width in pixels - WorldToScreenSize?

I am building a GUI using 3D plane objects and found this answer for positioning those planes in screen space: http://answers.unity3d.com/questions/7067/create-an-object-in-screen-space-coordinate

now is there a way to set the sizes of the planes relative to the screen? So i can for example set the size of that plane in pixels.

here's how my code looks like so far to create a plane at the bottom left corner of the screen:

function Start () {
    var screenPoint     = Vector3(0,0,20);
    var plane           = GameObject.CreatePrimitive(PrimitiveType.Plane);
    var worldPos        = Camera.main.ScreenToWorldPoint(screenPoint);
    plane.transform.position = worldPos;
    plane.transform.Rotate(-90,0,0);
} 

how can set that plane size to 100x100 pixels? is there any convenient conversions I can use?

thx

3 Answers

3

What I usually do for screenspace aligned 3D objects, is attach an empty object to the camera, and set its transform via script to:

  • localPosition=(0,0,0.5/Mathf.Tan(Camera.main.fieldOfView/2*Mathf.Deg2Rad))
  • localEulerAngles=(-90,0,0) // so that the unity plane stands upright in XY
  • localScale=(1,1,1)

As a result of that, this plane (the plane mathematically defined as "y=0") is now aligned in a normalized screen space. Any object you attach to it will be positioned so that

  • position (0,0,0) is the center of the screen
  • position (0,0,0.5) is the top of the screen
  • position (0,0,-0.5) is the bottom of the screen
  • position (0.88889,0,0) is the right edge for a 16:9 aspect ratio (0.5*16/9=0.88889), e.g. for 1920x1080 resolutions
  • position (-0.88889,0,0) is the left edge
  • position (0.66667,0,0) is the right edge for a 4:3 aspect ratio (0.5*4/3=0.66667), e.g. for 1024/768 resolutions

For a Unity plane, set the scale to (-xSize/10,1,-ySize/10) (the /10 is required because Unity planes are sized 10x10, the negative values are required to fix the UV orientation of the plane for texturing.

For example, to place a 100x100 pixel sized, correctly oriented Unity plane in the lower left corner at a resolution of 1024x768 (4:3), attach the plane to the object above, and set its transform to:

  • localPosition=(-(1024/2-50)/768.0,0,-(768/2-50)/768.0 (50 pixels off the corner)
  • localScale=(-100/768.0/10,1,-100/768.0/10)

Excellent solution, thanks Wolfram, I will try as suggested.

extending Wolfram’s answer i wrote a function that takes the transform of a plane object and converts it to screen rectangle:

public Rect GetElementScreenRect(Transform go)
{

    Rect result = new Rect();

    result.width = ScreenHeight * 10 * go.localScale.z;
    result.height = ScreenHeight * 10 * go.localScale.x;
    result.x = 0.5f * ScreenWidth + go.localPosition.x * ScreenHeight - result.width * 0.5f;
    result.y = ScreenHeight - ScreenHeight * (0.5f + go.localPosition.z) - result.height * 0.5f; //a slight difference...
    
    return result;
}

For perspective projection, here is the fomular for calculating the actual pixel size of ONE UNIT (size of a default cube) in Untiy3D (dz stands for the distance between the object and the camera):

pixelSize = screenHeight / 2f / dz / tan(fov/2f);

that means, if you want your object to has the pixel perfect you will need to set its localScale to 1/pixelSize. A Plane is 10 unit by default, so if you want it to be 1000 pixels, you will need to set (pseudo code)

scl = 2f / screenHeight * (plane.z - camera.z) * Mathf.tan(camera.fov/2f * Mathf.PI/180);
plane.localScale = 0.1 * 1000 * scl;

I looked around a lot on the web but didn’t see anyone mention this clearly, so I post here and hope it will work for you.

One side note : the plane will look a bit blurry when the screen size is NOT EVEN so for that case you will need to shift a little bit (0.5 pixels) on each axis (x and z). Just take that case into calculation.

This is a bit old but if you are still around, could you please explain what is 2f? I need this for a Flare3D project (rather than Unity) so I have to know how to convert it for my platform :-) Edit : Hohum, I get it now, it's "2.0" (float), sorry!