Benoît Jacob ([info]bjacob) wrote,
@ 2006-12-29 16:43:00
Previous Entry  Add to memories!  Tell a Friend!  Next Entry
Entry tags:eigen opengl

Eigen example: the OpenGL camera problem
It looks like Eigen 1.0 will be out in 2006 as promised. For this occasion, I have fixed a small example program. Since the beginning, one of the primary goals of Eigen has been to provide the math functionality that is useful in OpenGL apps. And one of the most common problems when writing OpenGL apps is, how to let the user freely move the camera around? Representing the camera orientation by 3 angles just doesn't work, and leads to the gimbal lock problem. So the best solution is to represent the camera position and orientation by a 4x4 matrix and pass it directly to OpenGL. The question is then, how to generate this matrix from the user input? This is where Eigen comes into play. Here is the GLWidget::mouseMoveEvent() function from the Eigen example program:

void GLWidget::mouseMoveEvent( QMouseEvent *event )
{
    QPoint delta = event->pos() - m_lastPos;
    m_lastPos = event->pos();
    if( event->buttons() & Qt::LeftButton )
    {
        m_cameraMatrix.prerotate3( - delta.x() * ROTATION_SPEED, AXIS_Y );
        m_cameraMatrix.prerotate3( - delta.y() * ROTATION_SPEED, AXIS_X );
    }
    if( event->buttons() & Qt::RightButton )
    {
        m_cameraMatrix.prerotate3( - delta.x() * ROTATION_SPEED, AXIS_Z );
        m_cameraMatrix.pretranslate( delta.y() * TRANSLATION_SPEED * AXIS_Z );
    }
    if( event->buttons() & ( Qt::LeftButton | Qt::RightButton ) )
        update();
}

This lets the user rotate around by drag & drop with the left mouse button, and tilt and move forwards/backwards by drag & drop with the right mouse button. Then the paintGL function looks like
void GLWidget::paintGL()
{
    glLoadMatrixd( m_cameraMatrix.array() );
    drawScene();
}

Obligatory screenshot:


(Post a new comment)


(Anonymous)
2007-01-03 04:57 am UTC (link)
i know this is just example code, but the matrix calculation is not optimal for two reasons:

1) efficiency: you're using two matrix multiplications per mouse move event
2) floating-point precision: you can expect over time that the calculations will get very imprecise.

a solution to both of these problems is to store the current x,y and z angles and calculate the camera matrix from scratch every time. for efficiency, you can define a function that returns a rotation and translation matrix for x, y and z angles and a translation vector that uses less multiplications than two complete matrix multiplications.

that being said, i see that the eigen API is quite readable and it seems pretty easy to use.

(Reply to this)(Thread)


[info]bjacob
2007-01-03 10:14 am UTC (link)
1) efficiency: you're using two matrix multiplications per mouse move event

that is true. However, there can be at most a few hundreds mouse movements per second, so this code will never be performance-critical.

2) floating-point precision: you can expect over time that the calculations will get very imprecise.

No, you're missing a key point here: the computed matrix is being used as the OpenGL modelview matrix. Which means that from the user's point of view, the matrix is always perfectly accurate, in that it perfectly matches the perceived camera orientation and position. And that's all what matters here.

a solution to both of these problems is to store the current x,y and z angles

there is no such thing as the "current x,y and z angles". It is a bad idea to represent a rotation by 3 angles, because there is no natural way to do so. Navigation systems based on these "Euler angles" always suffer from the gimbal lock, i.e. :
- the mouse movements gets interpreted in different ways for different camera orientations;
- for orientations that are near to either of the two poles, the mouse movement is interpreted in such an unexpected way that you get the feeling you can't control things anymore, that's the "gimbal lock".

(Reply to this)(Parent)(Thread)


(Anonymous)
2007-01-04 03:09 am UTC (link)
you're right about the performance in that case; but in real-life applications, every cpu cycle counts. but i don't really get what you're saying about the floating-point precision. you have to admit that the precision slowly but inevitably gets worse with every matrix multiplication you do.

i know about the gimbal lock problem and i know there is a solution to that which is different to your solution (i don't feel like getting up and take my copy of "realtime rendering" outta my shelf; if you don't know what i'm talking about, post a reply and i will get up :) ). what i tried to say is that the right solution would store some variables which represent the camera's orientation, rather than a matrix. on each mouse movement, you'd calculate the matrix from scratch.

the efficiency problem is probably negligible, but with your solution, the prescision problem will be noticable after a few hours of playing around with the example. it's a known fact about floating-point calculations that a lot of multiplications hurt precision. that might not be a problem in that particular case, but in real-life applications, like computer games, it certainly is. since you present an example of how to use your linear algebra library with opengl, i think it's important that you, as a "role model", show people how to use it in a way which has proven to be the "right way".

(Reply to this)(Parent)(Thread)


[info]bjacob
2007-01-04 10:28 am UTC (link)
in real-life applications, every cpu cycle counts

Actually I have written down the formula for the rotation matrix given by 3 angles around the x,y,z axes, and it's much more complicated than my current rotation method. A lot of multiplications, and 3 cos and sin computations (instead of currently 1) so I'm not sure it would be much faster than what I currently do, even considering that it spares one matrix multiplication. It would be slightly faster to make a rotation only around the x and y axes, since that's all what I'm using, but this doesn't fit well into Eigen's API (too special).

but i don't really get what you're saying about the floating-point precision

OK there are actually two separate issues there, one is serious, one isn't:

1. the matrix is not exactly the correct rotation matrix
2. the matrix is not exactly a rotation matrix at all

I my previous reply, I adressed 1. only, and I maintain that 1. is not an issue. At each frame, the computed matrix gets loaded as OpenGL modelview matrix, which means that the user's point of view gets synced which it. It becomes _the_ correct matrix, by definition. The imprecision is only in the process of converting user input to a modification of this matrix, and since this imprecision is of the of the order of 1e-14, it is totally negligible in comparison with the user's mouse imprecision, not to mention the user's own imprecision.

But 2. is a more serious problem, so I assume that's what you're talking about. It is true that at each frame we're drifting away from even being a rotation matrix (I mean, the topleft 3x3 block must be a rotation matrix in the sense that it satisfies M * adjoint(M) = Identity). This means that the scene gets distorted. As you say, this accumulates over frames, and could become noticeable after at least 1e+10 frames (which, with 100 frames per second, means several monthes... still an issue for servers or embedded systems). Fortunately, there is a very simple and good solution to that problem. There exists a natural mathematical way of associating to any square matrix the "closest rotation matrix". More generally, any square matrix decomposes naturally into the product of a positive selfadjoint matrix and a unitary matrix. This is called the "polar decomposition". In our case, our matrices are real, so "unitary" is synonymous to "rotation". So what I propose is that once in a while (say every 1000 frames), we replace the current pseudo-rotation matrix by the actual rotation matrix given by its polar decomposition.

if you don't know what i'm talking about, post a reply and i will get up

I think you're talking about quaternions, but they have exactly the same problem: not every quaternion represents a rotation, only quaternions of norm 1 do, and you don't want to have to normalize at each frame (you were talking about speed) so it's exactly the same as rotations :)

(Reply to this)(Parent)

ONLINE - DRUGSTORE!
(Anonymous)
2007-11-02 05:30 am UTC (link)
ONLINE - DRUGSTORE!
PRICES of ALL MEDICINES!

FIND THAT NECESSARY...
VIAGRA, CIALIS, PHENTERMINE, SOMA... and other pills!

Welcome please: pills-prices.blogspot.com


NEW INFORMATION ABOUT PAYDAY LOANS!

Welcome please: payday-d-loans.blogspot.com

GOOD LUCK!

(Reply to this)

tXeucTxlstVfmtCzF
(Anonymous)
2008-05-20 12:47 am UTC (link)
nice site dude

(Reply to this)

IeoUjJWMvertB
(Anonymous)
2008-06-05 08:42 am UTC (link)
Iyuxpw fdf043hj93jkfjw845qgtj6fqp

(Reply to this)

TgxLUGPeuLtutSr
(Anonymous)
2008-07-01 07:53 pm UTC (link)
Good crew it's cool :) flights and hotel for las vegas cheap hotels near michigan ave in chicago illinois =]]

(Reply to this)

gYFAujVdPiVkrTbl
(Anonymous)
2008-07-02 02:06 am UTC (link)
Best Site Good Work chicago cheap carabean hotels 046171

(Reply to this)

ftCkJqXuvJjWXi
(Anonymous)
2008-07-02 06:17 am UTC (link)
this is be cool 8) bangalore cheapest hotels in marina del rey ashed

(Reply to this)

uezBBvqpYU
(Anonymous)
2008-07-02 07:50 am UTC (link)
this post is fantastic vegas cheap hotels in maui 415519

(Reply to this)

uLiIEAFurn
(Anonymous)
2008-07-02 01:09 pm UTC (link)
very best job beach cheap hotels cheap hotel yuma 610905

(Reply to this)


Create an Account
Forgot your login?
Login w/ OpenID
English • Español • Deutsch • Русский…