VRay - Convert 3D coordinates to 2D in parallel projection

I am trying to perform a 3D to 2D conversion of some coordinates from a Top View in parallel projection to its rendered image using VRay fro SketchUp 3.6. All images have been scaled.

First step is to convert each query coordinate from World to Camera domain, so I obtain the camera matrix (it’s a right-handed system):

R|T = Matrix[ [1.0, 0.0, 0.0, -120], [0.0, 1.0, 0.0, -60], [0.0, 0.0, -1.0, 709], [0.0, 0.0, 0.0, 1.0] ]

Applying the R|T matrix, the coordinates of the centroids of the four small squares in the model are:

top_left = [-62.63, 66.95, 642.93]
top_right = [53.99, 68.57, 642.93]
bottom_left = [-58.31, -117.70, 642.93]
bottom_right = [60.47, -90.71, 642.93]

I use the following parameters for the rendering:


Obtaining the following image:
Being the [0,0] pixel the top left corner of the image, the coordinates of the squares in the rendered image are:

top_left = [175, 174]
top_right = [315, 172]
bottom_left = [180, 396]
bottom_right = [323, 364]

I would like to know how to map between both set of coordinates. I know the height of the camera (+Z axis) must be used somehow, and so the size of the VRay viewport and the size and AR of the final image. If I set “Safe Frame” in VRay, it shows the area of the rendering viewport:

In perspective projection I am able to map between coordinates by knowing the camera intrinsics and applying the corresponding equations, but I can’t do the same in orthographic projection. Any hint?

Well I don’t use V-ray, and don’t know what and why your matrix does.

But if I were to convert from coordinates based on the origin in your first screenshot,
… to coordinates based upon the top left corner of the model …
assuming that the image is the same size as the model …

# pts is an array of Geom::Point3d or 3 element arrays.

def model_to_image(pts)
  # Use left back bottom of model bounds as image origin
  y_offset = Sketchup::active_model.bounds.corner(2).y
  pts.map {|pt| pt.y= -(pt.y - y_offset); next pt }

def image_to_model(pts)
  # Use left back bottom of model bounds as image origin
  y_offset = Sketchup::active_model.bounds.corner(2).y 
  pts.map {|pt| pt.y= -(pt.y) + y_offset; next pt }

That solution works if the whole bounding box is inside the viewport and it has the same dimensions than the image, but it does not work if there is any object out of the viewport.

I think the solution is not so trivial, and I need to know the size of the rendering viewport in VRay for a parallel view of AR = 1:1, so maybe it’s more a question for VRay users/team, rather than SketchUp’s.

Another solution I am going to try is simulating a parallel projection in a perspective view, by defining a huge focal length (near infinite). We can set the camera focal length up to 3000, so maybe it’ll work if then I apply the perspective projection math. In this case I can obtain the size of the image plane by knowing the focal length and the depth (+Z) to the object, and then I can convert to pixel coordinates.

@DanRathbun Btw, do you know if there is a way to change the SketchUp viewport size programatically? If so, then my question will be solved, because there is an option in VRay to match the viewport when rendering.

In the API only the query methods are included, but not the setters…

I found a solution that is working pretty well, and doesn’t need to use the camera matrix:

view = Sketchup.active_model.active_view
cam = view.camera

# Set to parallel projection
cam.perspective = false

# Here we set the aspect ratio we want to use for rendering, changing the SU view size and matching it with the VRay view size
cam.aspect_ratio = 1.0
c0 = view.corner(0)
c1 = view.corner(1)
c2 = view.corner(2)
# c3 = view.corner(3)

# Calculate view coordinates of the 3D point
sc = view.screen_coords(point)

# Normalize between 0 and 1
pxn = (sc[0] - c0[0]) / (c1[0] - c0[0])
pyn = (sc[1] - c0[1]) / (c2[1] - c0[1])

# Remap to image resolution
image_width = image_height = 500
pixel_coords = [(pxn * image_width).round, (pyn * image_height).round]

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.