# Get pixel coords for vertex in viewport

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=list(p)
xy = xy * 680
xy = xy * 809
co2d.append(xy)


(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
return areas

camera = area_view_3d().spaces.camera



but when I take the resulting coordinates and map them onto the screenshot it looks like this: Clearly I get something that looks like coordinates, but isn't what I want.

What is it that I am getting wrong?

• 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 Sep 6, 2021 at 18:48
• @batFINGER - thanks, I was thinking I am getting the wrong camera... banging around this one for hints: stackoverflow.com/questions/9028398/… Sep 6, 2021 at 18:51
• @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 Sep 6, 2021 at 18:59
• render, finally twigged on re-reading question .. 8) Sep 6, 2021 at 19:00
• made it clearer in question Sep 6, 2021 at 19:01

3D location to region 2D 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]
break
else:
assert False, "Requires a \$D view"

plane_co = ob.matrix_world.translation

bm = bmesh.new()
bm.from_mesh(me)
bm.transform(ob.matrix_world)
u, v = np.array(
[view3d_utils.location_3d_to_region_2d(
region,
r3d,
v.co) for v in bm.verts]
).T

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 functools import partial

vertices = bbox2d

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

def draw():

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)
a.tag_redraw()

• 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 Sep 7, 2021 at 7:54
• Found out! up and down are inverted between Blender and OpenCV. Fixed the gist. Works 100% Sep 7, 2021 at 12:23