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?

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

public Rect BoundsToScreenRect(Bounds bounds)
{
	// Get mesh origin and farthest extent (this 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);
}

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, this 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.

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;
     }

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(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;
        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);
    }

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

/// <summary>
/// Returns a rectangle on this canvas that fully encloses the given GameObject.
/// </summary>
/// <param name="canvas">The canvas on which 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] this 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);
  }