So I have a camera in my WIP game that allows for WASD type movement (front, back and strafing) via glm translation matrix. When implementing a glm rotation for horizontal look around motion, I realized that it only worked at the axis origin, because that is the point of revolution. If I translate the world before rotating, it causes it to rotate around a place where the camera isn’t, a very undesirable effect. So I figured I could do some trig to translate the position while rotating to make it seem as though the camera / player is still in place. But this game will have a very large world, possible to go out thousands of units. This will mean that the translation will involve a very large circle radius if the camera / player is far from the origin. What’s the best solution?
If you’re concerned about floating-point accuracy for large models, the usual solution is to split the world into chunks, with vertex positions within each chunk specified relative to the chunk’s origin. The calculation of the viewpoint and the translation of each chunk’s origin relative to the viewpoint is calculated client-side using double-precision arithmetic. OpenGL 4.1 and later support double-precision vertex attributes but there’s a substantial performance penalty for using double-precision arithmetic on the GPU.
Single precision has a relative error of ~1.2e-7, i.e. 1.2 mm in 10 km. This allows you to determine how large a chunk can reasonably be given the minimum distance between the viewpoint and any geometry. Double precision has a relative error of 2.2e-16, which corresponds to an absolute error of ~1.6mm in the aphelion of Pluto’s orbit (~7.4e9 km).
Interesting thanks for the info. I was wondering actually what the usual solution was, how do most video game developers handle this? I was thinking about a rotation matrix that would rotate the vertices around a specific point, instead of the origin. This way there doesn’t have to be a translation in addition to the rotation.
Rotation about a specific point is a combination of translation and rotation, e.g.
glTranslatef(x,y,z); glRotatef(...); glTranslatef(-x,-y,-z);
Whether you combine the two into a single matrix or keep them separate, the precision issues remain.
The difference between the world/model-position and camera-position is the translation you need to do before rotating points … and then back.
… addition …
The formal approach may be something along these lines:
start the view-matrix as default identity.
It’s intuitive to bild the view of the world in this matrix.
Rotate it as you need and translate it to where it should be.
Instead of using it to transform points, you take it’s inverse transpose and uses that instead.
This is an FPS type game (or it will be), so the world is always being translated to simulate movement. Rotating after translating still causes the world to rotate around the origin.
If the rotation is the first (left-most) operation performed on the model-view matrix, it will rotate the view around the viewpoint.
The typical way to handle cameras in a non-trivial program is to treat them like objects (i.e. construct a transformation from object space to world space) then apply the inverse of the camera transformation prior to any object transformations. If matrices A and B each transform from object space to world space (or to any common coordinate system), the transformation A^-1.B transforms from B’s object space to A’s object space. This approach makes it straightforward to attach a camera to an arbitrary object.
The legacy OpenGL matrix operations don’t make this approach straightforward, as you can’t create isolated matrices and it doesn’t provide an operation to invert a matrix (as GLM does). So the usual approach was to start by constructing the inverse camera transformation using the fact that (A.B.C)^-1 = C^-1.B^-1.A^-1 (i.e. the inverse of a sequence of matrices is the reversed sequence of their individual inverses) and the fact that the individual operations (rotation, translation and scale) can trivially be inverted (by negating translation offsets, negating rotation angles, or using the reciprocal of scale factors).
For an object,
glTranslate(x,y,z); glRotate(angle,ax,ay,az); will place the object’s origin at (x,y,z) and rotate about that point. To do likewise for a camera, you’d use
Thanks, I just got it now after wrapping my head around the fact that the axes change with the rotation, so I have to adjust the translations accordingly. Now I need an aspirin and a break
Oh wait, so now whenever I turn, my translations won’t work the same. So I guess I’ll need to use dynamic vectors instead of static floats for movement…unless there is a better way.
It’s flattering to get a “v solution”, but I fear that it won’t work.
mat4 wm = world_matrix; (after translates and rotates on that matrix)
do the inverse transpose on wm, meaning
swap last column (position) for minus position
there is a function doing this … turning the effect of a rotate alfa to rotate minus alfa
If this won’t work, I’m blank if you cannot use lookAt(new_position_world, vec3(0,0,0)). And, if it does work, I’ll be surprised. ;o/
it would make more sense to fully substitute the world_matrix with wm
It works great, its just that the translations would have to be vectorized. Tried what you said but um not pretty the results :
mat4 wm = translation * x_rotation * z_rotation; mat4 last = projection * transpose(wm); GLuint MVPLoc = glGetUniformLocation(shaderProgram, "MVP"); glUniformMatrix4fv(MVPLoc, 1, GL_FALSE, &last);
It ought to … it’s not any more than we all new from scratch: Let camera stay fixed looking down -z, rotate world/model and translate it down -z. Translate it significantly away from z and it’ll be out of sight.
I meant that having the rotation matrix before the translation one worked, the transpose stuff did not. The caveat being I now have to have the translations as two component vectors, that are calculated based on rotation angle.
- Edit, got the movement vectors sorted, took some basic trig but its rather elegant and simple. The pitch was easy to implement and thus I have full FPS motion control
Next step, mouse input!
I’m left stupified enough to go look up Dave Shreiner’s “Opengl programming guide, seventh edition” in an attempt to find what I’m missing.
He talks about two ways of thinking about model-view transformations. One, the grand global coordinate-system, the other a model localized system. Thinking in each frame of reference seems to provide different order of setting up rotation and translation matrices. However. When you look at the final code, there is none, whatsoever difference on the two appearently different ways of setting up the matrices.
Have to admit that it surprises me.
That makes sense, I don’t see why the matrices would have be different. In one there is a translation on a rotation (what I’m doing), in the other there is a rotation on the translation. It works because the translation matrix is simple, its an identity matrix with only the right column set to values. Anyways, I sure am glad the solution isn’t complicated.