- Home /

# Is there an easy way to get on-screen render size (bounds)?

I need to quickly find the on-screen bounding box of a given object or mesh. Do I have to iterate convert all the points to world space and compute my own?

**Answer** by karl_
·
May 07, 2012 at 03:25 PM

If anyone is interested in Matt's solution, here is my implementation.

```
public Rect BoundsToScreenRect(Bounds bounds)
{
// Get mesh origin and farthest extent (t$$anonymous$$s works best with simple convex meshes)
Vector3 origin = Camera.main.WorldToScreenPoint(new Vector3(bounds.min.x, bounds.max.y, 0f));
Vector3 extent = Camera.main.WorldToScreenPoint(new Vector3(bounds.max.x, bounds.min.y, 0f));
// Create rect in screen space and return - does not account for camera perspective
return new Rect(origin.x, Screen.height - origin.y, extent.x - origin.x, origin.y - extent.y);
}
```

Edit: Here's a better implementation that accounts for rotation and scale:

```
public static Rect GUIRectWithObject(GameObject go)
{
Vector3 cen = go.renderer.bounds.center;
Vector3 ext = go.renderer.bounds.extents;
Vector2[] extentPoints = new Vector2[8]
{
HandleUtility.WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z-ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z-ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z+ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z+ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z-ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z-ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z+ext.z)),
HandleUtility.WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z+ext.z))
};
Vector2 min = extentPoints[0];
Vector2 max = extentPoints[0];
foreach(Vector2 v in extentPoints)
{
min = Vector2.Min(min, v);
max = Vector2.Max(max, v);
}
return new Rect(min.x, min.y, max.x-min.x, max.y-min.y);
}
```

Many thanks, Have pinched the fragment entire just to save me rebuilding this particular wheel

...annnnd as an extension method :

```
using UnityEngine;
using System.Collections;
public static class BoundsExtensions
{
public static Rect ToScreenSpace(this Bounds bounds, Camera camera)
{
var origin = camera.WorldToScreenPoint(new Vector3(bounds.min.x, bounds.min.y, 0.0f));
var extents = camera.WorldToScreenPoint(new Vector3(bounds.max.x, bounds.min.y, 0.0f));
return new Rect(origin.x, Screen.height - origin.y, extents.x - origin.x, origin.y - extents.y);
}
}
```

You have a typo. Should be:

var origin = camera.WorldToScreenPoint(new Vector3(bounds.min.x, bounds.max.y, 0.0f));

actually it returns 0,0,0

z should be:

```
bounds.center.z;
```

instead of z being 0.0f

karl_'s updated answer references a UnityEditor namespace class. The method can be decompiled to:

```
public static Vector2 WorldToGUIPoint(Vector3 world)
{
world = Handles.matrix.MultiplyPoint(world);
Camera camera = Camera.current;
if (!camera)
{
return new Vector2(world.x, world.y);
}
Vector2 screenPoint = camera.WorldToScreenPoint(world);
screenPoint.y = (float)Screen.height - screenPoint.y;
return GUIClip.Clip(screenPoint);
}
```

**Answer** by Bampf
·
Mar 01, 2011 at 12:55 AM

You could get the bounding volume using Renderer.bounds, then convert those coordinates to screen coordinates. The min and max of the screen X & Y coordinates will be a bounding box.

However, t$$anonymous$$s is only an approximation. Because it is axis-aligned, AND because the camera angle will distort the shape further. You might end up with a box that's bigger than necessary depending on the shape. It will work better for compact, convex objects.

Right, I thought of that, and passed on it for those reasons. What I'm going for is the old 'trombone' shot used by Steven Spielberg and the like, where the 'target object' stays the same size on screen, but the camera moves way back while zooming in at the same time (or vice versa). That approximation would mess a bit with the actual extent of the rendered object. I guess I should try it, but I think for my purposes it will be too distorted.

Actually, this effect was invented by Alfred Hitchcock in Vertigo.

**Answer** by Luloak2
·
May 23, 2016 at 08:10 PM

sgoodrow's answer is not 100% correct in Unity 5 anymore (GUIClip is missing), updated (and combined) the code:

```
public static Rect GUIRectWithObject(GameObject go)
{
Vector3 cen = go.GetComponent<Renderer>().bounds.center;
Vector3 ext = go.GetComponent<Renderer>().bounds.extents;
Vector2[] extentPoints = new Vector2[8]
{
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y-ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y-ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z-ext.z)),
WorldToGUIPoint(new Vector3(cen.x-ext.x, cen.y+ext.y, cen.z+ext.z)),
WorldToGUIPoint(new Vector3(cen.x+ext.x, cen.y+ext.y, cen.z+ext.z))
};
Vector2 min = extentPoints[0];
Vector2 max = extentPoints[0];
foreach (Vector2 v in extentPoints)
{
min = Vector2.Min(min, v);
max = Vector2.Max(max, v);
}
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
}
public static Vector2 WorldToGUIPoint(Vector3 world)
{
Vector2 screenPoint = Camera.main.WorldToScreenPoint(world);
screenPoint.y = (float) Screen.height - screenPoint.y;
return screenPoint;
}
```

**Answer** by pointcache
·
Jul 15, 2017 at 04:58 PM

An optimized runtime version of the above with zero allocations and garbage. Will work with UGUI if you have anchor and pivot at 0,0 (lower left corner is the start of coordinates). Swap collider with renderer if you need.

```
public static Rect GetScreenRect(t$$anonymous$$s Collider collider) {
Vector3 cen = collider.bounds.center;
Vector3 ext = collider.bounds.extents;
Camera cam = Camera.main;
float screenheight = Screen.height;
Vector2 min = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z - ext.z));
Vector2 max = min;
//0
Vector2 point = min;
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//1
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z - ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//2
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z + ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//3
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z + ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//4
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z - ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//5
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z - ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//6
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z + ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
//7
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z + ext.z));
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
}
```

`+1 but god, man, use a function with a 'ref vector' argument ...So many lines of nearly identical code

Well, it's an oprimised version. A method call can actually slow down the performance quite a bit if you need this literally million of times a frame. For example i also did inline some code i've written and it ran about 8 times faster. The script i posted in this answer is the "slow" version as it uses several helper methods (Clamp, FloorToInt, CeilToInt, Repeat, Color.Lerp). In the inlined version I've linked from my answer i removed all method calls. The code becomes a bit longer but runs way faster.

I would argue that this kind of optimization should be left for compiler to do ..or taken out into a c++ dll? This is definitely not as expensive as reflection, so such a tiny optimization would most likely not matter. Though maybe if it were in some super-deep recursion, it would

Have a look at the refactored version:

```
using System.Runtime.CompilerServices;
public static Rect GetScreenRect(this Collider collider) {
Vector3 cen = collider.bounds.center;
Vector3 ext = collider.bounds.extents;
Camera cam = Camera.main;
float screenheight = Screen.height;
Vector2 min = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z - ext.z));
Vector2 max = min;
//0
Vector2 point = min;
get_minMax(point, ref min, ref max);
//1
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z - ext.z));
get_minMax(point, ref min, ref max);
//2
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z + ext.z));
get_minMax(point, ref min, ref max);
//3
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z + ext.z));
get_minMax(point, ref min, ref max);
//4
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z - ext.z));
get_minMax(point, ref min, ref max);
//5
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z - ext.z));
get_minMax(point, ref min, ref max);
//6
point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z + ext.z));
get_minMax(point, ref min, ref max);
//7
point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z + ext.z));
get_minMax(point, ref min, ref max);
return new Rect(min.x, min.y, max.x - min.x, max.y - min.y);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static void get_minMax(Vector2 point, ref Vector2 min, ref Vector2 max) {
min = new Vector2(min.x >= point.x ? point.x : min.x, min.y >= point.y ? point.y : min.y);
max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
}
```

Refing a vector which is a struct causes boxing which creates garbage.

I think value-types will never create garbage in any scenario, only grow stack. They will be insta-cleaned up once used, but I might need confirmation

https://stackoverflow.com/a/36527617

That's the beauty of structs, but also their curse, since they must be fixed size, and therefore can't be inherited from (unlike classes)

I think boxing does not occur, as behind scenes, nothing will wrap struct into 'object', it simply does a c++ style & reference on it, pointing to its memory adress

also, thanks for the code :)

**Answer** by jokigenki
·
Feb 10, 2021 at 08:26 AM

If anyone wants a Canvas based version (based on Luloak2's answer):

```
/// <summary>
/// Returns a rectangle on t$$anonymous$$s canvas that fully encloses the given GameObject.
/// </summary>
/// <param name="canvas">The canvas on w$$anonymous$$ch to base the rectangle</param>
/// <param name="go">The game object to enclose</param>
/// <returns>A Rect that encloses the GameObject</returns>
public static Rect GetRect([NotNull] t$$anonymous$$s UnityEngine.Canvas canvas, [NotNull] GameObject go)
{
var canvasRT = canvas.transform as RectTransform;
var camera = canvas.worldCamera;
if (camera == null) camera = Camera.main;
var renderer = go.GetComponent<Renderer>();
if (camera == null || canvasRT == null || renderer == null) return Rect.zero;
var bounds = renderer.bounds;
var cen = bounds.center;
var ext = bounds.extents;
var extMin = cen - ext;
var extMax = cen + ext;
var extentPoints = new[]
{
new Vector3(extMax.x, extMin.y, extMin.z),
new Vector3(extMin.x, extMin.y, extMax.z),
new Vector3(extMax.x, extMin.y, extMax.z),
new Vector3(extMin.x, extMax.y, extMin.z),
new Vector3(extMax.x, extMax.y, extMin.z),
new Vector3(extMin.x, extMax.y, extMax.z),
extMax
};
var min = camera.WorldToScreenPoint(extMin);
var max = min;
foreach (var v3 in extentPoints)
{
var v = camera.WorldToScreenPoint(v3);
min = Vector2.Min(min, v);
max = Vector2.Max(max, v);
}
var sizeDelta = canvasRT.sizeDelta / 2f;
return new Rect(min.x - sizeDelta.x, min.y - sizeDelta.y, max.x - min.x, max.y - min.y);
}
```

### Your answer

### Welcome to Unity Answers

If you’re new to Unity Answers, please check our User Guide to help you navigate through our website and refer to our FAQ for more information.

Before posting, make sure to check out our Knowledge Base for commonly asked Unity questions.

Check our Moderator Guidelines if you’re a new moderator and want to work together in an effort to improve Unity Answers and support our users.

### Follow this Question

### Related Questions

Bounds Finding Box 1 Answer

drag a UI panel keeping it with the screen bounds 2 Answers

Bounds handle 0 Answers

Do the Bounds of an Mesh Change When you Change its Vertices Positions? 1 Answer