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)
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.
Excellent solution, thanks Wolfram, I will try as suggested.
– anon76620