Is it possible to zoom to a certain point on screen by modifying the field of view and rotating the view of the camera as to keep that point/object in the same place on screen while zooming ? Changing the camera position is not allowed.
I projected the 3D pos of the object on screen and remembered it.
Then on each frame I calculate the direction to it in camera space and then I construct a rotation matrix to align this direction to Z axis (in cam space).
After this, I calculate the direction from the camera to the object in world space and transform this vector with the matrix I obtained earlier and then use this final vector as the camera's new direction.
And it's actually "kinda working", the problem is that it is more/less off than the camera's rotation before starting to zoom depending on the area you are trying to zoom in (larger error on edges/corners).
It looks acceptable, but I'm not settling for only this. Any suggestions/resources for doing this technique perfectly? If some of you want to explain the math in detail, be my guest, I can understand these things well.
Answer
Expanding on my comment, the basic approach for a single zoom-step would be:
- Project object on screen and remember coordinates
- Reduce fov
- Unproject remembered screen coords (raycast into view space)
- Rotate camera to align ray with the camera-to-object vector (in view space)
Step 4
When unprojecting the point using the inverse projection matrix, the ray will already be in view space (camera space) by default. To get the camera-to-object vector (let's call it toObj
) in view space, it's easiest to transform the object position using the VIEW * MODEL
transform. The camera is at [0, 0, 0]
in view space, so that transformed vector is toObj
.
The following assumes the Y-axis is up/down in view space. This may or may not be the case in your engine; verify this.
Get rotation that aligns ray with toObj
:
ray = normalize(ray)
toObj = normalize(toObj)
angle = acos(dotProduct(ray, toObj)) // NOTE: may need to negate this
if (angle < THRESHOLD) exit // THRESHOLD is a small value, like 0.001
axis = normalize(crossProduct(ray, toObj))
rotationRay = { angle, axis }
This would be the rotation to rotate the camera with, but in many cases, we'd get a funky orientation because the camera will be rolling as well (rotating up vector around forward axis).
To fix this, we first determine the rotated up-vector to get the right-vector when considering toObj
as the forward vector. Then we determine the desired right-vector and rotate around the toObj
axis to align them (Using the right-vectors is just to save an extra calculation that would occur to get the desired up-vector):
upRotated = rotate(UP_VECTOR, rotationRay) // UP_VECTOR is (0, 1, 0)
right = normalize(crossProduct(upRotated, toObj))
rightDesired = crossProduct(UP_VECTOR, toObj)
angle = acos(dotProduct(right, rightDesired)) // NOTE: may need to negate this
rotationRoll = { angle, ray }
The last statement rotates around the ray
axis, because we will do this transform after rotating the camera with rotationRay
, so from the camera's point-of-view, ray
will remain unchanged.
Now we have to combine these rotations and transform the combined rotation back to world space (since your camera transform is probably defined in world space).
// combine rotations with quaternion product
q = quaternion(rotationRoll) * quaternion(rotationRay)
rotationView = q.toAngleAxis()
// transform rotation to world space
axis = inverse(VIEW).transformNormal(rotationView.axis)
rotationWorld = { rotationView.angle, axis }
I've used a fictional transformNormal
method to emphasize that the transform disregards translation and should strictly also be done with the transpose of the inverse of the matrix in case of scaling, but that probably doesn't occur.
Now you have the camera's rotation to perform in world space. If the camera already has an orientation, you can combine both rotations using the quaternion product like demonstrated earlier.
Keep in mind this was all written without testing or sketching anything, so maybe I messed something up along the way. Regardless, I hope it points you in the right direction. Good luck :)
No comments:
Post a Comment