Is it possible to get the screen XY coordinates in viewport for a vertex in orthographic view?

I get pixel locations as per a previous answer like this:

co2d = []
for v in cloth.data.vertices:
    p = bpy_extras.object_utils.world_to_camera_view(scene, camera, v.co)
    xy[0] = xy[0] * 680
    xy[1] = xy[1] * 809

(image width and height hardcoded here)

Where the camera is defined here:

def area_view_3d():
    areas = [a for a in bpy.context.screen.areas if a.type == "VIEW_3D"]
    if not areas:
    return areas[0]

camera = area_view_3d().spaces[0].camera

but when I take the resulting coordinates and map them onto the screenshot it looks like this:

enter image description here

Clearly I get something that looks like coordinates, but isn't what I want.

What is it that I am getting wrong?

  • $\begingroup$ Will hunt down some dupes. Issue most likely is vert coords are local, the world to camera view wants globals... use ob.matrix_world @ verr.co $\endgroup$
    – batFINGER
    Commented Sep 6, 2021 at 18:48
  • $\begingroup$ @batFINGER - thanks, I was thinking I am getting the wrong camera... banging around this one for hints: stackoverflow.com/questions/9028398/… $\endgroup$
    – simone
    Commented Sep 6, 2021 at 18:51
  • $\begingroup$ @batFINGER - I'll read through. Just a question - is the linked answer about the render camera or the viewport one? I'm struggling between the two. Apropos - this one seems good too, but also render camera, and not orthogonal $\endgroup$
    – simone
    Commented Sep 6, 2021 at 18:59
  • 1
    $\begingroup$ render, finally twigged on re-reading question .. 8) $\endgroup$
    – batFINGER
    Commented Sep 6, 2021 at 19:00
  • $\begingroup$ made it clearer in question $\endgroup$
    – simone
    Commented Sep 6, 2021 at 19:01

1 Answer 1


3D location to region 2D

enter image description here

Test script.

import bpy
from mathutils import Vector, Matrix
from bpy_extras import view3d_utils
import bmesh
import numpy as np

context = bpy.context
scene = context.scene
ob = context.object
mw = ob.matrix_world
me = ob.data
for a in context.screen.areas:
    if a.type == 'VIEW_3D':
        space = a.spaces.active
        r3d = space.region_3d
        plane_no = r3d.view_rotation @ Vector((0, 0, -1))
        region = a.regions[-1]
   assert False, "Requires a $D view"
plane_co = ob.matrix_world.translation

bm = bmesh.new()
u, v = np.array(
        v.co) for v in bm.verts]

bbox2d = (
        (u.min(), v.min()),
        (u.max(), v.min()), 
        (u.max(), v.max()),       
        (u.min(), v.max()),
#bbox2d = [(0, 0), (100, 0), (100, 100), (0, 100)]
bl, tr = (0, 0), (region.width, region.height)    

import gpu
from gpu_extras.batch import batch_for_shader
from functools import partial

vertices = bbox2d

indices = (
    (0, 1, 2), (2, 3, 0))

shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)

def draw():
    shader.uniform_float("color", (0, 1, 1, .10))

handle = bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

def kill(handle):
    if handle:
        bpy.types.SpaceView3D.draw_handler_remove(handle, 'WINDOW')
    return 0

bpy.app.timers.register(partial(kill, handle), first_interval=2)
  • $\begingroup$ thanks! that works 99%: when I use the coordinates to draw on a screenshot things are offset vertically (i.e.: the dots are positioned lower than the vertices) see here: pasteboard.co/Kjt5hO5.png - is there a way to determine the offset? full gist of the script (with screenshot and dot making) here: gist.github.com/simonecesano/2cefcfac7485939a18249c4672e4a5ff. And in general thanks for the thorough answers $\endgroup$
    – simone
    Commented Sep 7, 2021 at 7:54
  • $\begingroup$ Found out! up and down are inverted between Blender and OpenCV. Fixed the gist. Works 100% $\endgroup$
    – simone
    Commented Sep 7, 2021 at 12:23

You must log in to answer this question.

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