0
\$\begingroup\$

I'm currently working on a particular project where orthographic and perspective projections are both used interchangeably. To keep the transition between both projection modes somewhat seamless, I make use of an adjustable focus point in 3D space. This works really well for my use case, but I'm having a bit of trouble getting some of the vector math right to maintain the distance between the camera and the given focus point when the user is in orthographic mode.

I'll try and demonstrate the problem with a small mock-up drawing below. The actual rendering is in 3D but I don't think this particular problem differs much in 2D.

The red circle is the focus point. It initially exists at position 1 with a particular distance between it and the camera, and I want to maintain this camera distance when the focus point is moved to position 2. One way to accomplish this is to apply the same translation to the camera as the focus point, but I don't want the view itself to visibly shift. What I think I need to do is project the camera along its forward vector by an unknown amount, so that it reaches one of the blue line positions so that the distance between position 2 and the camera is still the same. That 'unknown' distance is what I'm trying to solve. It seems that projecting the delta of the two positions along the camera's forward vector is not enough.

camera distance problem

While I know how to find the closest distance to the line, I can't really see this basic formula being adapted for maintaining a specific distance. I feel like I'm failing to grasp some fairly simple math here and would really appreciate if someone could point me in the right direction.

\$\endgroup\$
1
  • \$\begingroup\$ Do you really want to maintain the distance to the point (measured diagonally) or the depth to the point (only the component of the distance parallel to the camera's forward axis)? A typical perspective camera scales content based on depth, not distance, so if you're using this to control the visible size of an object then it's probably depth that you want. \$\endgroup\$
    – DMGregory
    Sep 26, 2022 at 11:24

1 Answer 1

0
\$\begingroup\$

What you've asked for is easy:

Just draw a circle around your initial focus point, whose radius is equal to your camera's distance from the point, so the circle crosses your camera's axis at the camera's position.

Now draw a circle with the same radius around your shifted point. The (up to two) points where this crosses the camera's forward axis are the points that are the same distance away as the original point was from the camera.

You can find the closest of these two points with a "ray versus circle intersection test" — search those keywords to find existing answers here showing the math. In 3D, it's a ray vs sphere intersection test, but the math is pretty much identical.

But there is a risk that this second circle/sphere sits entirely on one side of the camera's axis and never crosses it. In this situation, no exact solution exists, and the best you can do is fall back to the closest point on the line, or rotate/strafe your camera to follow the point.

What I think you actually want is even easier though:

If you're doing this to try to control the amount of perspective scaling an object at that point experiences through the camera's linear perspective lens, then it's important to note that this scaling varies based on the depth to the point, not the distance. That is, take the displacement vector between the camera and the point, and measure only the component parallel to the camera's forward axis, not its entire diagonal length.

This is both easier to compute, and guaranteed to always have an exact solution, so you don't need a fallback strategy like when the line misses the sphere.

Vector3 pointShift = point2position - point1position;
float depthShift = Vector3.Dot(pointShift, camera.forward);
camera.position += camera.forward * depthShift;

Here assuming camera.forward is a unit vector in the direction the camera is looking.

If you're always trying to maintain some fixed depth, this version will be more numerically stable and prevent accumulating rounding errors by taking the target depth as an input:

float currentDepth = Vector3.Dot(point2position - cameraPosition, camera.forward);
float error = currentDepth - targetDepth;
camera.position += camera.forward * error;
\$\endgroup\$
1
  • \$\begingroup\$ That's fantastic, thank you! Both solutions you've provided would work well. I forgot to mention that I perform this camera shift only when the camera is using orthographic projection, however the numerically stable version works well there as well. Cheers! \$\endgroup\$
    – JJJohan
    Sep 27, 2022 at 2:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .