I am writing a toy operator which select the visible vertices of a mesh from active camera.

My approach is to do a perspective projection on vertices of that mesh to NDC(Normalized Device Coordinates. It is a glossary from rasterization graphic API like OpenGL) and check that whether the NDC is in range of [-1, 1]^3.

I encounter a problem when I construct the projection matrix using camera.view_frame() function: It seems that view_frame() returns double of the real size of camera's sensor.

I am confused with this behavior of view_frame().

Supplementary background: I write a Blog post for details about this toy operator on http://linearconstraints.net/?p=53

And the full source code is on https://gist.github.com/thebusytypist/8900746

Note that I divide the left/right/bottom/top by 2 in line 75-76.

  • $\begingroup$ Comparing Camera.clip_start with the z-values of Camera.view_frame() it seems it is being doubled. There is Camera.clip_end, Camera.fov and you might get the aspect ratio from Scene.render.resolution_x/y. An explanation how exactly blender does the calculation would indeed be helpful. $\endgroup$ Commented Feb 9, 2014 at 18:22

1 Answer 1


There's a utility function world_to_camera_view(), which makes it really easy to select the view frustum:

import bpy
import bmesh
from bpy_extras.object_utils import world_to_camera_view

def main(self, context):
    sce = context.scene
    cam = sce.camera
    ob = context.object
    me = ob.data
    mat = ob.matrix_world

    if not me.is_editmode:

    bm = bmesh.from_edit_mesh(me)

    if not self.extend:

    for v in bm.verts:
        x, y, z = world_to_camera_view(sce, cam, mat * v.co)
        if (0.0 <= x <= 1.0 and 
            0.0 <= y <= 1.0 and
            (not self.clip or cam.data.clip_start < z < cam.data.clip_end)):
            v.select = True


class MESH_OT_select_view_frustum(bpy.types.Operator):
    """Select geometry in view frustum (uses active object and scene camera)"""
    bl_idname = "mesh.select_view_frustum"
    bl_label = "Select View Frustum"
    bl_options = {'REGISTER', 'UNDO'}

    clip = bpy.props.BoolProperty(
        description="Take camera near and far clipping distances into account",

    extend = bpy.props.BoolProperty(
        description="Extend the current selection",

    def poll(cls, context):
        return (context.object is not None and
                context.object.type == 'MESH' and
                context.scene.camera is not None)

    def execute(self, context):
        main(self, context)
        return {'FINISHED'}

def register():

def unregister():

if __name__ == "__main__":

You must log in to answer this question.

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