HowTo:Extract 2D screen coordinates from 3D scenes
Introduction
At some point in your POV-Ray career you may need to retrieve high precision 2D screen coordinates of a point in a 3D POV-Ray render. Simply measuring the number of pixels in MS Paint is rarely a good solution because the precision is too low, even for very large renders. Instead, you need to use POV-Ray's own mechanisms to calculate and output finer measurements.
I faced this problem when designing two images for Wikipedia. For the first image, I needed to overlay 2D vector elements on top of a 3D POV-Ray render in order to show a series of measurements I made of important angles in the 3D bitmap. For the second image, I used the points outputted from a POV-Ray render to create a brand new 2D vector image from scratch. In both cases I used GeoGebra to create the 2D vector elements, but you could use InkScape as well.
(The first image is named "Graphical projection comparison.png". The second image is named "Various projections of cube above plane.svg". I can't link to them from here because the captcha widget on this website is broken, and I am prevented as a result from adding external links.)
Steps
The first thing you will need to do is download the updated version of "screen.inc" I created several years back, and place it somewhere in your library path. (Again, I cannot link to the file, but it is named "Updated screen.inc" and is located in the "povray.text.scene-files" newsgroup.) Once you have copied the script file to the correct directory, you will need to include it in your scene.
#include "screen.inc"
The next step is to reconfigure your camera code to make it compatible with "screen.inc". Here is some camera code I created for a test scene:
#local cam_dist = 8; #local cam_move = 1/2; #local cam_area = 2; #local cam_loca = -z * cam_dist; #local cam_look = 0; #local cam_angl = 45; #local cam_tran = transform { rotate +x * asind(tand(30)) rotate +y * 045 translate +y * cam_move } Set_Camera_Orthographic(false) Set_Camera_Transform(cam_tran) Set_Camera(cam_loca, cam_look, cam_angl)
The updated script can also handle orthographic renders, and input vulues that take the form of location, direction, up and right instead of location, look_at, angle and sky. For example:
#local cam_aspc = image_width/image_height; #local cam_dist = 8; #local cam_move = 1/2; #local cam_area = 2; #local cam_loca = -z * cam_dist; #local cam_dirc = +z; #local cam_rgvc = +x * cam_area * cam_aspc; #local cam_upvc = +y * cam_area; #local cam_tran = transform { rotate +x * asind(tand(030)) rotate +y * 045 translate +y * cam_move } Set_Camera_Orthographic(true) Set_Camera_Transform(cam_tran) Set_Camera_Alt(cam_loca, cam_dirc, cam_rgvc, cam_upvc)
From that point onward, all you need to do is choose which 3D points you want to capture and output as 2D screen coordinates. For instance, maybe there is a light source in your scene and you want to record where it is in the final render:
#local light_location_3d = <+30, +30, -30>; light_source { light_location_3d color rgb <1, 1, 1> parallel point_at 0 }
Now that you have the coordinates, just plug them into the Get_Screen_XY macro, like this:
#local light_location_2d = Get_Screen_XY(light_location_3d);
and print the result in the Messages window like this:
#debug concat("light_location_2d = (", vstr(2, light_location_2d, ",", 0, -1), ")\n")
As you render the scene, additional text will appear in the Messages window, looking similar to this:
light_location_2d = (205.036679,371.407180)
You can now use these coordinates to place 2D objects in a vector image program such as GeoGebra or InkScape.
Sample Scene
// Persistence of Vision Ray Tracer Scene Description File // This file is licensed under the terms of the CC-LGPL. // File: czech_hedge_hog.pov // Vers: 3.7 // Desc: Trigonometry and camera test // Date: 08/30/2018 // Auth: Michael Horvath // #version 3.7; #include "colors.inc" #include "screen.inc" // requires the updated version available here: http://news.povray.org/povray.text.scene-files/message/%3C5b889a96%40news.povray.org%3E/#%3C5b889a96%40news.povray.org%3E global_settings { assumed_gamma 1.0 } // ---------------------------------------- #local cam_view = 0; #switch (cam_view) #case (0) // perspective #local cam_dist = 8; #local cam_move = 1/2; #local cam_area = 2; #local cam_loca = -z * cam_dist; #local cam_look = 0; #local cam_angl = 45; #local cam_tran = transform { rotate +x * asind(tand(30)) rotate +y * 045 translate +y * cam_move } Set_Camera_Orthographic(false) Set_Camera_Transform(cam_tran) Set_Camera(cam_loca, cam_look, cam_angl) #break #case (1) // isometric (axonometric) #local cam_aspc = image_width/image_height; #local cam_dist = 8; #local cam_move = 1/2; #local cam_area = 2; #local cam_loca = -z * cam_dist; #local cam_dirc = +z; #local cam_rgvc = +x * cam_area * cam_aspc; #local cam_upvc = +y * cam_area; #local cam_tran = transform { rotate +x * asind(tand(030)) rotate +y * 045 translate +y * cam_move } Set_Camera_Orthographic(true) Set_Camera_Transform(cam_tran) Set_Camera_Alt(cam_loca, cam_dirc, cam_rgvc, cam_upvc) #break #end sky_sphere { pigment { gradient y color_map { [0.0 rgb <0.6,0.7,1.0>] [0.7 rgb <0.0,0.1,0.8>] } } } light_source { <0, 0, 0> color rgb <1, 1, 1> translate <+30, +30, -30> parallel point_at 0 } // ---------------------------------------- plane { +y, 0 pigment { checker pigment {Gray} pigment {White} } } #local TempAngle = pi/2 - atan(1/cos(pi/4)); #local TempTrans = transform { rotate +x * 45 rotate +z * degrees(TempAngle) translate +y * sin(TempAngle) translate +y * 1/10 } union { cylinder {-x,+x,0.1 pigment {Red}} cylinder {-y,+y,0.1 pigment {Green}} cylinder {-z,+z,0.1 pigment {Blue}} transform {TempTrans} } #debug "\n" #local SphereLoc_1 = vtransform(-x, TempTrans); #local ScreenLoc_1 = Get_Screen_XY(SphereLoc_1); sphere {SphereLoc_1, 0.1 pigment {Cyan}} #debug concat("ScreenLoc_1 = (", vstr(2, ScreenLoc_1, ",", 0, -1), ")\n") #local SphereLoc_2 = vtransform(+x, TempTrans); #local ScreenLoc_2 = Get_Screen_XY(SphereLoc_2); sphere {SphereLoc_2, 0.1 pigment {Cyan}} #debug concat("ScreenLoc_2 = (", vstr(2, ScreenLoc_2, ",", 0, -1), ")\n") #local SphereLoc_3 = vtransform(-y, TempTrans); #local ScreenLoc_3 = Get_Screen_XY(SphereLoc_3); sphere {SphereLoc_3, 0.1 pigment {Magenta}} #debug concat("ScreenLoc_3 = (", vstr(2, ScreenLoc_3, ",", 0, -1), ")\n") #local SphereLoc_4 = vtransform(+y, TempTrans); #local ScreenLoc_4 = Get_Screen_XY(SphereLoc_4); sphere {SphereLoc_4, 0.1 pigment {Magenta}} #debug concat("ScreenLoc_4 = (", vstr(2, ScreenLoc_4, ",", 0, -1), ")\n") #local SphereLoc_5 = vtransform(-z, TempTrans); #local ScreenLoc_5 = Get_Screen_XY(SphereLoc_5); sphere {SphereLoc_5, 0.1 pigment {Yellow}} #debug concat("ScreenLoc_5 = (", vstr(2, ScreenLoc_5, ",", 0, -1), ")\n") #local SphereLoc_6 = vtransform(+z, TempTrans); #local ScreenLoc_6 = Get_Screen_XY(SphereLoc_6); sphere {SphereLoc_6, 0.1 pigment {Yellow}} #debug concat("ScreenLoc_6 = (", vstr(2, ScreenLoc_6, ",", 0, -1), ")\n") #debug "\n"