|
|
Back in 2006, Norbert posted a really interesting macro 'tool' -- an object
'position finder' (or 'new object placer' as I call it)--that I've only recently
become aware of. (Thanks, Thomas de Groot.) The code had a few flaws that I
thought I would try and fix, as it's quite magical in what it does. Norbert
even mentioned at the time that in was not very mature. My own code is below, in
a preliminary form. Currently, it's a stand-alone scene, for testing; I want to
turn it into an include file eventually..
The general idea of his code is to add and place an extra object into your scene
after the fact, purely by choosing pixel coordinates of a 'visual' point in
the screen preview render (or in a rendered image.) His code returns a rotation
and translation that you then plug into your new object, and it magically shows
up in proper 3D space in your re-rendered scene. (Then, you no longer need the
macro.) As Thomas pointed out elsewhere, this sames gobs of time trying to
'deduce' the new 3D coordinates that you need. (The scene here already includes
a 'new' test object.)
For all of this to work, a 'surface object' in the scene needs to be
pre-#declared and given to the macro-- usually the ground object in your scene;
that surface is traced automatically (within the macro.) It's a key concept of
Norbert's code. But this 'surface' can actually be a union of *many* objects;
then you can choose a position on any of them.
It's still Norbert's magic; I've just tried to make it more consistent. Many of
my own changes were arrived at experimentally, so the code may not be very
elegant. BTW, I didn't want to amateurishly fumble around with his core math;
that's intact.
Something I didn't understand at first: Of the two transforms that are
returned-- rotate and translate-- the rotation actually aligns the placed object
along the surface normal there. That's a nice Norbert touch (but see below.)
Here's Norbert's original code...
http://news.povray.org/povray.binaries.scene-files/attachment/%3Cweb.4478509041a06b1c9bff7e8b0%40news.povray.org%3E/pos
ition_finder.pov.txt
.... and his initial post about it...
http://news.povray.org/povray.binaries.images/thread/%3Cweb.44784f2141a06b1c9bff7e8b0%40news.povray.org%3E/?ttop=423977
&toff=3700
For picking the 'point of interest' in the initial preview render (the 2-D x/y
pixel position), Norbert originally chose to use screen *percentages*. I thought
of changing that to the render's actual pixel positions-- but Norbert's idea is
better: the code doesn't depend on the actual size/resolution/aspect ratio of a
render; that's taken care of automatically. (The slight CHANGE I made to that
scheme is, instead of the render percentage going from 0 to 100, it's now 0 to
1000 to get better precision for the point-of-interest-- simply because of the
way POV-ray shows percentages at the upper-left corner of a render preview when
you move your mouse cursor around: like 0.216 for the x-percentage there, which
would then be 216 as used in the code.)
A BIG caveat: I don't like posting code when I know it still has flaws, but
that's the case here, But rather than continuing to work on it for the next
week/month/year(!), I wanted to get it out for testing. The remaining flaw is
*specific* to chosen points on the 'underside' of objects (like the lower half
of a sphere.) As far as I can tell, the original code was not designed for this
situation. The trouble appears to be when Norm.y goes *negative*, and might also
depend on the camera location vs. the 'point-of-interest' (the vector between
the two.) I think the code lines that use vaxis_rotate and/or atan2 are
the culprits, but I don't know how to fix them, try as I might. Some things
need 'flipping' somewhere, when Norm.y goes negative. I tried lots of different
ad-hoc code additions elsewhere-- kludges, really-- which do work in *most*
circumstances but not all. (For example, my experiments worked for the underside
of horizontal cylinders , but not spheres.) For clarity's sake (and sanity!), I
left out all of that stuff. But if you restrict the point-of-interest to the
'upper surface' of objects, it seems to work consistently.
Also: The found rotation (based on the traced normal of the 'surface') is not
very accurate at times; put another way, the rotation gets better as the traced
normal approaches the +y direction (and not just 90-degrees orthogonal to any
particular surface, but basically parallel to the sky vector.) I've tried lots
of workarounds for this, but I haven't been able to disentangle all the things
that are going on. (To see this effect, try some positions on the upper
hemisphere of the large sphere but close to the horizontal mid-line; also move
the camera around.)
HOWEVER, the found *position* always works the way it should. AFAIK!
Some of my changes:
* The macro has its own camera now (temporary, used only when the macro is
actually in use.) It replaces your scene's camera, but uses the same data which
you supply.
* The camera and its look-at point can be anywhere now , with consistent results
(although I haven't tested EVERY possible combination)
* The indicator arrow now remains the same size (more or less), no matter where
it happens to be in 3D space. IMO, this aids clarity.
* The on-screen text is now fixed in the upper-left of the render. (I used a
matrix to do that, but I can't take credit for it-- it's 'borrowed' from the
original screen.inc file.)
* A strange mirror-flipping behavior (along the x-axis) has been eliminated.
* Note that it only works with a typical perspective camera.
*The code seems to be more precise if, for cam_ang, you use more of a 'zoom'
angle than a wide-angle (i.e., 30 is better than 67, for example.) This would
just be temporary, until the new object is correctly placed and you no longer
need the macro.
---------------- the code ------------
// 9/1-16/18 [Norbet Kern's original code from 2006, with modifications
// by Kenneth Walker]
#version 3.7; // or 3.71 or 3.8
global_settings{assumed_gamma 1.0}
// definitions (for test scene below) _____________________________________
#declare screen_x_position = 420; // screen percentages X 1000 (0 to 1000)
#declare screen_y_position = 623; // ditto
#declare cam_loc = <2,4,-8>;
#declare lookat = <.3,-.1,-.2>;
#declare cam_ang = 50; // 36
// [optional origin markers; pointing only in the POSITIVE directions]
union{
cylinder{0,.8*x .03 pigment{rgb 1.5}} // X-- WHITE
cylinder{0,.8*y .03 pigment{rgb <0,1,0>}} // Y-- GREEN
cylinder{0,.8*z .03 pigment{rgb <0,.2,1>}} // Z-- BLUE
text{ttf "cyrvetic.ttf" "+ X" 0.2, 0 scale .28
translate <.9,.04,0> pigment{rgb 1.5} no_shadow} // " + X"
text{ttf "cyrvetic.ttf" "+ Y" 0.2, 0 scale .28
translate <-.2,.9,0> pigment{rgb <0,1,0>} no_shadow} // " + Y"
text{ttf "cyrvetic.ttf" "+ Z" 0.2, 0 scale .28
translate <-.13,.04,.9> pigment{rgb <0,.2,1>} no_shadow} // "+ Z"
scale 1.3
translate .19*y
}
// [The NEW object you want to place in the scene...]
#declare NEW_OBJECT =
union{
cylinder{0,.6*x .06 pigment {rgb 1.5}} // WHITE for x-axis
cylinder{0,.6*y .06 pigment {rgb <0,1,0>}} // GREEN for y-axis
cylinder{0,.6*z .06 pigment{rgb <0,.2,1>}} // BLUE for z-axis
box{0,.3 translate -.15 pigment{rgb <1,.7,0>}} // YELLOW
scale .7
translate .15*y
}
#declare SURFACE =
union{
height_field {
function 500,500 {
pattern {bozo scale 0.08}
}
smooth
translate <-0.5,0,-0.5>
scale <7,0.4,7>
texture {
pigment{
average
pigment_map{
[1.0 rgb <0.5,1,0.3>]
[0.6 gradient x frequency 8
color_map{
[.07 rgb -.3]
[.07 rgb <0.7,1,0.7>]
}
]
[0.6 gradient z frequency 8
color_map{
[.07 rgb -.3]
[.07 rgb <0.7,1,0.7>]
}
]
}
}
finish {ambient 0 diffuse 0.5}
}
}
sphere{0,.9
pigment{
average
pigment_map{
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.5]
}
]
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.5]
}
rotate 90*z
]
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.5]
}
rotate 90*x
]
}
scale .03
}
translate <-1.8,.85,1.8>
}
box{0,2 translate -1 rotate 45*x rotate 45*z
pigment{bumps scale .05}
translate <-1.8,-.6,-1.5>
}
union{
cylinder {-1.4*z,1.4*z, .7
pigment{
average
pigment_map{
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.1]
}
]
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.1]
}
rotate 90*z
]
}
scale .03
}
translate <1.5,0,0>
}
cylinder {-1.4*z,1.4*z, .7
pigment{
average
pigment_map{
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.1]
}
]
[1.0 planar
pigment_map{
[.5 bumps scale 2]
[.5 rgb -.1]
}
rotate 90*z
]
}
scale .03
}
rotate -90*y
translate <2.8,0,1.4>
}
rotate 90*y
translate <1.5,.7,4.0>
}
} // end of 'SURFACE' union
// creating the macro___________________________________________________
#macro pos_finder (SURFACE, cam_loc, lookat, cam_ang, screen_x_position,
screen_y_position, arrow_scale)
#if(cam_loc.z <= 0)
// do nothing
#else
#local screen_x_position = 1000 - screen_x_position;
#end
#local screen_x_position = screen_x_position/10;
#local screen_y_position = screen_y_position/10;
#local cam_mirror = #if(cam_loc.z < lookat.z) -1; #else 1; #end
#local cam_z = 0.5*image_width/image_height/tan(radians(cam_ang*0.5));
#local cam_a = cam_mirror*image_width/image_height;
#local cam_s = y;
#local cam_d = vnormalize (lookat-cam_loc);
#local cam_r = vnormalize (vcross(cam_s,cam_d));
#local cam_up = vnormalize (vcross(cam_d,cam_r));
#local cam_dir = cam_d*cam_z;
#local cam_right = cam_r*cam_a;
#local TEXT_OBJECT_TRANSFORM =
transform {
matrix <
cam_r.x, cam_r.y, cam_r.z,
cam_up.x, cam_up.y, cam_up.z,
cam_d.x, cam_d.y, cam_d.z,
cam_loc.x, cam_loc.y, cam_loc.z
>
}
#local dir_y = vaxis_rotate (lookat-cam_loc,vcross (lookat-cam_loc,cam_up),
(0.5-screen_y_position*0.01)*cam_ang/image_width*image_height);
#local dir_xy = vaxis_rotate (dir_y,cam_up,
(0.5-screen_x_position*0.01)*cam_mirror*cam_ang);
#local Norm = <0,0,0>;
#local Inter = trace (SURFACE,cam_loc,dir_xy,Norm);
#if (vlength (Norm) != 0)
#local rx = degrees (atan2 (Norm.z,Norm.y + .0001));
#local rz = -degrees (atan2 (Norm.x,Norm.y + .0001));
#local precise = 3;
union{
cylinder {0.2*y,0.6*y,0.02}
cone {0,0,0.2*y,0.06}
text{ttf "cyrvetic.ttf" "POV-Ray" 0.2,
0 scale .2 translate <-.40,.5,-.02>} // "POV-Ray"
rotate <rx,0,rz>
scale arrow_scale*.0040*vlength(cam_loc - Inter)*cam_ang
texture{
pigment {color rgb .6*x}
finish {ambient .15 diffuse .85 specular 0.3 roughness 0.1}
}
translate Inter
}
union{
text{
ttf "cyrvetic.ttf"
concat ("rotate <",str (rx,0,precise-1),",0,",
str (rz,0,precise-1),">")
0.02,0
translate .6*y
}
text{
ttf "cyrvetic.ttf"
concat ("translate <",str (Inter.x,0,precise),",",
str (Inter.y,0,precise),",",str (Inter.z,0,precise),">")
0.02,0
translate -.6*y
}
no_shadow
scale .025*(cam_ang)/90
translate <-.38*(cam_ang/90),.32*(image_height/image_width)*
(cam_ang/90),.5>
transform{TEXT_OBJECT_TRANSFORM}
pigment {color rgb z}
finish {ambient .15 diffuse .85}
}
#debug concat ("\n","rotate <",str (rx,0,precise-1),",0,",
str (rz,0,precise-1),">\n")
#debug concat ("\n","translate <",str (Inter.x,0,precise),",",
str (Inter.y,0,precise),",",str (Inter.z,0,precise),">\n\n")
#else
#debug concat ("\n","chosen position not on traced SURFACE, please try
other screen_x_position or screen_y_position values\n\n")
#end
camera {
perspective
location cam_loc
look_at lookat
right cam_right
angle cam_ang
direction -z
}
#end // [end of macro]
// test scene __________________________________________________________________
// The macro call
pos_finder (SURFACE, cam_loc, lookat, cam_ang, screen_x_position,
screen_y_position, 1.0)
light_source {0 color rgb .7 translate <-200,200,-200>}
light_source {0 color rgb .7 translate <200,200,200>}
background {color rgb .7}
object {SURFACE}
object{NEW_OBJECT
// rotate ...*y // [optional, in case you don't like the basic x or
// z 'direction' that the new object is pointing in. This needs to come
// BEFORE the following transforms that are returned from the code]
// [the RETURNED on-screen rotation and translation-- replace with the
// new ones, in this order]
rotate <5.01,0,5.12>
translate <2.844,0.229,-2.911>
}
Post a reply to this message
|
|