Using raycasting to occlude obscured objects

Hi there,

I’ve been developing a function to return a list of all GameObjects visible by a particular camera, and at this point I’ve been able to narrow down the list to all GameObjects within the view frustum using Geometry.TestPlanesAABB(). However, this returns all GameObjects in the frustum, when I need only the objects that aren’t hidden behind other objects.

As an initial step in that direction, I’ve been trying to cast a ray from the camera to each object in the frustum and see whether it hits a different object instead. If it does, it is hidden (well, its center at least). I’ve got the following code:

foreach (object go in objects)
{
	GameObject g = (GameObject) go;
	RaycastHit hit = new RaycastHit();

 	// perform raycast. Note: cam is a Camera object attached to the script
	Physics.Raycast(cam.transform.position, g.transform.position, out hit);
			
	if (hit.transform.gameObject.Equals(g))
		visibleObjects.Add(g);
}

However, this doesn’t seem to be casting the proper ray. For example, in a scene with several crates, pillars, and walls (square room with just four walls around the outside), the only objects where the ray hits the proper target are the walls.

Am I using the right position vectors in my Raycast call? cam.transform.position should be the position of the camera, and g.transform.position should be the position of the object whose visibility I’m testing, but for some reason the line between the two is hitting objects (like walls) that are not there.

Thanks in advance for any insight - this problem is a hard one to troubleshoot.

Ahonek

Hello there!

Ah yes, the magic of raycasting.
I’m fairly certain the problem lies here:

Physics.Raycast(cam.transform.position, g.transform.position, out hit);

More specificly your second parameter.
The second parameter is supposed to be the direction, hence change it to:

Vector3 direction = (g.transform.position - cam.transform.position).normalized;
Physics.Raycast(cam.transform.position, direction, out hit);

That will result in a vector pointing at your target and should give you a better result.

Ah, a direction vector between the start and end, not an end position. Of course, I’m not sure why I didn’t catch that.

Thanks very much for your help. I’m still experiencing some weird behavior but it’s much closer to what I’m looking for now.

Also, I am almost certain that Physics.Raycast only populates hit info if you use it as a conditional, eg:

if(Physics.Raycast(cam.transform.position, direction, out hit)){
	if(hit.transform.gameObject.Equals(g))
		visibleObjects.Add(g);
}

At least, thats the only way I’ve ever used it and seen it used.

It returns a boolean, but you don’t have to check its return value.

Raycast is great, but see Linecast too. It’s often easier to use.

I have to say I like your idea, a sort of jerryrigged occlusion culling method for Unity indie :slight_smile:

I’m wondering how you could get around the “object center out-of-view but part of object mesh renderer in view” problem.

You could maybe use Renderer.bounds to calculate 4 points on the extreme x and y edges of the renderer bounds, and raycast all of those in addition to the center, and see any of the rays intersect the collider (assuming the collider covers the whole mesh renderer).

That’s more or less what I’m doing, legend. It’s a pretty simple but effective solution as long as your objects are convex. If you start using donut objects and windows and such, where you can see through a section in the middle, then things become a lot more complicated.

It’s also worth noting that this method, using raycasting, is not the most efficient thing in the world. I wouldn’t use it for standard occlusion culling, I’m just using it to sync my AI module’s visual model of the world with the game instance, so I don’t have to do it more than a couple times per second or so.

Pakfront, it’s true that in my case Linecast probably would have been better. Does Linecast just do the (endVector - startVector) math internally, and is it otherwise the same as Raycast?

I believe so.

I wonder if using Collider.bounds might be better than Renderer.bounds, since the Collider is what you are actually hitting (or not hitting)

That’s interesting, but the Renderer is what you see. So it would be more accurate to use the Renderer, as long as the collider is not smaller than the Renderer.

If we used the Collider’s bounds, we would get false positives for objects whose Colliders were larger than their Renderers, whereas if we used the Renderer’s bounds we’d get false negatives for objects whose Colliders were smaller than their Renderers.

I guess it’ll just come down to what fits best for this project.