Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question How to tell if *entire* object is visible in camera bounds

Discussion in 'Scripting' started by jrhager84, Jan 31, 2023.

  1. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    I am trying to detect if an object is visible in the bounds of the camera, but it only originates from the center of the object. The object is a sphere, so I was thinking about having multiple rays being cast from the upper Y and X and lower Y and X, but I'm thinking there might be a more performant way to check.

    Here is the logic I'm using to check (in FixedUpdate):
    Code (CSharp):
    1. // We check if obj is within the camera frustum
    2.         var bounds = collider.bounds;
    3.         planes = GeometryUtility.CalculateFrustumPlanes(camera);
    4.         bool inBounds = GeometryUtility.TestPlanesAABB(planes, bounds);
    5.  
    6.         // if in bounds and no objects are in between object and camera
    7.         if (inBounds && canSeePlayer)
    8.         {
    9.             capturedScale.x = capturedScale.y = capturedScale.z = s;
    10.             // Apply the transform
    11.             transform.localScale = Mathf.Max(0.01f, capturedScale.x) * transform.localScale.normalized;
    12.         } else if( inBounds && !canSeePlayer)
    13.         {
    14.             transform.localScale = initialScale;
    15.             capturedScale = initialScale;
    16.             return;
    17.         } else
    18.         {
    19.             transform.localScale = initialScale;
    20.             inFrame = false;
    21.             capturedScale = initialScale;
    22.             capturedDistance = 1;
    This does *exactly* as I want, except for the fact that if the center of the sphere is obstructed, the transform will apply (which the player can see at the edge of the sphere). I want to ensure that *NONE* of the object is visible rather than a single ray pointing to the center of the object. I was thinking of maybe checking the dot product to estimate based on the scale of the object? I'm still new, so I'm not quite sure where to go from here. Thanks!
     
    Last edited: Feb 1, 2023
  2. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,947
    That's usually how it's done.

    Then cast the center! Cast as much as you need for the game design you have in mind.

    Usually you put "important points" on a character rather than trying to compute bounds. The reason is that this gives you much better authoring control. You might put points on things such as head, center of chest, hips, perhaps legs, and depending on the visibility of each of those, you can decide how hittable they are for things like VATS or cover systems like what XCOM uses.

    You could also put "important heights" on a character, such as at head, at chest, at hips, and then cast several rays to points going across each of the height horizontal "strips"
     
    Bunny83 likes this.
  3. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    If it's really important to know the exact percentage how much is visible by a camera, you can use replacement shaders and render the object two times to two different render textures. Once with the other geometry as usual, and once just the object alone from the same view. As a result you get two overlapping images that you can simply subtract from each other. Of course you may render the object with white color and everything else black. That way you can simply count the white pixels in both images. It's kinda expensive, so you usually would do that only once every couple of frames (or whenever necessary).

    Note that this approach only accounts for parts of the object being hidden by other geometry. If you care about only part of an object being in view, it depends on the accuracy you need. You can use approximations using raycasts like Kurt said. If it's just about the camera view, you could use the frustum planes to check which parts / reference points / vertices are inside / outside the frustum. That's relatively cheap since you can simply translate the frustum planes into the local space of the object and check the local vertices against the plane(s). Of course skinned meshes would not work that great since you need to bake the current animation state in order to read the actual vertex positions.

    Note that your title ask to check if the entire object is visible while later you said
    which is pretty much the opposite condition. Those two conditions are not simply the negative of the other since between those two cases you have all the intermediate partial visible stages. So maybe you should clear up that first.

    • What's your actual condition you want to check for?
    • Are you interested in occluded geometry, camera frustum or both?
    • What kind of accuracy you need for the result? What's the deeper purpose of that check?
     
  4. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    To be honest - it's both. A boolean that would represent whether or not each condition is met. I'll explain below:

    I have a sphere that I'm applying forced perspective logic on when the sphere is in the camera frustum bounds, *and* is unobstructed. However, the ray is only at the center of the sphere, so if the sphere is 10 x 10 x 10, you can see the delta between the center and the radius, meaning it would be obstructed in the center for the ray, but still technically visible.

    Since transform logic is happening when those conditions change, it results in the sphere 'popping' into it's new size because it shouldn't be visible when I apply it.

    I attempted a terrible drawing to visualize it.
     

    Attached Files:

  5. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    How do people normally cast imprecise vertices on a sphere? Four points? Forgive my ignorance.
     
  6. Bunny83

    Bunny83

    Joined:
    Oct 18, 2010
    Posts:
    3,571
    Yes, we understood the partially visible thing. Though this is a bit vague:
    Do you try to do something like in superliminal? If that's the case, well yes, you can not really avoid this since it's a non realistic behaviour. So you have to decide how you want your mechanic to actually work. If it's not related to that, be more clear about your actual issue and how knowing if it's visible or not actually solved your issue.
     
  7. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    Kind of. The behavior I coded is exactly what I want except for me not knowing how to cast from each extreme position of the sphere to detect if *all* of it is visible / invisible in the camera frustum. For instance - I'd have to calculate rotation so the ray origin points match the frustum plane.

    Maybe I can also put the transform in a coroutine to interpolate it as well? I'm not sure common / best practices for this. Apologies for my ignorance.
     
  8. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    This is what I believe I'm looking for. I'm probably going to explain this terribly, but I'm going to try.

    Let's say a box obstructs a third of the sphere. I would need to get a 2d set of x,y Coords that would be the upper lower left right vertices of a circle (the sphere cut in half at the same orientation of the camera)

    I can evaluate whether or not all points hit the camera or not, and then run my transform.

    This should demonstrate what I'm looking for:
    The green is the angle of the sphere the camera is looking at, so we have to take the leftmost, uppermost, lowermost, and rightmost vertices to make it a circle relative to the camera frustum.
     

    Attached Files:

  9. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,947
    Can you define what an 'imprecise vertex' is? You can take any Vector3 and randomly perturb it by adding offsets to the pieces.

    There's nothing magical about coroutines. They run in EXACTLY the same CPU / processing space as Update() and everything else in Unity. It's all one thread, one busy little hamster running around.

    Here is some timing diagram help:

    https://docs.unity3d.com/Manual/ExecutionOrder.html
     
  10. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    I just need between 4-8 points along the circumference of the sphere that would translate to it being a circle relative to the camera's location. Even though the sphere is equal in all directions, it *does* have a 'forward', so depending on the orientation of the sphere relative to the camera, a 2d cross-section at a static location could look like an ellipse.

    So I basically need a billboard effect on the rays to make sure they *always* fire parallel to the camera's orientation.
     
  11. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,947
    You can sweep out a circle on the sphere itself by using the
    transform
    of the camera.

    Code (csharp):
    1. for ( float angle = 0; angle < 360; angle += 45)
    2. {
    3.   Quaternion rotation = Quaternion.AngleAxis( angle, cameraTransform.forward);
    4.   Vector3 camRelativeOffset = rotation * cameraTransform.right;
    5.  
    6.   // TODO: multiply camRelativeOffset by your radius
    7.  
    8.   // TODO: add camRelativeOffset to the center of the sphere
    9. }
    OR... if you mean keeping that ring of points aligned to the sphere's notion of "forward", use that Vector3 instead!
     
  12. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    But wouldn't this raycast 'foward' from the sphere? I just need a vector pointing from the sphere to the camera, no matter the sphere's orientation. The equivalent would be facing a direction and turning your head.

    Also, now that I'm thinking about it, the ray goes to the camera from the sphere, but it goes to 0,0,0 as well. So wouldn't I also need to do the reverse for each coordinate of the bounds of the camera in space?

    I appreciate everybody's help and apologize for my ignorance. This is the first time I've ever attempted something of this nature, so I'm woefully unequipped.
     
  13. Kurt-Dekker

    Kurt-Dekker

    Joined:
    Mar 16, 2013
    Posts:
    36,947
    Make sure you solidify the concept of a Ray in your mind.

    A Ray has an origin and goes in a direction.

    Two non-equal points in space may be subtracted from one another to get a direction vector, which may be normalized to make its length always 1, regardless of spacing of the points.

    NEITHER of those two points needs to be the Ray's origin, although often one of them is.

    When making points on or around the target object:

    - you are NOT making the Rays' origin

    - you are NOT making the Ray's direction

    - you ARE making ONE of the two points that could be subtracted to generate a direction vector.

    The other point in that subtraction would traditionally be the origin, but it doesn't have to be.
     
  14. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    I apologize for my denseness. The issue I'm having wrapping my head around is finding out if every point along the circumference of a sphere is unobstructed and visible anywhere in the frustum plane of the camera or with an object in between. I understand that vectors are directions and magnitudes, but it's from a single point to a single point. These objects are comprised of many points in space with many potential angles of intersect. In my mind, that would mean had certain points could potentially be visible along the plane off-center, but the center ray will incorrectly call it obstructed.

    Hopefully that makes sense and I'm not embarrassing myself by explaining it poorly.

    The picture does a better job explaining.it shows the green circle having vertices blocked by an object within the frustum plane.
     

    Attached Files:

  15. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    The object is a sphere. The camera's view is a frustum (i.e. a pyramid shape with the tip cut off). So, look up a sphere-frustum collision algorithm and implement it. This will give you an answer which is both exact and cheap.

    Here's one example of the algorithm. Basically, you check the distance of the sphere from each side of the frustum. If the distance outside of any of the planes is bigger than the radius then it is not visible.

    In Unity you can get the frustum planes of a camera from GeometryUtility.CalculateFrustumPlanes(Camera camera).
     
  16. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    That wouldn't account for other objects inside the bounds that may be obscuring the sphere though, right?
     
  17. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    No, you'd need to do that separately. I'd missed that in your later posts. However, I'd still do what I described as a first step, because there's no point doing any more detailed occlusion checks if you know the sphere is outside of the frustum anyway.

    The only general-case, accurate method I know to check full areas for partial occlusion is the rendering comparison which @Bunny83 described, or variations on that approach.

    That being said, depending on your game's design you may be able to get a "close enough" result with raycasts to a few points on the edge or with sphere casts. I've used both approaches successfully before, because I knew based on the rest of the game's design that the cases they would miss (e.g. small debris floating around, or having to check occlusion through small gaps) either didn't happen or wouldn't matter.
     
  18. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    Actually... are the occluders specific objects, or are they just arbitrary geometry in the scene?

    If they're specific objects then there's some pretty simple stuff you can do to cheaply and accurately discard definite misses.
    - If an occluder is behind an occludee, it can't be occluding it. So you can discard some with just a distance check.
    - If they're sufficiently far apart in screen space they can't be occluding. For this you need to know distance (which you already calculated for the above) and approximate size (possibly from the Renderer's bounds, or you could add a 'radius' field to a component).
    - If you have occluders in front of occludees and also nearby in screen space, then you can do a bunch of raycasts, or render to some low-res buffers, or whatever high-resolution detection you want to do. Because you know the pairs where occlusion is likely you can focus just on those without wasting effort elsewhere.

    If it's got to account accurately for arbitrary geometry, then I'd lean towards doing some low-res renders to separate buffers.
     
  19. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    I am currently checking frustum bounds, as well as checking visibly with one ray. I just don't know how to offset the rays around a circle from a vector.

    It would be any object in the scene (walls, terrain, windows with rails etc). Since the only shape I would estimate is a sphere, I think a few ray casts would suffice.

    This render logic and the sphere are a huge focal point that needs to behave as expected.

    This is all *way* beyond my wheelhouse, so I apologize for being clueless. haha
     
  20. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    You're creating a ray based on a line from (camera position) -> (sphere position).

    Instead, create a ray based n a line from (camera position) -> (sphere position + offset). Offset can be any Vector3 with magnitude less than or equal to the sphere's radius. It's probably a good idea to make it something like (camera.transform.left * radius * x + camera.transform.up * radius * z), with a few X and Z values between -1 and 1, as that will go "across" the camera's view without going in/out.
     
  21. jrhager84

    jrhager84

    Joined:
    Oct 8, 2018
    Posts:
    18
    So I add them up? Or do I send separate rays? I just want to make sure I get it right. Thank you for helping me wrap my head around this. It's unlike anything I've ever done and I'm struggling with it. haha

    This is what my original idea was (8 points at 45deg intervals to make sure the circumference of the sphere isn't visible (in a relatively efficient way).


     
  22. angrypenguin

    angrypenguin

    Joined:
    Dec 29, 2011
    Posts:
    15,519
    Don't worry about getting it right the first time. Just give something a go and see what happens.