# Coordinates of corners of camera view border

How to get coordinates of corners of camera border in camera view relative to the viewport area?

After checking

import bpy
print('-----')
print()
print()
for a in bpy.context.screen.areas:
if a.type == 'VIEW_3D':
for s in a.spaces:
print(s)
for d in dir(s):
print(d)
print('-----')


I have no idea how to find out where I can get this

• Rather than using that unwieldy python script, you can explore the structure using the "Datablocks" mode of the Outliner. I'm looking around to see if I can find a way to actually get the coordinates :) – linuxhackerman Jan 19 '14 at 16:21
• Yes, I know about Outliner, but Outliner is for datablocks, and what I wanna get is not store in datablocks at all. 100 % information =) – Sergey Krumas Jan 19 '14 at 16:24
• I'm not so sure it's possible to get it directly, you'd probably have to calculate it from the zoom and the camera's attributes... – linuxhackerman Jan 19 '14 at 16:31
• Same thoughts, but it's too hard. Need to do a commit later. – Sergey Krumas Jan 19 '14 at 16:37
• Before getting the coordinates I can check area.spaces[0].region3d.view_perspective == 'CAMERA'. It works cool =) – Sergey Krumas Jan 20 '14 at 11:02

There are 2 things to consider here:

• The camera frame. The camera frame is not as simple as you might expect since its effected by the field-of-view, aspect & x/y shift.
• The 2D view pixel coords, The user can pan & zoom the view, so this also has to be calculated.

This script gets the camera bounds and prints the pixel boundaries.

import bpy

def view3d_find():
# returns first 3d view, normally we get from context
for area in bpy.context.window.screen.areas:
if area.type == 'VIEW_3D':
v3d = area.spaces[0]
rv3d = v3d.region_3d
for region in area.regions:
if region.type == 'WINDOW':
return region, rv3d
return None, None

def view3d_camera_border(scene):
obj = scene.camera
cam = obj.data

frame = cam.view_frame(scene)

# move from object-space into world-space
frame = [obj.matrix_world * v for v in frame]

# move into pixelspace
from bpy_extras.view3d_utils import location_3d_to_region_2d
region, rv3d = view3d_find()
frame_px = [location_3d_to_region_2d(region, rv3d, v) for v in frame]
return frame_px

frame_px = view3d_camera_border(bpy.context.scene)
print("Camera frame:", frame_px)


See API docs for the important functions used here:

• Thanks, that's what I actually needed! Your comment "# move into object space" confused me. Maybe the comment should be like "moving into world space"? Because view_frame function gives the positions relatively to object space of the camera, and for getting pixelspace coordinates we must provide positions of the points in world space, don't we? – Sergey Krumas Jul 5 '17 at 8:33
• @sergey-krumas good point, done! – ideasman42 Jul 5 '17 at 22:19

Workaround example

Add a plane to the scene, to see the effect:

import bpy
from mathutils import Matrix
from bpy_extras import view3d_utils

mesh = bpy.data.objects['Plane'].data
camera = bpy.data.objects['Camera']
data = camera.data

frame = data.view_frame()
render = bpy.context.scene.render
ar = render.resolution_y / render.resolution_x

mesh.vertices[0].co = frame[0]
mesh.vertices[1].co = frame[1]
mesh.vertices[2].co = frame[3]
mesh.vertices[3].co = frame[2]

scale = Matrix.Scale(ar, 4, (0.0,1.0,0.0))
mat = camera.matrix_world

mesh.transform(mat*scale)
mesh.update()

for area in bpy.context.screen.areas:
if area.type=='VIEW_3D':
break

space = area.spaces[0]
region = area.regions[4]

points_on_screen = [
view3d_utils.location_3d_to_region_2d(
region,
space.region_3d,
v.co
)
for v in mesh.vertices
]

print(*points_on_screen, sep="\n")


You can apply the matrix transform directly to the vectors in camera.view_frame and use location_3d_to_region_2d to get the screen coordinates.

The plane is used for visualization.

BTW: To get the W-component you have to expand the vectors before multiplication

v = Vector((0.0, 0.0, 0.0))
v.to_4d()
# Vector((0.0, 0.0, 0.0, 1.0))


space_data.region_3d.perspective_matrix seems to be already multiplied with the view matrix. You can reverse it like this:

perspective_matrix * view_matrix.inverted()


So

ndc = [None] * 4
for i, v in enumerate(camera.view_frame()):
ndc[i] = perspective_matrix * matrix_world * scale * v.to_4d()
ndc[i] /= ndc[i][3]


should give you the NDC-coordinates

• Awesome pink vertex! This is exactly the calculation! I turned it into a modal draw op to visulize the result without a plane, fixed a problem with aspect ration > 1.0 and added a pink vertex in each corner =D It does not take Shift Y into account however, any idea how to solve this? pasteall.org/49117/python (Run Script, spacebar menu over 3D View, "modal", switch to camera view with Numpad 0! – CoDEmanX Jan 28 '14 at 22:00
• Shift Y screws up the scaling because (0.0,0.0) is not the center anymore. You can fix this by a translation. – pink vertex Jan 29 '14 at 9:13
• center=sum(camera.view_frame(),Vector((0.0,0.0,0.0)))/4 Create a translation matrix tmat=Matrix.Translation(Vector((0.0,-center[1],0.0)) And multiply it like this tmat.inverted()*scale*tmat You can simplify this by computing it by hand and set the entry in the scale matrix directly. – pink vertex Jan 29 '14 at 9:28
• Great! Integrated the manual way into my script and accounted for aspect ration > 1.0 again: pasteall.org/49139/python Note that you need to be in Camera perspective already if you want to use the get points function standalone (or it won't return the correct locations). – CoDEmanX Jan 29 '14 at 15:50
{"extraction_info": {"found_math": true, "script_math_tex": 0, "script_math_asciimath": 0, "math_annotations": 0, "math_alttext": 0, "mathml": 0, "mathjax_tag": 0, "mathjax_inline_tex": 0, "mathjax_display_tex": 0, "mathjax_asciimath": 1, "img_math": 0, "codecogs_latex": 0, "wp_latex": 0, "mimetex.cgi": 0, "/images/math/codecogs": 0, "mathtex.cgi": 0, "katex": 0, "math-container": 0, "wp-katex-eq": 0, "align": 0, "equation": 0, "x-ck12": 0, "texerror": 0, "math_score": 0.19639860093593597, "perplexity": 5134.132806741267}, "config": {"markdown_headings": true, "markdown_code": true, "boilerplate_config": {"ratio_threshold": 0.18, "absolute_threshold": 10, "end_threshold": 15, "enable": true}, "remove_buttons": true, "remove_image_figures": true, "remove_link_clusters": true, "table_config": {"min_rows": 2, "min_cols": 3, "format": "plain"}, "remove_chinese": true, "remove_edit_buttons": true, "extract_latex": true}, "warc_path": "s3://commoncrawl/crawl-data/CC-MAIN-2019-43/segments/1570986707990.49/warc/CC-MAIN-20191020105426-20191020132926-00546.warc.gz"}