Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
8
Question by DaveA · Mar 01, 2011 at 12:34 AM · meshscreenboundsbox

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?

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

5 Replies

· Add your reply
  • Sort: 
avatar image
7

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 (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);
 }
Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Duke Euphoria · Nov 02, 2012 at 02:05 PM 0
Share

$$anonymous$$any thanks, Have pinched the fragment entire just to save me rebuilding this particular wheel

avatar image duke · Apr 07, 2013 at 02:41 AM 0
Share

...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.$$anonymous$$.x, bounds.$$anonymous$$.y, 0.0f));
         var extents = camera.WorldToScreenPoint(new Vector3(bounds.max.x, bounds.$$anonymous$$.y, 0.0f));
 
         return new Rect(origin.x, Screen.height - origin.y, extents.x - origin.x, origin.y - extents.y);
     }
 }
avatar image Davitosan · Aug 08, 2013 at 10:47 PM 0
Share

You have a typo. Should be:

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

avatar image sdgd · Mar 09, 2014 at 09:04 PM 0
Share

actually it returns 0,0,0

z should be:

 bounds.center.z;

ins$$anonymous$$d of z being 0.0f

avatar image sgoodrow · Dec 08, 2014 at 06:49 AM 1
Share

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

 public static Vector2 WorldToGUIPoint(Vector3 world)
 {
     world = Handles.matrix.$$anonymous$$ultiplyPoint(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);
 }


avatar image
6

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

Comment
Add comment · Show 2 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image DaveA · Mar 02, 2011 at 12:09 AM 0
Share

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 zoo$$anonymous$$g 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.

avatar image PatHightree · Apr 05, 2012 at 02:47 PM 1
Share

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

avatar image
3

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

Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Antypodish · Jun 25, 2017 at 07:03 PM 0
Share

Working also fine in Unity 2017 beta. thx

avatar image
1

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(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);
     }
Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image IgorAherne · Sep 12, 2017 at 08:38 PM 0
Share

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

avatar image Bunny83 IgorAherne · Sep 12, 2017 at 08:59 PM 0
Share

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.

avatar image IgorAherne Bunny83 · Sep 13, 2017 at 12:53 AM 1
Share

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 $$anonymous$$ = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z - ext.z));
     Vector2 max = $$anonymous$$;
  
     //0
     Vector2 point = $$anonymous$$;
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //1
     point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z - ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
  
     //2
     point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y - ext.y, cen.z + ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //3
     point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y - ext.y, cen.z + ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //4
     point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z - ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //5
     point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z - ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //6
     point = cam.WorldToScreenPoint(new Vector3(cen.x - ext.x, cen.y + ext.y, cen.z + ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     //7
     point = cam.WorldToScreenPoint(new Vector3(cen.x + ext.x, cen.y + ext.y, cen.z + ext.z));
     get_$$anonymous$$$$anonymous$$ax(point, ref $$anonymous$$, ref max);
  
     return new Rect($$anonymous$$.x, $$anonymous$$.y, max.x - $$anonymous$$.x, max.y - $$anonymous$$.y);
 }
 
 
 [$$anonymous$$ethodImpl($$anonymous$$ethodImplOptions.AggressiveInlining)]
 static void get_$$anonymous$$$$anonymous$$ax(Vector2 point, ref Vector2 $$anonymous$$, ref Vector2 max) {
     $$anonymous$$ = new Vector2($$anonymous$$.x >= point.x ? point.x : $$anonymous$$.x, $$anonymous$$.y >= point.y ? point.y : $$anonymous$$.y);
     max = new Vector2(max.x <= point.x ? point.x : max.x, max.y <= point.y ? point.y : max.y);
 }
avatar image pointcache · Sep 12, 2017 at 08:57 PM 0
Share

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

avatar image IgorAherne pointcache · Sep 13, 2017 at 12:35 AM 0
Share

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 :)

avatar image
0

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 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);
   }
Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

11 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

Bounds Finding Box 1 Answer

Determine if two meshes overlap, without using bounds or trigger? 0 Answers

Stop the player at center of cube mesh 2 Answers

Box/mesh collider 0 Answers

Getting Screen Bounds 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges