10

We've tried to figure out for quite a while in the #python channel how to compute the eye/target/up vectors out of a view matrix using sympy. One possible way to do it could be:

from sympy import *
from pprint import pprint

v1, v2, v3, v4 = symbols('v1 v2 v3 v4')
v5, v6, v7, v8 = symbols('v5 v6 v7 v8')
v9, v10, v11, v12 = symbols('v9 v10 v11 v12')
v13, v14, v15, v16 = symbols('v13 v14 v15 v16')

V = Matrix([
    [v1, v2, v3, v4],
    [v5, v6, v7, v8],
    [v9, v10, v11, v12],
    [v13, v14, v15, v16],
    ])
u1, u2, u3 = symbols('u1 u2 u3', real=True)
t1, t2, t3 = symbols('t1 t2 t3', real=True)
e1, e2, e3 = symbols('e1 e2 e3', real=True)

U = Matrix([u1, u2, u3])
T = Matrix([t1, t2, t2])
E = Matrix([e1, e2, e3])

def calculate_view_matrix(up, eye, target):
    zaxis = (eye - target).normalized()
    xaxis = up.cross(zaxis).normalized()
    yaxis = zaxis.cross(xaxis)

    orientation = Matrix([
        [xaxis[0], yaxis[0], zaxis[0], 0],
        [xaxis[1], yaxis[1], zaxis[1], 0],
        [xaxis[2], yaxis[2], zaxis[2], 0],
        [0, 0, 0, 1],
            ])

    translation = Matrix([
            [1, 0, 0, 0],
            [0, 1, 0, 0],
            [0, 0, 1, 0],
            [-eye[0], -eye[1], -eye[2], 1],
            ])

    return orientation * translation

print(V - calculate_view_matrix(U, E, T))

s = solve([
    V - calculate_view_matrix(U, E, T),
    U.norm() - 1,
    T.norm() - 1],
    [u1, u2, u3, t1, t2, t3, e1, e2, e3])

print(s)

But for some reason that script has been running for ~20 minutes and sympy hasn't been able to give any solution so far.

Another attempt has also been trying to simplify the above generic problem to something simpler as how would you compute the up vector?

In a simpler context, the problem definition would be something like this:

  1. u,z,x are 3d vectors which forms an orthonormal basis.
  2. z, x are constant vectors
  3. u is the unknown vector

And the equation to solve this:

u.cross(z).normalized() - x

If you tried to solve a simple particular case of the above generic equation like this...

from sympy import *
u1,u2,u3=symbols('u1 u2 u3', real = True)
x=Matrix([1,0,0])
z=Matrix([0,0,1])
u=Matrix([u1,u2,u3])

print(solve(u.cross(z).normalized() - x, u))

you'd get NotImplementedError: could not solve u2 - Abs(u2).

NS: Thing is, in order to extract the inputs from the view matrix is required than the function computing the matrix be injective or bijective, otherwise the initial information will be lost. If you don't add any constraints the above functions are definitely not injectives because at the moment which is using a normalize operation the function becomes automatically not injective anymore, for instance:

a) normalize(x) = x/|x|
b) To prove normalize is injective then normalize(a)=normalize(b) should give a=b
c) normalize(a)=normalize(b) => a/|a|=b/|b| , which is not true then normalize is not injective

Of course, this could be trivially proved just saying than infinitelly vectors can provide the same normalized vector.

That's the reason why there has been added few constraints to calculate_view_matrix. Ie: U.norm() - 1, T.norm() - 1. Theorically, that should grant the calculate_view_matrix to become injective... (or not :))

So the main question would be, how can you constrain/modify properly the calculate_view_matrix so it can calculate the eye/target/up vectors out of the view matrix?

BPL
  • 9,632
  • 9
  • 59
  • 117
  • Just a little side note on the last part about finding `u`: I take it `u` just needs to be orthogonal to both `z` and `x` and needs to have unit length, right? So something like `u = np.cross(x, z) ; u /= np.linalg.norm(u)` should do it, right? (Just swap x and z if you need the opposite direction. Also, when `x` and `z` are already unit vectors, there's no need to normalize the `u` after the cross product, safe for correcting some inexactness.) If I'm not mistaken, the equation you're trying to solve expresses the same relationship, just using a detour. – blubberdiblub Apr 30 '17 at 14:56
  • If statement 1 is true (orthonormality), and z and x are known, then u is one two vectors: z^x or x^z. E.g. (u, v, x) is either a direct or indirect basis. Are you sure you stated the problem correctly? Your "equation to solve" does not have a left or right hand side. It's not clear what you are trying to solve. – mdavezac Apr 30 '17 at 15:12
  • @mdavezac sympy implicitly equates an expression (or multiple ones) against 0 when using `solve()` if you don't use `Eq()` explicitly. – blubberdiblub Apr 30 '17 at 15:15
  • Have you tried your code for concrete values for `v_1, ..., v_16`? Also I would try to simplify the Ansatz by fixing `v_16=1`. – coproc May 01 '17 at 19:00
  • see [Understanding 4x4 homogenous transform matrices](http://stackoverflow.com/a/28084380/2521214) – Spektre May 02 '17 at 07:11

1 Answers1

1

Besides a typo (T = Matrix([t1, t2, t2])) there are several flaws in your Ansatz of getting up, eye and target vectors back from a view matrix:

  • The view matrix describes a rigid transformation in 3D, which has only 6 degrees of freedom (3 axis rotations, 3 axis translations). This roughly means that only 6 out of the 16 values v1, v2, ..., v16 can be chosen (more or less arbitrarily), the others are dependent or determined any way (e.g. v4 = v8 = v12 = 0, v16 = 1, v3**2 = 1 - v1**2 - v2**2, ...). So in general the equations from the matrix difference are contradicting.
  • Even when requiring U.norm() - 1 = 0 the up-vector U can take infinitely many values (one angle is not determined). To reduce the possible solutions for U to finitely many cases one could add the condition U*(E-T) = 0.
  • The condition T.norm() - 1 = 0 is wrong. It is T - E (the direction of view) that could/should be requested to have length 1.

Altogether I do not see a way to fix the Ansatz s.t. U, E, T could be computed from the view matrix using equations and sympy. But U, E, T can be easily extracted from the view matrix:

  • a normalized U (fulfilling the above requirements) can be read from the second column
  • -E can be read from the last row
  • a normalized view vector E - T can be read from the third column

In sympy/Python code:

def calculate_up_eye_target(viewMat):
  eye = -viewMat[3,0:3].T
  target = eye - viewMat[0:3,2]
  up = viewMat[0:3,1]
  return up, eye, target
coproc
  • 6,027
  • 2
  • 20
  • 31