6

... by which I mean the vectors from a perspective camera's viewing point to the corners of the rendered rectangle, as displayed by the diagonal sides of a camera's rectangular pyramid in a 3D View. (Normalized if you like, in Camera Space, if you like.)

The angle and view_frame attributes of a Camera don't seem to carry this information, unless I'm mistaken?

There are quite a few answers on how to test whether a point is visible to a camera, that's not quite the same thing.

Better still, and perhaps of more general use to others, too, would be a diagram of how bpy refers to a camera: what a Camera's important attributes actually mean, in 3D / device / screen space.

EDIT: following @batFINGER's examples (I think?)..trying to get the render frame, I run this:

import bpy

scn = bpy.context.scene

rx = scn.render.resolution_x ry = scn.render.resolution_y cam_obj = scn.camera

if (cam_obj.name in bpy.data.cameras):

cam_mw = cam_obj.matrix_world
cam = cam_obj.data
vf = cam.view_frame()
world_frame = [cam_mw @ v for v in vf]

print (rx,ry)
for v in world_frame:
    print (v)  

And, changing the render resolutions, visibly changing the shape of the frustum in the 3D view, I get these outputs:

1512 1371
<Vector (2.1208, -6.3833, 4.1026)>
<Vector (2.3175, -6.8641, 3.2482)>
<Vector (1.3703, -7.1821, 3.2090)>
<Vector (1.1736, -6.7013, 4.0635)>

427 1432 <Vector (2.1208, -6.3833, 4.1026)> <Vector (2.3175, -6.8641, 3.2482)> <Vector (1.3703, -7.1821, 3.2090)> <Vector (1.1736, -6.7013, 4.0635)>

Which is puzzling me. There's no change in the frame as a consequence of changing the proportions of the render - (the change isn't even hidden in the camera's world transform..)

SOLUTION: the camera's .view_frame() method needs its optional named scene parameter to update.. until I know why, I don't fancy making that an answer..

Robin Betts
  • 76,260
  • 8
  • 77
  • 190
  • So you mean that four lines initial from camera origin which display as the camera Gizmos? – HikariTW Jul 21 '20 at 09:08
  • @HikariTW Those are the ones .. the length doesn't matter, just the vectors.. or any other way of getting the bounds of the field of view : the rendered rectangle – Robin Betts Jul 21 '20 at 10:03
  • Related https://blender.stackexchange.com/questions/6377/coordinates-of-corners-of-camera-view-border also https://blender.stackexchange.com/questions/45146/how-to-find-all-objects-in-the-cameras-view-with-python/45324#45324 (used in that recent cow shed q your answer to which is great btw) and https://blender.stackexchange.com/a/160388/15543 – batFINGER Jul 21 '20 at 10:32
  • @batFINGER Thank you! I got those answers of yours, even UV'd and soaked them up enough to write the script around the stripped-down example above, (couldn't find them again to mention here) but I'm still stumped.. see edit.. I must be misunderstanding something obvious.. – Robin Betts Jul 21 '20 at 13:00
  • @batFINGER OK, got it. The named scene parameter to view_frame() for some reason kicks an update? Unless you know better why it should make a difference? Hesitate to answer my own q until I know what's going on. Maybe you do. – Robin Betts Jul 21 '20 at 13:25
  • Posted answer... looked like you got there in the end. Perhaps it uses the defaults for scene resolution et al, if scene not passed. – batFINGER Jul 21 '20 at 15:02
  • also sure there is a Q / A that does this and adds small icospheres to the corners. For life of me couldn't find it. – batFINGER Jul 21 '20 at 15:11

1 Answers1

7

Pass the scene to view frame

Think you got there in the end, here is @ideasman42's script modified to add empties to 5 points of frustum, for each camera object in scene.

All the info re resolution will come with the passed as keyword scene, else it defaults to None.

>>> C.scene.camera.data.view_frame(
view_frame()
Camera.view_frame(scene=None)
Return 4 points for the cameras frame (before object transformation)

Test script:

import bpy

def camera_as_planes(scene, obj): """ Return planes in world-space which represent the camera view bounds. """ from mathutils.geometry import normal print(obj.name) camera = obj.data # normalize to ignore camera scale matrix = obj.matrix_world.normalized() frame = [matrix @ v for v in camera.view_frame(scene=scene)] origin = matrix.to_translation() frame.append(origin) for p in frame: bpy.ops.object.empty_add(location=p)

test call

scene = bpy.context.scene cams = [o for o in scene.objects if o.type == 'CAMERA'] for cam in cams: camera_as_planes(scene, cam)

batFINGER
  • 84,216
  • 10
  • 108
  • 233
  • Thanks!... seems odd to have to pass an unchanged scene, to me ... and what would none mean to this method, if not a live ref. to the camera's context.scene? I have to learn more about what context is.. when it's about, when it isn't. Cheers. – Robin Betts Jul 21 '20 at 15:18
  • 1
    I'm assuming None uses the default values for resolution etc. Doesn't need to be context scene, simply the scene containing the camera. The camera data has no idea what scene it is in (could be in none or all) This data is required to make the calc to match the scene. Used context in example above to add empties. To simply print out could for scene in bpy.data.scenes: – batFINGER Jul 21 '20 at 15:24
  • Okydoky, beginning to get it... :) – Robin Betts Jul 21 '20 at 15:37