Difference between revisions of "HowTo:Extract 2D screen coordinates from 3D scenes"

From POV-Wiki
Jump to navigation Jump to search
(→‎Result: wording)
(→‎Sample Scene: update example)
Line 98: Line 98:
 
// Persistence of Vision Ray Tracer Scene Description File
 
// Persistence of Vision Ray Tracer Scene Description File
 
// This file is licensed under the terms of the CC-LGPL.
 
// This file is licensed under the terms of the CC-LGPL.
// File: czech_hedge_hog.pov
+
// File: screen_cube.pov
 
// Vers: 3.7
 
// Vers: 3.7
// Desc: Trigonometry and camera test
+
// Desc: Screen and camera test
 
// Date: 08/30/2018
 
// Date: 08/30/2018
 +
// Updt: 09/01/2018
 
// Auth: Michael Horvath
 
// Auth: Michael Horvath
 
//
 
//
Line 107: Line 108:
 
#version 3.7;
 
#version 3.7;
  
#include "colors.inc"
+
#include "screen.inc" // requires the latest version appearing in this thread: http://news.povray.org/povray.text.scene-files/message/%3C5b889a96%40news.povray.org%3E/#%3C5b889a96%40news.povray.org%3E
#include "screen.inc" // requires the updated version available here: http://news.povray.org/povray.text.scene-files/thread/%3C5b889a96%40news.povray.org%3E/
 
  
 
global_settings {
 
global_settings {
Line 114: Line 114:
 
}
 
}
  
// ----------------------------------------
+
// -------------------------------------
  
 
#local cam_view = 0;
 
#local cam_view = 0;
Line 120: Line 120:
 
#switch (cam_view)
 
#switch (cam_view)
 
#case (0) // perspective
 
#case (0) // perspective
#local cam_dist = 8;
+
#local cam_dist = 12;
#local cam_move = 1/2;
 
#local cam_area = 2;
 
 
#local cam_loca = -z * cam_dist;
 
#local cam_loca = -z * cam_dist;
 
#local cam_look = 0;
 
#local cam_look = 0;
Line 129: Line 127:
 
{
 
{
 
rotate +x * asind(tand(30))
 
rotate +x * asind(tand(30))
rotate +y * 045
+
rotate +y * 300
translate +y * cam_move
 
 
}
 
}
 
Set_Camera_Orthographic(false)
 
Set_Camera_Orthographic(false)
Line 136: Line 133:
 
Set_Camera(cam_loca, cam_look, cam_angl)
 
Set_Camera(cam_loca, cam_look, cam_angl)
 
#break
 
#break
#case (1) // isometric (axonometric)
+
#case (1) // isometric
 
#local cam_aspc = image_width/image_height;
 
#local cam_aspc = image_width/image_height;
 
#local cam_dist = 8;
 
#local cam_dist = 8;
#local cam_move = 1/2;
+
#local cam_area = 4;
#local cam_area = 2;
 
 
#local cam_loca = -z * cam_dist;
 
#local cam_loca = -z * cam_dist;
 
#local cam_dirc = +z;
 
#local cam_dirc = +z;
Line 148: Line 144:
 
{
 
{
 
rotate +x * asind(tand(030))
 
rotate +x * asind(tand(030))
rotate +y * 045
+
rotate +y * 300
translate +y * cam_move
 
 
}
 
}
 
Set_Camera_Orthographic(true)
 
Set_Camera_Orthographic(true)
Line 157: Line 152:
 
#end
 
#end
  
sky_sphere {
+
background {color srgb <1,1,1>}
  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 {
 
light_source {
Line 175: Line 162:
 
}
 
}
  
// ----------------------------------------
+
light_source {
 
+
   <0, 0, 0>
plane {
+
   color rgb <1, 1, 1>
   +y, 0
+
  translate <+30, +30, -30>
   pigment {
+
  rotate y * 30
  checker
+
  parallel
  pigment {Gray}
+
   point_at 0
  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 {
+
box {
cylinder {-x,+x,0.1 pigment {Red}}
+
<-1,-1,-1>, <+1,+1,+1>
cylinder {-y,+y,0.1 pigment {Green}}
+
pigment {
cylinder {-z,+z,0.1 pigment {Blue}}
+
color srgbt <1,1,1,1/4>
transform {TempTrans}
+
}
 
}
 
}
 
  
 
#debug "\n"
 
#debug "\n"
  
#local SphereLoc_1 = vtransform(-x, TempTrans);
+
#local SphereLoc_1 = <+1,+1,+1>;
 
#local ScreenLoc_1 = Get_Screen_XY(SphereLoc_1);
 
#local ScreenLoc_1 = Get_Screen_XY(SphereLoc_1);
sphere {SphereLoc_1, 0.1 pigment {Cyan}}
+
sphere {SphereLoc_1, 0.1 pigment {color srgb SphereLoc_1}}
 
#debug concat("ScreenLoc_1 = (", vstr(2, ScreenLoc_1, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_1 = (", vstr(2, ScreenLoc_1, ",", 0, -1), ")\n")
  
#local SphereLoc_2 = vtransform(+x, TempTrans);
+
#local SphereLoc_2 = <+1,+1,-1>;
 
#local ScreenLoc_2 = Get_Screen_XY(SphereLoc_2);
 
#local ScreenLoc_2 = Get_Screen_XY(SphereLoc_2);
sphere {SphereLoc_2, 0.1 pigment {Cyan}}
+
sphere {SphereLoc_2, 0.1 pigment {color srgb SphereLoc_2}}
 
#debug concat("ScreenLoc_2 = (", vstr(2, ScreenLoc_2, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_2 = (", vstr(2, ScreenLoc_2, ",", 0, -1), ")\n")
  
#local SphereLoc_3 = vtransform(-y, TempTrans);
+
#local SphereLoc_3 = <+1,-1,+1>;
 
#local ScreenLoc_3 = Get_Screen_XY(SphereLoc_3);
 
#local ScreenLoc_3 = Get_Screen_XY(SphereLoc_3);
sphere {SphereLoc_3, 0.1 pigment {Magenta}}
+
sphere {SphereLoc_3, 0.1 pigment {color srgb SphereLoc_3}}
 
#debug concat("ScreenLoc_3 = (", vstr(2, ScreenLoc_3, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_3 = (", vstr(2, ScreenLoc_3, ",", 0, -1), ")\n")
  
#local SphereLoc_4 = vtransform(+y, TempTrans);
+
#local SphereLoc_4 = <+1,-1,-1>;
 
#local ScreenLoc_4 = Get_Screen_XY(SphereLoc_4);
 
#local ScreenLoc_4 = Get_Screen_XY(SphereLoc_4);
sphere {SphereLoc_4, 0.1 pigment {Magenta}}
+
sphere {SphereLoc_4, 0.1 pigment {color srgb SphereLoc_4}}
 
#debug concat("ScreenLoc_4 = (", vstr(2, ScreenLoc_4, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_4 = (", vstr(2, ScreenLoc_4, ",", 0, -1), ")\n")
  
#local SphereLoc_5 = vtransform(-z, TempTrans);
+
#local SphereLoc_5 = <-1,+1,+1>;
 
#local ScreenLoc_5 = Get_Screen_XY(SphereLoc_5);
 
#local ScreenLoc_5 = Get_Screen_XY(SphereLoc_5);
sphere {SphereLoc_5, 0.1 pigment {Yellow}}
+
sphere {SphereLoc_5, 0.1 pigment {color srgb SphereLoc_5}}
 
#debug concat("ScreenLoc_5 = (", vstr(2, ScreenLoc_5, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_5 = (", vstr(2, ScreenLoc_5, ",", 0, -1), ")\n")
  
#local SphereLoc_6 = vtransform(+z, TempTrans);
+
#local SphereLoc_6 = <-1,+1,-1>;
 
#local ScreenLoc_6 = Get_Screen_XY(SphereLoc_6);
 
#local ScreenLoc_6 = Get_Screen_XY(SphereLoc_6);
sphere {SphereLoc_6, 0.1 pigment {Yellow}}
+
sphere {SphereLoc_6, 0.1 pigment {color srgb SphereLoc_6}}
 
#debug concat("ScreenLoc_6 = (", vstr(2, ScreenLoc_6, ",", 0, -1), ")\n")
 
#debug concat("ScreenLoc_6 = (", vstr(2, ScreenLoc_6, ",", 0, -1), ")\n")
 +
 +
#local SphereLoc_7 = <-1,-1,+1>;
 +
#local ScreenLoc_7 = Get_Screen_XY(SphereLoc_7);
 +
sphere {SphereLoc_7, 0.1 pigment {color srgb SphereLoc_7}}
 +
#debug concat("ScreenLoc_7 = (", vstr(2, ScreenLoc_7, ",", 0, -1), ")\n")
 +
 +
#local SphereLoc_8 = <-1,-1,-1>;
 +
#local ScreenLoc_8 = Get_Screen_XY(SphereLoc_8);
 +
sphere {SphereLoc_8, 0.1 pigment {color srgb SphereLoc_8}}
 +
#debug concat("ScreenLoc_8 = (", vstr(2, ScreenLoc_8, ",", 0, -1), ")\n")
  
 
#debug "\n"
 
#debug "\n"

Revision as of 11:42, 1 September 2018

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.)

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 the thread with the attachment 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 them as 2D screen coordinates. For instance, maybe there is a light source in your scene that you want to record the 2D position of:

#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 3D coordinates, just plug them into the Get_Screen_XY macro, like this:

#local light_location_2d = Get_Screen_XY(light_location_3d);

and send the result to 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

Here is a sample scene I created for this tutorial:

// Persistence of Vision Ray Tracer Scene Description File
// This file is licensed under the terms of the CC-LGPL.
// File: screen_cube.pov
// Vers: 3.7
// Desc: Screen and camera test
// Date: 08/30/2018
// Updt: 09/01/2018
// Auth: Michael Horvath
//

#version 3.7;

#include "screen.inc"		// requires the latest version appearing in this thread: 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 = 12;
		#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 * 300
		}
		Set_Camera_Orthographic(false)
		Set_Camera_Transform(cam_tran)
		Set_Camera(cam_loca, cam_look, cam_angl)
	#break
	#case (1)	// isometric
		#local cam_aspc = image_width/image_height;
		#local cam_dist = 8;
		#local cam_area = 4;
		#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 * 300
		}
		Set_Camera_Orthographic(true)
		Set_Camera_Transform(cam_tran)
		Set_Camera_Alt(cam_loca, cam_dirc, cam_rgvc, cam_upvc)
	#break
#end

background {color srgb <1,1,1>}

light_source {
  <0, 0, 0>
  color rgb <1, 1, 1>
  translate <+30, +30, -30>
  parallel
  point_at 0
}

light_source {
  <0, 0, 0>
  color rgb <1, 1, 1>
  translate <+30, +30, -30>
  rotate y * 30
  parallel
  point_at 0
}

// -------------------------------------

box {
	<-1,-1,-1>, <+1,+1,+1>
	pigment {
		color srgbt <1,1,1,1/4>
	}
}

#debug "\n"

#local SphereLoc_1 = <+1,+1,+1>;
#local ScreenLoc_1 = Get_Screen_XY(SphereLoc_1);
sphere {SphereLoc_1, 0.1 pigment {color srgb SphereLoc_1}}
#debug concat("ScreenLoc_1 = (", vstr(2, ScreenLoc_1, ",", 0, -1), ")\n")

#local SphereLoc_2 = <+1,+1,-1>;
#local ScreenLoc_2 = Get_Screen_XY(SphereLoc_2);
sphere {SphereLoc_2, 0.1 pigment {color srgb SphereLoc_2}}
#debug concat("ScreenLoc_2 = (", vstr(2, ScreenLoc_2, ",", 0, -1), ")\n")

#local SphereLoc_3 = <+1,-1,+1>;
#local ScreenLoc_3 = Get_Screen_XY(SphereLoc_3);
sphere {SphereLoc_3, 0.1 pigment {color srgb SphereLoc_3}}
#debug concat("ScreenLoc_3 = (", vstr(2, ScreenLoc_3, ",", 0, -1), ")\n")

#local SphereLoc_4 = <+1,-1,-1>;
#local ScreenLoc_4 = Get_Screen_XY(SphereLoc_4);
sphere {SphereLoc_4, 0.1 pigment {color srgb SphereLoc_4}}
#debug concat("ScreenLoc_4 = (", vstr(2, ScreenLoc_4, ",", 0, -1), ")\n")

#local SphereLoc_5 = <-1,+1,+1>;
#local ScreenLoc_5 = Get_Screen_XY(SphereLoc_5);
sphere {SphereLoc_5, 0.1 pigment {color srgb SphereLoc_5}}
#debug concat("ScreenLoc_5 = (", vstr(2, ScreenLoc_5, ",", 0, -1), ")\n")

#local SphereLoc_6 = <-1,+1,-1>;
#local ScreenLoc_6 = Get_Screen_XY(SphereLoc_6);
sphere {SphereLoc_6, 0.1 pigment {color srgb SphereLoc_6}}
#debug concat("ScreenLoc_6 = (", vstr(2, ScreenLoc_6, ",", 0, -1), ")\n")

#local SphereLoc_7 = <-1,-1,+1>;
#local ScreenLoc_7 = Get_Screen_XY(SphereLoc_7);
sphere {SphereLoc_7, 0.1 pigment {color srgb SphereLoc_7}}
#debug concat("ScreenLoc_7 = (", vstr(2, ScreenLoc_7, ",", 0, -1), ")\n")

#local SphereLoc_8 = <-1,-1,-1>;
#local ScreenLoc_8 = Get_Screen_XY(SphereLoc_8);
sphere {SphereLoc_8, 0.1 pigment {color srgb SphereLoc_8}}
#debug concat("ScreenLoc_8 = (", vstr(2, ScreenLoc_8, ",", 0, -1), ")\n")

#debug "\n"

Result

Here is what a rendering of the above sample scene should look like:

czech hedge hog.png

The center points of the spheres at the end of each arm are the points I am recording. You should be able to confirm in an 2D image editor that the measurements the script made are the correct ones.

SVG files are not working properly on this wiki, so here is a bitmap image of what a very simple GeoGebra image might look like.

czech hedge hog vector.png