0

Edit: My question may be too complex for what I am really asking, so skip to the TLDR; if you need it.

I have been programming 3D graphics for a while now and up until now I never seemed to have this issue, but maybe this is the first time I really understand things like I should (or not). So here's the question...

My 3D engine uses the typical OpenGL legacy convention of a RH coordinate system, which means X+ is right, Y+ is up and Z+ is towards the viewer, Z- goes into the screen. I did this so that I could test my 3D math against the one in OpenGL.

To coop with the coordinate convention of Blender 3D/Collada, I rotate every matrix during importing with -90 degrees over the X axis. (Collada uses X+ right, Y+ forward, Z+ up if I am not mistaken)

When I just use the projection matrix, an identity view matrix and a model matrix that transforms a triangle to position at (0, 0, -5), I will see it because Z- is into the screen.

In my (6DOF space) game, I have spaceships and asteroids. I use double-precision coordinates (because they are huge) and by putting the camera inside a spaceship, the coordinates are made relative every frame so they are precise enough to fit a single-precision coordinate for rendering on the GPU.

So, now I have a spaceship, the camera is inside, and it its rotation quaternion is identity. This gives an identity matrix and if I recall correctly, row columns 1-3 are representing the X, Y and Z axis of where the object is pointing at. To move the ship, I use this Z axis to go forward. With the identity matrix, the Z-axis will be (0, 0, 1).

Edit: actually, I don't take the columns from the matrix, I extract the axes directly from the quaternion.

Now, when I put the the camera in the spaceship, this means that its nose is pointing at (0, 0, 1) but OpenGL will render with -1 going into the screen because of its conventions.

I always heard that when you put the camera inside an object in your scene, you need to take the model matrix and invert it. It's logical: if the ship is at (0, 0, 1000) and an asteroid is at (0, 0, 1100), then is makes sense that you need to put the camera at (0, 0, -1000) so that the ship will be at (0, 0, 0) and the asteroid will be at (0, 0, 100).

So when I do this, the ship will be rendered with its nose looking at Z-, but now, when I start moving, my ship moves to its rotation (still identity) Z being (0, 0, 1) and the ship will back up instead of going forward. Which makes sense if (0, 0, 1) is towards the viewer...

So now I am confused... how should I handle this correctly??? Which convention did I use incorrectly? Which convention did I forget? It doesn't seem logical, for example, to invert the rotation of the ship when calculation the movement vectors...

Can someone clarify this for me? This has been bothering me for a week now and I don't seem to get it straight without doubting that I am making new errors.

Edit: isn't it at all very strange to invert the rotational part of the model's matrix for a view matrix? I understand that the translation part should be inverted, but the view should still look at the same direction as the object when it would be rendered, no?

TLDR; If you take legacy OpenGL, set a standard projection matrix and an identity modelview matrix and render a triangle at (0, 0, -5), you will see it because OpenGL looks at Z-.

But if you take the Z-axis from the view matrix (3rd row column), which is (0, 0, 1) on an identity matrix, this means that going 'forward' means that you will be getting further away from that triangle, which looks illogical.

What am I missing?

Edit: As the answer is hidden in many comments below, I summarize it here: conventions! I chose to use the OpenGL legacy convention but I also chose to use my own physics convention and they collide, so I need to compensate for that.

Edit: After much consideration, I have decided to abandon the OpenGL legacy convention and use whatever looks most logical to me, which is the left-handed system.

scippie
  • 2,011
  • 1
  • 26
  • 42
  • When the ship moves then the model matrix changes. The model matrix is applied before the view matrix. You've to calculate model matrix independent on the view onto the scene. – Rabbid76 Sep 15 '19 at 11:31
  • I know that. That is not my question. My question is why my rotation tells me it is pointing to Z+ when OpenGL renders it to Z- (and how to handle this correctly) – scippie Sep 15 '19 at 11:42
  • *"why my rotation tells me it is pointing to Z+"* - How does it tell that? Because the ship is moving in the wrong direction? This leads to my first comment *"The model matrix is applied before the view matrix"*. The transformation of a vertex hast be `clip_pos = projection * view * model * model_pos`. When the point of view changes, then you've to change `view`. When the position of the model changes, then you've to change `model`. If the "ship" moves in the wrong direction, the the new model transformation is somehow applied after the view. – Rabbid76 Sep 15 '19 at 12:01
  • You didn't read my post correctly (or I am completely misunderstanding you): my camera is actually always at (0, 0, 0) and my objects are relative to it. I actually only need the view matrix for rotation or camera displacement. The rotation is pointing Z+ because the math says so: the third row of my rotation matrix, calculated from a quaternion is [0 0 1], when identity, which is basic math. OpenGL legacy renders to Z- when given an identity rotation, so where does the sign go? – scippie Sep 15 '19 at 12:10
  • A (right) rotation by 90 degrees (of 2 dimensional vector) is calculated by `rotate90((x, y)) = (-x, y)`. If the vector (0, 1, 0) is rotated around the x axis, then the result is (0, -0, 1). If the vector (0, 0, -1) is rotated this leads to (0, -(-1), 0). So the 90° rotation of the matrix `[(1, 0, 0), (0, 0, -1), (0, 1, 0)]` around the x axis leads to `[(1, 0, 0), (0, 1, 0), (0, 0, 1)]` – Rabbid76 Sep 15 '19 at 12:20
  • I am really sorry @Rabbid76 but I really feel like you are not at all understanding my question. I know I have a view matrix. That's not the issue. The fact that I am using models and rotating them to get them in the same conventions is even not the issue. Just take triangles. If you take OpenGL legacy, set a default projection matrix and an identity view matrix. OpenGL will show me the triangle rendered at (0, 0, -5) while an identity view matrix has a Z-axis of (0 0 1), which means that going forward will get you further away from the triangle. That's my issue simply put... – scippie Sep 15 '19 at 12:38
  • You're right I don't understand your question, this may relay on a basic misunderstanding. But note, when you set up a projection matrix by `glOrtho` or `gluPerspective`, then the projection matrix inverts the z axis. It transforms from the right handed system to the left handed system of the normalized device space. When the projection matrix is the identity matrix, then this is missed. – Rabbid76 Sep 15 '19 at 12:46
  • Does this mean that I also need to invert the z axis in my view matrix to get it "right" in legacy OpenGL? Do 'kids these days' no longer invert the z-axis in their projection matrix? – scippie Sep 15 '19 at 12:53
  • When the modelview matrix and the projection matrix are the identity matrix, then you've to draw the scene in normalized device space. Normalized device space is a left handed system with the left bottom front of (-1, -1, -1) and right top back of (1, 1, 1). The z axis points "into" the view. You've to set a projection matrix where the z axis is inverted. You can concatenate the projection and the view matrix, this means to invert the z axis of the view matrix if the translation of the view is (0, 0, 0). – Rabbid76 Sep 15 '19 at 12:57
  • Ah-ha... I think that clarifies it. I will have to reread your sentence a couple of times and have it sink in I think... – scippie Sep 15 '19 at 13:04
  • I know, I'm not a native english speaker. It is hard to formulate such technical behavior, especially if it feels "unnatural" like that. – Rabbid76 Sep 15 '19 at 13:09
  • Me neither :-). I guess that if I want my conventions to stay the same as legacy OpenGL (a choice I made a long time ago and I don't want to rewrite all the math), I need to invert every rendering so that Z- is actually Z+ but nobody knows. I just flipped it and everything works perfectly. Going forward now means going forward, both graphically as mathematically and physically. I think what I am learning is that OpenGL has actually changed the more "normal" Z+ behaviour to Z- to have a RH coordinate system where the device space is actually LH. Maybe DirectX was right on this one? – scippie Sep 15 '19 at 14:02
  • 1
    May be, but that is probably a bit opinion based. It's just math. – Rabbid76 Sep 15 '19 at 14:05
  • True. Thanks for your clarifications. It is getting more and more clear now. I wanted to upvote your answer, accidentally removed the upvote and now I no longer can upvote it (because I already un-upvoted it). Sorry... Upvoting your previous answer then. – scippie Sep 15 '19 at 14:13
  • Oh, doesn't matter (I think you meant the comment). Anyway, I have no idea how to explain all that in a comprehensible answer. – Rabbid76 Sep 15 '19 at 14:18
  • IIRC the perspective projection matrix is what leads to view direction `Z-` (you can flip it if you want) anyway your problem is related to matrix multiplication order as you are comparing values at different stages of the matrix that mean in different coordinate systems... some values are global some local depending on the stage your matrix multiplication is. Take a look at this: [Understanding 4x4 homogenous transform matrices](https://stackoverflow.com/a/28084380/2521214) at the end are few links with camera and player control examples which might shine some light on the problem for you... – Spektre Sep 16 '19 at 09:16

1 Answers1

1

I think the root cause of your confusion might lie here

So, now I have a spaceship, the camera is inside, and it its rotation quaternion is identity. This gives an identity matrix and if I recall correctly, row 1-3 are representing the X, Y and Z axis of where the object is pointing at. To move the ship, I use this Z axis to go forward. With the identity matrix, the Z-axis will be (0, 0, 1).

Since we can assume that a view matrix contains only rotations and translations (no scaling/shear or perspective tricks), we know that the upper left 3x3 sub-matrix will be a rotation only, and those are orthogonal by definition, so the inverse(mat3(view)) will be the transpose(mat3(view)), which is where your rows are coming from. Since in a standard matrix which you use to transform objects in a fixed coordinate frame (as opposed to moving the coordinate frame of reference), the columns of the matrix will simply show where the unit vectors for x, y and z (as well as the origin (0,0,0,1) will be mapped to by this matrix. By taking the rows, you use the transpose, which, in this particular setup, is the inverse (not considering the last column containing the translation, of course).

The view matrix will transform from wolrd space into eye space. As a result, inverse(view) will transform from eye space back to world space.

So, inverse(view) * (1,0,0,0) will give you the camera's right vector in world space, inverse(view) * (0,1,0,0) the up vector, but as per convention the camera will be looking at -z in eye space, so forward direction in wolrd space will be inverse(view) * (0,0,-1,0), which, in your setup, is just the third row of the matrix negated.

(Camera position will be inverse(view) * (0,0,0,1) of course, but we have to do a bit more than just transposing to get the fourth column of inverse(view) right).

derhass
  • 43,833
  • 2
  • 57
  • 78
  • So if I take your answer and the comments on my post together, it is safe to assume that because OpenGL legacy changes the device space from being left-handed to a projection space being right-handed by negating the Z, so I need to re-negate the Z into everything else? (it looks like it solves my problem in my engine) – scippie Sep 15 '19 at 18:27
  • @scippie: No. The projection matrix has nothing to do with this. A classic GL projection matrix also adheres to the _lookat is `-z` in eye space_ convention, but none of that matters for your question.\ – derhass Sep 15 '19 at 18:36
  • Oh, but there is something wrong with my post: I keep talking about the rows, but that was merely to say that I know how you normally get the axes from a matrix (and it should be columns of course), but I am getting my axes directly from the quaternion, so it doesn't matter at all. I understand the part about the inverse and transpose. I still don't understand why I need to negate my z to get the forward direction of my object when that same rotation matrix (no negated z) renders the object in the correct direction when seen from the camera. – scippie Sep 15 '19 at 18:59
  • I updated my OP to correct the row/column confusion. Sorry about that. – scippie Sep 15 '19 at 19:02
  • 1
    @scippie: I really don't know where your confusion lies now. " still don't understand why I need to negate my z to get the forward direction of my object when that same rotation matrix (no negated z) renders the object in the correct direction" That's just because you imagine that for your oboject, `+z` is towards the front, while for the camera, `-z` is towards the front. Both are just conventions you chose more or less arbitrarily. – derhass Sep 15 '19 at 19:29
  • Wow... is it that simple? I just chose two different conventions?? Damn it :-) I now feel that I have chosen one bad convention. That, of course, explains everything. Thank you! – scippie Sep 16 '19 at 06:49