5
$\begingroup$

I need to place objects randomly into a scene so that they are consistently within the camera's view.

I have a script which randomly places a perspective camera pointed at [0,0,0] at a variable distance. What I would like to do is to place a flat object onto the xy plane, centered at [x,y,0] , with x, y placed so that the object's center is guaranteed to be within view of the camera.

But it's not enough to place the object at the center of the view, there needs to be some variability. So I want to convert from camera coordinates (2d, ie [0,0] for camera top left and [1,1] for camera bottom right) to 3-d coordinates [x,y,0]; if I place an object randomly within the camera view, it will be placed randomly in world-space so that it is visible. Attached image shows what I mean: Camera coordinates to scene coordinates

I'm aware of bpy_extras.object_utils.world_to_camera_view(), I effectively need the opposite of that command parameterized by 2 out of 3 world coordinates...

$\endgroup$

1 Answer 1

6
$\begingroup$

From the camera view frame.

bpy: How do you find the corners of a camera frustum?

A camera points in the -Z direction. For a perspective camera can find the vectors of the origin to the frustum and if and where they intersect with the XY plane.

Test script. Have kept it simple. Uses the scene camera, finds the frustum, adds a cube on XY plane given image (0, 1) coordinate.

Please note: have used (0, 0) as bottom left and (1, 1) as top right.

import bpy
from math import inf
from mathutils.geometry import intersect_line_plane as ilp

# Global XY plane
plane_co = (0, 0, 0)
plane_no = (0, 0, 1)

context = bpy.context
scene = context.scene

cam = scene.camera
mw = cam.matrix_world
o = mw.translation

camdata = cam.data

tr, br, bl, tl = [
mw @ f for f in camdata.view_frame(scene=scene)]
# offset vectors from bl.
# eg middle would be bl + 0.5 * (x + y) 
x = tr - tl
y = tr - br
# roll around in CCW direction
for image_coord in ((0, 0), (0, 1), (1, 0), (1, 1)):
    cx, cy = image_coord
    # vector pointing from cam origin thru image point (PERSP)
    v = (bl + (cx * x + cy * y)) - o

    pt = ilp(o, o +  v, plane_co, plane_no, True)
       
    if pt and (pt - o).dot(v) > 0:
        print("Adding cube at ", pt)
        bpy.ops.mesh.primitive_cube_add(
                location=pt,
                )

    elif pt and (pt - o).dot(v) < 0:
        print("BEHIND THE CAMERA")
    else:
        print("NOT POSSIBLE") 

A plane to represent this.

Make a plane outlining the possible locations seen by camera. Tweak to make a plane mesh based on the points.

enter image description here Plane defining allowable locations in default scene

coords = []
for image_coord in ((0, 0), (1, 0), (1, 1), (0, 1)):
    cx, cy = image_coord
    # vector pointing from cam origin thru image point (PERSP)
    v = (bl + (cx * x + cy * y)) - o

    pt = ilp(o, o +  v, plane_co, plane_no, True)
    if pt:
        coords.append(pt)
print(coords)   
if coords:
    name = f"{cam.name}_ViewPlane"
    me = bpy.data.meshes.new(name)
    faces = [range(len(coords))] if len(coords) > 2 else []
    me.from_pydata(coords, [], faces)
    ob = bpy.data.objects.new(name, me)
    context.collection.objects.link(ob)

Tests

enter image description here 59 random scaled dots per click at random(), random()

enter image description here 150 Foos

Related.

How to find all objects in the Camera's view with Python?

Camera coverage calculation inside a room

projecting onto a mirror and back?

https://blender.stackexchange.com/a/160388/15543

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .