Notifications
Clear all

[Closed] Objects in camera cone?

Is there a way to calculate what objects are within a camera cone?
I don’t even know where to start to do this. Hopefully someone can point me down a path.

Cheers,

Cg.

9 Replies

As I have struggled with this problem myself, I found a couple of methods to do it:

1.choose a resolution and cast rays from the camera to “virtual pixels” in front of it.
the higher the resolution you choose the less objects you will miss but it will be very slow.

  1. check if the bounding box of the object is in the cone.
    you will have to check all 8 corners of the bounding box and switch between 3 cases:
    A) At list one corner is in the cone.
    B) All corners are out of the cone and on the same side.
    C) All corners are out of the cone but in both sides of the cone (the camera cone goes through the middle of an object).

Cases A & C will tell you that the object is probably in the cone.
Case B will tell you that it surely isn’t.
The problem is that you might include objects that their bounding box is in the cone but the actual geometry is out of it.

  1. You could divide the camera cone into boxes with increasing width and height and test for intersection between these boxes and the objects of the scene.
    option 2 is more precise and can be implemented much faster but this is pretty easy to implement.

  2. You could look through the camera and use a rectangle to select all objects in the view.
    I found this method to be the best because it won’t miss any object and it’s very fast.

visibleObjs = boxPickNode (box2 [0,0] (getViewSize()))

TzMtN’s solution above (box selection in viewport) is a very good solution,
thanks for posting this! Very neat trick.

Here’s my contribution… (all though TzMtN’s solution is much more easy and elegant).
maybe this can help you on your way, right now it uses pivot.
It needs to be changed to use the boundingbox or mesh faces \ vertex pos, to check
if it REALLY is inside the view.

you could make it like this,
check if boundingbox is inside the view, if it is then go deeper and check the vertexes.
if bounding box is not inside, then just skip that object.


  
  	fn isPointInFrustrum pointPos = (
  		thePos = pointPos * viewport.getTM()
  		screen_origin = mapScreenToView [0,0] (thePos.z) [renderWidth,renderHeight]
  		end_screen = mapScreenToView [renderWidth,renderHeight] thePos.z [renderWidth,renderHeight]
  		world_size = screen_origin-end_screen
  		x_aspect = renderWidth/(abs world_size.x)
  		y_aspect = renderHeight/(abs world_size.y)
  		screen_coords = point2 (x_aspect*(thePos.x-screen_origin.x)) (-(y_aspect*(thePos.y-screen_origin.y)))
  		(((screen_coords.x >= 0) AND (screen_coords.x < renderWidth)) AND ((screen_coords.y >= 0) AND (screen_coords.y < renderHeight)))
  	)
  
  TheObjects =   objects as Array
  FoundObjects = #()
  
  	for i = 1 to TheObjects.count do
  	(
  		if (isPointInFrustrum (TheObjects[i].pos))do
  		(
  			appendIfUnique FoundObjects TheObjects[i]
  		)
  	)
  
  	for o = 1 to FoundObjects.count do
  	(
  		clearSelection()
  		select FoundObjects
  	)
  
  

This could be a nice one for the next script-challenge, with the added difficulty is has to make a list of all object visible trough the camera in a given time span.

select all visible objects from start to end as seen trough $camera.

When you make a city flytrough you could use this to remove all objects that will never be seen, taking in account those that are in the viewport area but hidden behind other objects.

Just to fill you all in on why I want to do this: a script that would calculate the Zdepth limits for a Vray Zdepth Pass. Thanks so much for the speedy replies.

Cg.

I ended up using TzMtN’s viewport selection method. Thanks for that. Here is the code for the Zdepth limit calculator so far. This will work fine (and quickly) for objects who’s boundingbox is small compared to the distance from the center of the object to the cam. So far the only method I have in place is testing the distance from the 8 corners of each object’s bounding box to the camera. So great for small things but for larger objects like groundplanes I know I’m going to have to use a different method as there could be an object who’s polys are closer to camera than it’s bounding box corners. How to then test distances to the larger objects? The only way that I can think of is to attach all large objects and use one of the ray-mesh functions to shoot rays from the cam to test lots of possible hit points. This just seems like it could be very expensive.

  Can anyone suggest other methods?

For this code to work as is, you need to have a camera called Camera01 and at least one geometry or renderable spline object visible from that camera on at least one of the frames in the animationrange.


    (
    	fn isValidRenderObj camObj = if superclassof camObj == GeometryClass or ((superclassof camObj == shape) and (camObj.render_renderable)) then true else false
    	
    	fn isValidCamera theCam = if superclassof theCam != camera then false else true
    	
    	fn collectCamObjs = boxPickNode (box2 [0,0] (getViewSize())) crossing:true
    	
    	fn getBBoxCorners obj =
    	(
    		bb = nodeGetBoundingBox obj obj.transform
    		xyz = bb[2] - bb[1]
    		bBoxCorners = #()
    		for x = -.5 to .5 do for y = -.5 to .5 do for z = -.5 to .5 do append bBoxCorners (([ (xyz[1]*x) , (xyz[2]*y) , (xyz[3]*z) ]-(obj.pos-obj.center))  * obj.transform )
    		bBoxCorners
    	)
    
    	
    	fn calcLimits theCam =
    	(
    		if isValidCamera theCam do
    		(
    			viewTemp = viewport.getType()
    			viewport.setCamera theCam
    			sliderTemp = sliderTime
    			local theLimits
    			
    			for i = animationrange.start to animationrange.end do
    			(
    				slidertime  = i
    				(
    					frameObjs = collectCamObjs()
    					if frameObjs.count != 0 do
    					(
    						if theLimits == undefined do
    						( 
    							baseDist = distance theCam (frameObjs[1])  
    							theLimits = [baseDist,baseDist]
    						)
    						for frameObj in frameObjs where (isValidRenderObj frameObj) do
    						(		 
    							bBoxCorners = getBBoxCorners frameObj
    							
    							for bBoxCorner in bBoxCorners do
    							(
    								point pos:bBoxCorner name:("Point_" + frameObj.name + " " + bBoxCorner as string)
    								theDist = distance theCam.pos bBoxCorner 
    								if theDist < theLimits[1] then theLimits[1] = theDist
    								else if theDist > theLimits[2] then theLimits[2] = theDist
    							)
    						)
    					) 
    				)
    			)
    			viewport.setType viewTemp
    			sliderTime = sliderTemp
    		)
    		
    		if theLimits == undefined do messagebox "Oh No!!! Total failure!

Any renderable objects visible from
defined camera within animation range?

Was there even a valid camera defined?"
    		
    		theLimits
    	)
    	
    	theLimits = calcLimits $Camera01
    	format "%
"theLimits
    )
    
    

I guess another way to do this, would be to use the IntersectRayEx() I believe? It’s that or the normal Ray one. But one of those collects the object that was hit, and it doesn’t have to be a EPoly or EMesh I believe. But the hard part/down side of that, is making it fast with good accuracy…

Hey Kickflip, thanks for the reply. I am though, looking for workflows other than the “shooting rays at mesh” method. Which I would think could be quite expensive to calculate. But if I can’t think of, or someone else doesn’t suggest another method, that’s the one I’ll go with. I’m just looking to see what my other options might be before I spend the time implementing a ray-mesh method as it looks like it might take me a while to do.

Cheers,

Cg

Here’s a little start. It’s a g-buffer like approach. Each object should have a different color with the red channel at 100% (.red=255). This is done to filter out the other colors that exists in the viewport for gizmos and background etc.

Before you run it, set the viewport to ‘consistent colors’, turn off any shadows and disable progressive refinement. Each object should now be visible as a single solid color.

When you run the code it returns an array with unique colors found in the viewport that have a .red=255. You can make the viewport a bit smaller to speed it up. The next step would be match these colors to objects in the scene.

This method returns the objects that are actually visible to the camera. So if its behind another object, but in the camera cone, it can be flagged as invisible.

The viewport settings and color assignments can be automated but I do have a real job to do


 b=gw.getViewportDib();
 
 tmp=#()
 for y=0 to b.height do
 (
 	join tmp (makeUniqueArray  (getPixels b [0,y] b.width))
 )
 
 out=#()
 
 tmp=makeUniqueArray tmp
 
 for i=1 to tmp.count do
 (
 	if ( (tmp[i]).red == 255) do append out tmp[i]
 	
 )
 
 print out
 

interesting opengl tutorial on this here