0
\$\begingroup\$

Given a fixed rotation and a target "center" point, how to I find the position the camera should be in so that that point is the center? LookAt changes the rotation keeping the position constant. Basically, I'm trying to do the reverse, but the math escapes me :-). Thanks!

Camera is currently psuedo-isometric (orthographic with rotation from Euler angles [X:35.264389683ish°, Y:45°, Z:0°]). Might want to change these a bit or allow rotation though, so ideally a solution would work for any rotation. Can easily get the projection matrix.

EDIT: thinking about it, there would be infinite such positions that satisfy this constraint. I would want to fix the Y component of the position (camera height) and find only an X/Z.

Thanks to Mokosha's answer below, here's the final code I'm using:

    private static Vector3 reverseLookAt(Quaternion cameraRotation, float cameraHeight, Vector3 targetPosition)
    {
        Vector3 d = cameraRotation * Vector3.forward;
        Plane plane = new Plane(Vector3.up, -cameraHeight);
        Ray ray = new Ray(targetPosition, d);
        Vector3 pos = UnityUtils.intersect(ray, plane);
        return pos;
    }
\$\endgroup\$
2
\$\begingroup\$

Let's rephrase your question:

Given a rotation R, and a position p, we would like the rotated point p' to lie along the Z-axis (also known as the center of the camera). For this, we can use linear algebra:

  1. Compose the rotation matrix R from your euler angles.
  2. Solve the problem Rd = [0 0 1]^T, or

               | 0 |
    d = inv(R) | 0 |
               | 1 |
    
  3. d is now the direction in world space that corresponds to the Z-axis of the camera in eye space. Construct p' by translating p by d: p' = p + d.
  4. Using p and p', construct a line P.
  5. Compute the intersection of P with the plane whose normal is [0 1 0] at D distance away from the origin where D is the desired Y value of your camera in world units.

The code for this in Unity's C# would look something like this:

Quaternion rot = ...
Vector3 p = ...
float desired_y = ...

Vector3 d = rot * (new Vector3(0, 0, 1)); // Perhaps Camera.forward...?

Vector3 camPos;
if (Mathf.Abs(d.y) < Mathf.epsilon) {
  Log.Error("Camera direction parallel to XZ plane");
} else {
  Plane y_plane = new Plane(new Vector3(0, 1, 0), desired_y);
  float dist;
  y_plane.Raycast(new Ray(p, p + d), dist)
  camPos = r.GetPoint(dist);
}
\$\endgroup\$
  • \$\begingroup\$ Thanks! Step 2 seems like it's missing something though since p' = inv(R)*(0,0,1) doesn't seem to involve p at all. \$\endgroup\$ – Robert Fraser Apr 29 '15 at 0:07
  • \$\begingroup\$ Sorry, I used the wrong terminology, d is the direction in world space that you need to translate p. I've edited my post. \$\endgroup\$ – Mokosha Apr 29 '15 at 0:14
  • \$\begingroup\$ Tried that and something still seems off. For example using the above rotation, targetPos = (0, 0, 0) and desired_y = 10 (and flipping direction), I get a point at (-17.32051, 10, 14.14213), which seems way off in no man's land. The solution looks to be somewhere around (-10, 10, -6) or so. \$\endgroup\$ – Robert Fraser Apr 29 '15 at 0:39
  • \$\begingroup\$ Never mind, figured it out! You don't invert the rotation. \$\endgroup\$ – Robert Fraser Apr 29 '15 at 0:52
  • 1
    \$\begingroup\$ It depends on how your rotation is defined. I've updated the code for Unity's conventions. \$\endgroup\$ – Mokosha Apr 29 '15 at 1:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.