0
$\begingroup$

Ok, I am completely new at scripting in blender but I wanted to know if there is a way to move an object within a scene randomly (x,y,z) but constrain its movement to only the camera view.

In other words, I want to position one object randomly in a scene without leaving the camera view.

I was thinking maybe getting the camera viewport coordinates but how do I constraint the object to those points withing the bpy.obj.transform?

Thanks for the help!

$\endgroup$
1
  • $\begingroup$ D.objects['Camera'].id_data.data.angle_x and D.objects['Camera'].id_data.data.angle_y give the camera view angles (relative to camera rotation) if the object is inside those angles, it is in the camera view... Probably need a transform matrix set to go to and from camera coordinates. See Angle of View $\endgroup$
    – Ron Jensen
    Jul 6 '20 at 3:00
3
$\begingroup$

camera.data.angle_x/y doesn't seem to follow changes in resolution, so I made the following script taking into account resolution, pixel aspect and focal length.< br>

The following functions camera_normalized_frame1() and camera_normalized_frame2 both return camera frame normalized by the distance btween camera image plane and origin.

import bpy
from mathutils import Matrix

#-------------------------------------------------------------
def camera_normalized_frame1(cam):
    render = bpy.context.scene.render
    aspect = (render.resolution_x * render.pixel_aspect_x) \
                / (render.resolution_y * render.pixel_aspect_y)
    view_frame = cam.data.view_frame()
    frame = [-f/view_frame[0].z for f in view_frame]
    if aspect > 1:
        mat = Matrix.Diagonal((1, 1/aspect, 1),)
    else:
        mat = Matrix.Diagonal((aspect, 1, 1), )
    for i in range(len(frame)):
        frame[i] = mat @ frame[i]
    return frame


#-------------------------------------------------------------
def camera_normalized_frame2(cam):

    import bpy_extras
    scene = bpy.context.scene

    frame = cam.data.view_frame()
    poss = bpy_extras.object_utils.world_to_camera_view(scene, cam, cam.matrix_world @ frame[0])

    ratio = [0.5/(poss[i]-0.5) for i in range(2)]
    norm_frame = []
    for f in frame:
        norm_frame.append(f.copy())
        for i in range(2):
            norm_frame[-1][i] *= ratio[i]
        norm_frame[-1] /= -norm_frame[-1][2]

    return norm_frame

enter image description here


Create Icosphere objects in the corners of a rendering imag.
import bpy
from mathutils import Matrix

cam = bpy.data.objects['Camera']
frame = camera_normalized_frame1(cam)
#frame = camera_normalized_frame2(cam)

distance = -cam.data.view_frame()[0].z
for v in frame:
    loc_l = distance * v
    loc_w = cam.matrix_world @ loc_l
    bpy.ops.mesh.primitive_ico_sphere_add(radius=0.1, location=loc_w)

Place objects at random locations within camera view.
import bpy
from mathutils import Vector, Matrix
import random, os
import math

random.seed(os.times().user)
min_z, max_z = 2, 10

# Base object for duplication
bpy.ops.mesh.primitive_ico_sphere_add(radius=0.05)
obj = bpy.context.object


cam = bpy.data.objects['Camera']
frame = camera_normalized_frame1(cam)
#frame = camera_normalized_frame2(cam)

for i in range(200):
    v = Vector([
                random.uniform(-1, 1) * frame[0].x, 
                random.uniform(-1, 1) * frame[0].y, 
                frame[0].z
                ])
    distance = max_z * math.sqrt(random.uniform( math.pow(min_z/max_z, 1), 1 ) )
    loc_w = cam.matrix_world @ (distance * v)

    obj.select_set(True)
    bpy.ops.object.duplicate_move_linked(TRANSFORM_OT_translate={"value":loc_w})
    bpy.ops.object.select_all(action='DESELECT')

obj.hide_set(True)
bpy.context.view_layer.objects.active = cam
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.