Zoom to screen space selection box in 3D
Asked Answered
P

2

3

I wish to know the best algorithm to be able to zoom to a screen space selection box/window in 3D.

I have it currently sort of working but it doesn't seem to zoom in correctly when the distance from the target is significant.

Currently it works by figuring out the scale of the selection box relative to the viewport width/height and applies that scale to the distance/range of the camera to it's target.

Penurious answered 11/5, 2012 at 4:40 Comment(2)
Can you explain in more detail what you mean by "zoom to a screen space selection box?"Grampositive
What I mean by screen space selection box is a rectangle, or window, drawn by the user which is at screen space and not world space. I wish to then have the camera zoom into the region that is selected.Penurious
I
15

Actually it's not that simple. Look at this picture (it's a 2D projection for simplicity):

frustum1

The blue area is the current camera's view frustum. The yellow area is a part of the blue frustum covered by the rectangular selection of the screen. The aim is to make a new frustum which best represents the yellow area. The problem is what way should the new frustum fit the yellow area.

One possibility is presented in the picture below:

frustum2

The new camera's view frustum is coloured purple and the camera's eye lies on the green line. Assuming the new camera has the same properties (fovy, znear, zfar, aspect) as the old one, we can compute its new position and direction.

Now some calculations:

Height and width of the near plane:

h = 2 * tan(fovy/2) * znear
w = aspect * h

Screen-space coordinates of the rectangle:

rectangle = ( x0, y0, x1, y1 )

Screen-space center of the rectangle:

rcenter = ( (x0+x1)/2, (y0+y1)/2 )

Another image to clarify next calculations:

frustum3

View-space vector lying on the near plane, pointing from the near plane's center to the rectangle's center:

center = ( (rcenter.x / screen_width  - 0.5) * w,
           (0.5 - rcenter.y / screen_height) * h, 0 )

Then we have to transform this vector to world-space:

centerWS = center.x * camera_right_dir + center.y * camera_up_dir

New direction of the camera (dir2n):

dir1 = camera_dir * znear
dir2 = dir1 + centerWS
dir2n = normalize(dir2)

New position of the camera (pos2):

I've made some assumptions, in order to simplify calculations. Approximately new and old near planes are parallel, so:

(w, h) / dist = (w * (x1-x0)/screen_width, h * (y1-y0)/screen_height) / znear
(1, 1) / dist = ( (x1-x0)/screen_width, (y1-y0)/screen_height) / znear
(1/dist, 1/dist) = ( (x1-x0)/screen_width, (y1-y0)/screen_height) / znear

Hence:

dist = znear * screen_width / (x1-x0)

Which should be equal to:

dist = znear * screen_height / (y1-y0)

That is true only if the rectangle has the same proportions as the screen, which you can guarantee by locking the rectangle's proportions while the user is drawing it, or you can simply use only the rectangle's width (x1-x0) and ignore its height (y1-y0) or vice versa.

Finally:

pos2 = pos1 + dir2n * (dist-znear)
Instant answered 11/5, 2012 at 21:6 Comment(11)
Awesome explanation, I have yet to try it but I will accept this answer until proven incorrect ;) I was going to post a picture of the problem I was facing but I didn't have enough stackoverflow points so I was hoping that someone understood what I meant.Penurious
I have already forced the rectangle to be the same proportions as the screen so that will make things easy. The only other issue I will have is that we do support orthographic as well as perspective rendering so the maths for orthographic will be slightly different.Penurious
For the ortographic rendering that will be much easier actually ;)Instant
I implemented this today, but it appears that the distance calculated does not work for me (or I'm doing something wrong). For example if my near plane is 1, and my screen height is 668, and y1-y0 = 1 (i.e. a tiny rectangle), then my max distance is only 668 metres. This for me should be hundreds of kilometres. Not sure what I need to do to fix this :(Penurious
It really depends on how close the camera is to it's target. That must be what's missing. If the target is far then a selection will move the camera a greater distance than if the target were really close.Penurious
What do you mean 'target'? This method does not take any 3D models into consideration.Instant
By target I mean the cameras look at point. The main issue is just that the range calculated is far to small. The camera basically does not move at all with this method.Penurious
I've just implemented this method, here you have what I've found out: the scale of camera's movement depends on znear. That's because this method just works in that way. As I mentioned it's not simple to find a good new frustum. To make zoom noticable, you can multiply dist by a fixed factor (in my case znear=0.1, zfar=150, a factor about 100 gives a good result).Instant
Thanks for the suggestion. Unfortunately for me using a constant factor won't work because my near plane is actually a factor of the distance to the camera target (i.e. the closer you get to the target the closer the near plane will be to the camera). Haven't been able to find an acceptable way to do this yet. Maybe I'll have to settle for second best for now...Penurious
@Instant is there anyway you could shed some light onto how things change (math wise) when moving to orthographic?Saraisaraiya
When using orthographic projection you simply need to change the projection's width and height to match the rectangle's dimensions and offset the camera's position towards the center of the rectangle (along the projection plane). You could also change near/far planes but it's not necessary. The direction of the projection doesn't change.Instant
C
1

What I understand is that you a rectangle drawn on screen to align with the window boundaries and have everything inside that rectangle zoom in.

If I am right, what we are trying to achieve here is to align the near-plane with the an imaginary plane on which the rectangle was drawn.

To do this, we have to play around with some trigonometry. Assuming we align top and bottom of the rectangle:

We should compute the height of the near plane as:
H = tan(fovy/2) * nearPlane; --------(1)

height of the drawn rectangle on near plane:

fraction = WindowHeight / rectangle height;  
h = H * fraction; ---------(2)

To align near plane with the imaginary plane, camera must move forward. Assume D is the distance to move forward, then by geometry,
h / nearPlane = H / (nearPlane+D) --------(3)

Using equation(2), equation (3) can be reduce as:

fraction / nearPlane = 1 / (nearPlane+D)

which gives D as:

D = (nearPlane / fraction)  - nearPlane; 

or

D = nearPlane * (1-fraction)/fraction;

Now move camera forward by D. and that should do it.

If the rectangle was not center aligned, computation is slightly more complex.

Cultigen answered 11/5, 2012 at 13:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.