0

Scenario

I have a scene which contains several cameras, which may be facing any direction. I have no control over the creation of scene or cameras, but I need to work out which direction the cameras are facing in order to align them correctly.

The code below is a sample based on simple numbers, and I think shows that I'm missing something fundamental.

I am currently extracting the forward vector from the camera matrix using

Vector3 forward = new Vector3(-worldToCamera.M13, -worldToCamera.M23, -worldToCamera.M33);

as specified at http://roy-t.nl/2010/03/04/getting-the-left-forward-and-back-vectors-from-a-view-matrix-directly.html

However, for any given rotated input, this always returns (0, 0, -1). My question is:

How do I get the forward direction in world space from the camera matrix?

Code

using System;
using System.Numerics;

public class Program
{
    private const double OneEightyOverPI = 180d / Math.PI;
    private const double TwoPI = Math.PI * 2d;
    private const double PIOverTwo = Math.PI / 2d;
    private const double ThreePIOverTwo = 3d * Math.PI / 2d;

    public static void Main()
    {       
        Vector3 cameraPosition = new Vector3(5, 5, 5);
        Matrix4x4 translate = Matrix4x4.CreateTranslation(-cameraPosition); 
        Matrix4x4 cameraMatrix = translate;

        Test(cameraMatrix, cameraPosition, 0);

        // Z-axis is vertical, so rotate around it to change the pan angle of the camera
        cameraMatrix = Matrix4x4.CreateRotationZ((float)PIOverTwo) * translate;     

        Test(cameraMatrix, cameraPosition, 90);

        cameraMatrix = Matrix4x4.CreateRotationZ((float)Math.PI) * translate;       
        Test(cameraMatrix, cameraPosition, 180);

        cameraMatrix = Matrix4x4.CreateRotationZ((float)ThreePIOverTwo) * translate;        
        Test(cameraMatrix, cameraPosition, 270);
    }

    private static void Test(Matrix4x4 worldToCamera, Vector3 cameraPositionWorld, int expected) {  
        // http://roy-t.nl/2010/03/04/getting-the-left-forward-and-back-vectors-from-a-view-matrix-directly.html
        Vector3 forward = new Vector3(-worldToCamera.M13, -worldToCamera.M23, -worldToCamera.M33);

        // input always aligned such that:
        Vector3 north = Vector3.UnitY;

        double angle = Math.Atan2(north.Y, north.X) - Math.Atan2(forward.Y, forward.X);


        if (angle < 0) {
            angle += TwoPI;
        }

        int deg = (int)(angle * OneEightyOverPI);

        Console.WriteLine("Expected: " + expected + ", actual: " + deg + ", diff: " + (expected - deg));    
    }
Ben Owen
  • 102
  • 9
  • First of all, why the minus signs? The post says you should take the elements directly. Also, `translate` should come before the rotation matrix. – meowgoesthedog Aug 15 '17 at 14:18
  • In the example linked in the question, the author defines a `Backward` vector as the positive values from the matrix and then a `Forward` vector which was the negative of `Backward`. I chose not to implement `Backward`. Of course, this may be wrong! – Ben Owen Aug 15 '17 at 14:21
  • I see. I just found it strange that he decided to define it that way; usually that vector is the forward direction of the camera (so that Z-values are positive). I think I've spotted the error though - the Z axis of `CreateRotationZ` is (in effect) not the world Z-axis, but the *camera*'s Z-axis, i.e. its (reverse) direction. A rotation about an axis will obviously not change a vector parallel to that axis. Try `CreateRotation(X/Y)` instead and see what you get. Therefore your math is in fact correct, and the result is as expected. – meowgoesthedog Aug 15 '17 at 14:22
  • Thanks for the suggestions! Using `CreateRotationX()`, all tests pass except the 180° case (which incorrectly returns 270). Using `CreateRotationY()`, all cases return 0° except for 90° (which returns 180). I guess that means some other part of my calculation is wrong. Unfortunately, in the real-world application, I'm not creating the matrices (they come from another software package) so can't change how they have been created. Presumably using one of the other columns would have the same effect? – Ben Owen Aug 15 '17 at 14:52
  • Not sure what you mean by "using another column"; another column would be a different camera vector, as the post says. – meowgoesthedog Aug 15 '17 at 14:57
  • OK - let's forget that then. If my maths is correct, then perhaps I need a different approach to the problem - because the results are not what I need. For any given input matrix from the third party software, I get a forward vector equal or very close to (0,0,-1). I need the forward vector of the camera *in world space*, rather than to know that the camera is looking down it's -Z axis. It doesn't seem to be as simple as applying the `cameraToWorld` matrix to the extracted forward vector? – Ben Owen Aug 16 '17 at 08:09
  • No, the extracted forward vector *is in world space* (that's the point of the camera matrix). It is also the -Z axis in the *camera's frame of reference*. – meowgoesthedog Aug 16 '17 at 09:00
  • I think I might have figured it out eventually - I really wanted the angle of rotation around the Z-axis. From http://nghiaho.com/?page_id=846: `angle = Math.Atan2(worldToCamera.M21, worldToCamera.M11) - PIOverTwo`. This passes all my real-world tests to within 5° of my expected outputs (which were quite crude). Thanks a lot for your help, I was tying myself up in knots. If you want to make an answer along the lines of "that part of your code is correct, you're asking the wrong question" I will happily accept! – Ben Owen Aug 16 '17 at 12:14
  • That's fine, as long as your question has been answered, there is no need for me to post; however you could post an answer yourself, detailing how your problem was solved and what you've understood, for future reference. – meowgoesthedog Aug 16 '17 at 12:21
  • see [Understanding 4x4 homogenous transform matrices](https://stackoverflow.com/a/28084380/2521214) so you need to know the notation of your matrix and also if it is direct or inverse matrix ... (cameras are usually inverse in OpenGL and direct in DirectX). From that just extract 3th row or column (counting from 1). however if your matrices are not just camera matrix but the cumulation of modelview or whatever else then this approach will not work – Spektre Aug 18 '17 at 08:42

0 Answers0