1
\$\begingroup\$

Imagine you'd like to highlight a 3D GameObject by drawing a (translucent) UI panel above it. The UI panel should cover the object exactly. Therefore the object's bounds, as it is seen from the camera, are needed. How do you get these bounds? (In word space, or screen, viewport or canvas space, any way's fine).

My problem is that [Collider|Renderer|Mesh].bounds does not take the camera's position into account. Hence, depending on the camera's location and the object's dimensions, the bounds are more or less off. Do you know how this can be fixed?

Any advise is very much appreciated. Thank you!

\$\endgroup\$
5
  • 1
    \$\begingroup\$ What kind of object? The most general solution is to transform every vertex into screen space and take the min/max of the results. But if you have a narrower class of objects you care about then we may be able to compute the bounds more efficiently than that. \$\endgroup\$
    – DMGregory
    Commented Dec 3, 2020 at 22:20
  • \$\begingroup\$ @DMGregory, thanks for your help! That sounds very interesting. See, the object is wrapped in a box - regular six sided "cuboid". It's the box's visible bounds on the screen I'm looking for. \$\endgroup\$ Commented Dec 4, 2020 at 2:23
  • 1
    \$\begingroup\$ Then that's trivial: project the 8 corners to the screen using WorldToScreenPoint. \$\endgroup\$
    – DMGregory
    Commented Dec 4, 2020 at 2:24
  • \$\begingroup\$ Yes it is. Thanks for pointing to WorldToScreenPoint(). \$\endgroup\$ Commented Dec 7, 2020 at 16:28
  • 1
    \$\begingroup\$ If you've solves your problem, I'd bet other users would appreciate learning from it if you posted it as an Answer. 🙂 \$\endgroup\$
    – DMGregory
    Commented Dec 7, 2020 at 16:38

1 Answer 1

2
\$\begingroup\$

As DMGregory pointed out, the solution is quite simple: Just iterate through the vertices of a mesh, convert them from world to screen coordinates and store the min and max values of both x and y axis. In code it looks like this:

private List<Vector3> vertices = new List<Vector3>();

public Rect GetBoundingBoxOnScreen(Mesh mesh, Camera camera) {
    mesh.GetVertices(vertices);
    Rect retVal = Rect.MinMaxRect(float.MaxValue, float.MaxValue, float.MinValue, float.MinValue);

    for (int i = 0; i < vertices.Count; i++) {
        Vector3 v = camera.WorldToScreenPoint(vertices[i]);
        if (v.x < retVal.xMin) {
            retVal.xMin = v.x;
        }

        if (v.y < retVal.yMin) {
            retVal.yMin = v.y;
        }

        if (v.x > retVal.xMax) {
            retVal.xMax = v.x;
        }

        if (v.y > retVal.yMax) {
            retVal.yMax = v.y;
        }
    }

    return retVal;
}        
     

The min and max corners of the returned rectangle are them passed to RectTransformUtility.ScreenPointToLocalPointInRectangle() to convert them from screen to canvas coordinates. And that's it. @DMGregory, again, thank you for your advice.

\$\endgroup\$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .