Hi there,
I need to obtain a list of objects that are visible to a particular camera. Each player has a camera, but the server also has an overhead camera, so I can’t just use OnBecameVisible to flag objects, since the server will see things individual players might not.
I’ve thought about using Camera ray-tracing (prohibitively inefficient) and Camera.OnWillRenderObject, but haven’t come across an elegant solution yet.
What would you suggest as a method of listing the objects visible to a given camera?
Thanks,
Ahonek
After writing some code to flag objects when they are about to be rendered using Camera.OnWillRenderObject, I’ve discovered that it’s awkward enough that someone must have a better solution. Any ideas?
Ahonek
Rather than going through all objects in the scene manually and checking if they can be seen, I’d recommend you first try and limit the collection of objects to be considered as much as possible.
Now, I’ve not directly worked with your use case. I usually do similar checks to determine which objects can be seen by AI characters, but you should be able to use the same method just fine.
First off I limit the number of objects to consider by defining a range of sight. I use this range in a call to Physics.Check sphere which gives me a list of all colliders within a certain radius of a given point. You can even limit this collection more by specifying a layer mask.
Secondly, I check the angle between the forward vector of the camera and the vector between the camera and the object. As an example, here’s a snippet from a script where I have some debug GUI rendered for my character, which I only want to show if the character is in front of the camera:
const float kDebugAreaWidth = 300.0f, kDebugAreaHeight = 100.0f;
protected virtual void OnGUI ()
{
if (
!m_ShowDebug ||
Vector3.Angle (transform.position - Camera.main.transform.position, Camera.main.transform.forward) > 90.0f
)
{
return;
}
Vector2 screenPosition = Camera.main.WorldToScreenPoint (transform.position);
GUILayout.BeginArea (
new Rect (screenPosition.x, Screen.height - screenPosition.y, kDebugAreaWidth, kDebugAreaHeight),
GUI.skin.GetStyle ("Box")
);
OnDebugGUI ();
GUILayout.EndArea ();
}
Now I just clamp at 90 degrees in this example, but you could of-course tweak to whatever fits your setup.
With all this said and done, OnWillRenderObject will give you the most accurate result.
Thanks for your reply - that is indeed a solution that I’ve considered. Note that my game environment is very small; this is mainly an experiment in novel AI techniques, so an accurate model of precisely what an agent can see is more what I’m looking for, performance is a secondary consideration.
A problem that I’ve run into with your vector comparison suggestion is that an object whose body is large enough to be seen on the screen when its center is off screen (a large object like a wall, for example) won’t show up in the list. As a solution to this, I’ve considered allowing a tolerance margin on the vector difference representing an object’s size, but the coordinate geometry involved when taking into account an object’s rotation and distance from the camera is not insignificant.
The AI will only need to resynchronize its model of the world on the order of once per second, so I think it won’t be a huge problem to use a brute force method. However, if I run into performance issues, I will certainly keep your suggestion in mind.
Thanks
Ahonek
Yea if your objects are indeed that big, one thing you could do is, in stead of looking for the object position, look for the point along vector V, clamped to a magnitude of the objects radius, which is closest to the position P, where:
- V = The vector from the position to the position projected onto W.
- W = The vantage point forward vector with the magnitude of the distance between the object and the vantage point.
- P = The intersection point between V and W.
Could you somehow do a sweepcast that would maintain the same dimensions as the view frustrum?
The sample code on this page looks pretty interesting, might help.
Good suggestion, legend, but it doesn’t take into account objects that are obscured by other objects. For example, with my camera up against a wall, I need my script to return only the wall, not the tables, chairs, and people of the dinner party happening behind it.
That said, that’s a great way to narrow the objects down to the ones within the view volume. Any ideas for detecting only non-obscured objects, given a list of objects whose bounds lie within the frustum? This is a nontrivial problem on its own (the problem of clipping), but are there any built-in methods within Unity for deciding whether an object is being hidden by another?
Did anyone figure this out?
1 Like