PDA

View Full Version : How do I correctly move a camera towards the direction its looking at?



hashbrown
10-07-2016, 10:40 AM
I'm currently getting the forward vector from the view matrix (row major)...



Vec3 fwd(GetView().mat[0][2], GetView().mat[1][2], GetView().mat[2][2]);
fwd.Normalize();


I then calculate a new postion using this fwd vector, current Position, speed, and dt:



// Move Forward
m_pos += (fwd * speed * dt);


This works fine if I change fwd.y/z to negative and if I'm moving down the +Z, but if I turn the camera around 180deg, the camera dir is inverted. In fact, if I leave the fwd vector the way it is, instead of moving forward, I end up moving back, everything is inverted. Which is why I make fwd.y/z negative.

Is there a way I can calculate the new position of my camera correctly regardless if the cam is looking down the positive or negative Z? I don't think changing fwd.y/z to negative is a solution, at some point the camera is going to turn around.

Just in case you want to see how I calculate the rotation:


Quat pitch(Vec3(1,0,0), Trig::DegToRad(mouseY));
Quat yaw(Vec3(0,1,0), Trig::DegToRad(mouseX));

Quat newRot = pitch * m_transform.GetRotationQ() * yaw;

m_transform.SetRotq(newRot);
m_transform.GetRotq()->Normalize();

john_connor
10-07-2016, 11:21 AM
I'm currently getting the forward vector from the view matrix (row major)...

you have to keep in mind that OpenGL expects (by default) matrices in column-major order



This works fine if I change fwd.y/z to negative and if I'm moving down the +Z, but if I turn the camera around 180deg, the camera dir is inverted. In fact, if I leave the fwd vector the way it is, instead of moving forward, I end up moving back, everything is inverted. Which is why I make fwd.y/z negative.

http://www.learnopengl.com/#!Getting-started/Camera

take a look at the camera's "forward" direction and the direction it "looks at":
-- forward points backwards

thats only the case for the "View Matrix"
"Model Matrices" or "Transformation Matrices" dont have a backward-pointing "forward" direction


litte advice:
you dont have to re-invent the wheel, use GLM ;)
http://glm.g-truc.net/0.9.8/index.html

hashbrown
10-07-2016, 11:31 AM
you have to keep in mind that OpenGL expects (by default) matrices in column-major order




http://www.learnopengl.com/#!Getting-started/Camera

take a look at the camera's "forward" direction and the direction it "looks at":
-- forward points backwards

thats only the case for the "View Matrix"
"Model Matrices" or "Transformation Matrices" dont have a backward-pointing "forward" direction


litte advice:
you dont have to re-invent the wheel, use GLM ;)
http://glm.g-truc.net/0.9.8/index.html

Thanks John. I've used GLM before, but I'm in the mood of learning all this, just a personal project. :) I'm also letting OpenGL know I'm using row-major by passing GL_TRUE in the 3rd agument of glUniformMatrix4fv.

So now I know the forward vector of a view matrix always points backwards. Thanks for that.

Question

Why does the orientation of my camera change when I turn around though? I'm using the forward vector as I explained above, but depending if I look forward or backwards, the Y Axis changes. If I'm moving +z and pitch my camera upwards, the camera travels downward, if I'm moving -z, and pitch my cam upwards again, it moves upward. Apparently it only affects the Y Axis.

hashbrown
10-07-2016, 08:32 PM
I kind of fixed it. If I zero out the y in my forward vector (which I will) the problem goes away and I got a FPS cam, but what if I wanted a fly cam?

I noticed my Y was inverted every time I moved my camera in the positive Z direction. So I did this:



if (m_fwd.z >= 0 ) { m_fwd.y = -m_fwd.y; }


Now, no matter where I look, my Y is not inverted.

Bug

1) I zero out m_fwd.y in order to let gravity handle the player's position.y...but if I look down while walking forward, the player slows down. This only happens when I look completely down.

GClements
10-07-2016, 10:58 PM
I'm currently getting the forward vector from the view matrix (row major).
You probably need to get the forward vector from the inverse of the view matrix (which will normally be the same as its transpose).

The view matrix normally transforms world space to eye space, i.e. its columns are eye-space vectors corresponding to the world-space axes.

For motion, it's typically easier to maintain a camera matrix (i.e. one that transforms from eye space to world space), use that for motion, and just use the inverse of the camera matrix as the view matrix.

BBeck1
10-09-2016, 07:08 AM
Adding to what GClements said, the View matrix is basically the same as a World Matrix except it's for the camera, and maybe more importantly: it's inverted. I cover this in my Matrices video (https://www.youtube.com/watch?v=T7sb4yKKzFg) at about 46:15 into it.

The way 3D graphics works is all math. So, there is no such thing as a "camera". If you understand how the Projection matrix works, it merely projects what's down the primary axis (Z?) onto a 2D surface so that it can be drawn on a 2D monitor. That's cool, but it doesn't allow you, or the camera, to move through the 3D world. The world matrix allows you to place things in the 3D world. As long as they happen to be in front of the "camera"/projection they can be seen.

That's where the view matrix comes in. It moves the entire 3D world to simulate a camera. Except in order to do this, it has to do everything backwards. If it moves the world 3 units forward, it will have the effect of moving the 3D world 3 units backwards visually. If it moves the 3D world 7 units left, it will have the effect of making the viewer think the camera has moved 7 units right. If it rotates the entire world to the right, the viewer will see that as the camera rotating left. Everything must be done backwards from the viewer's perspective because there is no camera that can move, there is only the ability to move the entire game universe around the projection point to make you think the "camera" is moving through the scene.

The bottom line is that everything done to the view matrix has to be backwards.

I actually got caught up in this just last week and I had to laugh at myself for forgetting when I realized what was happening. I was working on something and told the camera/view-matrix to "go up" and it went down. I fed a translation matrix an up vector and then multiplied to combine it with the view matrix and the result was that it looked as if the camera were moving downward. The funny thing is that I understand how this works and it still caught me off guard. It was about 10 minutes later when I suddenly realized why it does that. You can feed it the opposite data to do the job, or you can do it the way you expect and then invert the resulting matrix. So, if I had of fed it an up vector wanting to move the camera up, then inverted that matrix, and then combined it with the view matrix through multiplication, the result would have been the camera moving up. Instead, I just negated the up vector and turned it into a down vector, which is fine as long as you understand why that's what you have to do.

I believe a matrix inversion is just a scale matrix where you scale on the X, Y, and Z axes by -1. That's going to reverse the direction of everything.

Anyway, the forward vector out of your view matrix probably is "camera forward", but to alter it you need to do the opposite of what you want or build a matrix to change it and then invert that matrix.

Also, the view matrix - just like an object's world matrix - is subject to order of multiplication problems. One thing that annoys me about GLM is that I can't seem to just build a translation or rotation matrix; it want's to rotate it for me even though it doesn't know how I want the rotation carried out.

With matrices:

A = B*C

is not at all the same as

A=C*B

With rotations one will rotate around the local axis, and the other will rotate around the global axis. Or to restate that more clearly, one will rotate the object and the other will cause it to orbit. Honestly, I don't know which is which; if it does the opposite of what I want, I reverse it and then it works. But if you are using some sort of "rotation" function that does the math for you, you have no control over what order it multiplies things in. As far as I can tell, GLM doesn't allow you to do the math yourself by building a rotation matrix, which is why I'm considering writing my own math library. None the less, I got around this fairly simply like so:



Triangle.Transform(glm::rotate(glm::mat4(), glm::radians<float>(1), glm::vec3(0.0f, 1.0f, 0.0f)));


In this example, it's object oriented and I'm calling the game object's Transform method. That looks like this:



void HandCodedObjectClass::Transform(glm::mat4 TransformationMatrix)
{
WorldMatrix *= TransformationMatrix;
}


So, all it's doing is multiplying a rotation matrix by the object's world matrix. The "*=" dictates the order of multiplication, so be careful with that. Sometimes you have to write it out so that you can set the order of multiplication. This would be a rotation rather than an orbit because everything is being done relative to the local axis (note that quaternion multiplication is opposite of this).

But back to the original code, notice that I'm feeding it "glm::mat4()". This is because I don't want it to rotate my matrix for me. In this particular case, it would probably be more efficient because I've fixed the order here to the same thing it would have used. But there might be a case where I want it to orbit instead of rotate (then I need a different method than my Transform method here). I probably should just get rid of that Transform method; I may do that. It's one line of code. Anyway, I'm building a rotation matrix by feeding it an empty (Identity) matrix as the input and then specifying my rotation. That's a way around the fact that I can't seem to find a way to get GLM to just give me a rotation matrix. In this case, I'm forcing it to go through extra un-needed steps in order to give me a rotation matrix rather than having it carry out the rotation for me where I can't control it.

But if you're in this situation where you have a rotation function where it chooses for you how the order of multiplication is carried out and you want to rotate instead of orbit, an old trick is to translate the object to the origin while keeping track of it's original position. Then you rotate the object at the origin. And finally you translate it back to the original position rotated.

So, all of this is for an object's world matrix. With a view matrix, remember that anything you do to change it must be inverted. In fact, I would have to test this, but getting values out of a view matrix might require the view matrix to be inverted.

Sure enough, when I look at my code, I have:



ConversionMatrix = glm::inverse(View);
CameraPos = (glm::vec3) ConversionMatrix[3]; //Get the camera position from the view matrix.


So, I'm inverting the view matrix before I pull the position out of it. And I would need to invert values going back into it. (I believe ConversionMatrix[0 to 2] are the forward, right, and up vectors for the camera [x,y,z]).

I don't memorize a lot of this stuff; when I need it I usually try it one way and if it doesn't work I switch it. But that's another reason why I sometimes get confused about what it's doing and catch myself doing it wrong. The important thing is understanding why it's doing it the way it does and then 10 minutes after the fact you'll suddenly realize why your code's not working.

Edit:I went to go delete that GameObject's Transformation method and realized why I'm doing it that way. It's the object oriented idea that the object's world matrix belongs to the object and should not be accessed directly from outside the object. If I'm going to do it this way, I should probably create a rotate, orbit, and translate method that eliminate the need to even build a rotation or translation matrix and obfuscates the whole thing. Or, I could expose the object's world matrix publicly and operate on it directly, which may break the principles of OOP, but actually makes a fair amount of sense since the ability to read that matrix and write to it is needed. In C#, we would have used properties (getter's and setter's) for this sort of thing to have the best of both worlds there. Not sure which way I'll go with it, but I should probably commit to going one way or the other rather than being half way in between like it is now; I should probably either expose the matrix or get rid of the Transform matrix and create Rotate, Orbit, and Translate methods the completely obfuscate the math.

BBeck1
10-09-2016, 07:59 AM
I'm currently getting the forward vector from the view matrix (row major)...


Just in case you want to see how I calculate the rotation:


Quat pitch(Vec3(1,0,0), Trig::DegToRad(mouseY));
Quat yaw(Vec3(0,1,0), Trig::DegToRad(mouseX));

Quat newRot = pitch * m_transform.GetRotationQ() * yaw;

m_transform.SetRotq(newRot);
m_transform.GetRotq()->Normalize();


See my Gimbal Lock video (https://www.youtube.com/watch?v=4ebJXGlUDbA) as to why this will never work and mathematically is designed to give you the problems that it is giving you. This code is designed to crate quaternion gimbal lock. The video explains why and how to get out of it. (The short version is that you can't store pitch, yaw, and roll values if you want your code to actually work and translating them to quaternions does nothing to change that. You need to either store the orientation as a matrix or as a quaternion, but as long as you store it as 3 separate values, you will be fighting gimbal lock forever. The reason that matrices or quaternions solves this problem is because they are one value storing the orientation, not 3 separate values, thus changing one changes the other two without you doing anything. It's the fact that all the orientation information is stored as a single value (quaternion or matrix) that avoids gimbal lock. Break it up into 3 values and you will always have gimbal lock.)


This line of code is the one that creates quaternion gimbal lock (rumored to not be a thing, but here it is in the flesh):


Quat newRot = pitch * m_transform.GetRotationQ() * yaw;


If you want a quick "fix" on this, you can control the axis where the gimbals actually lock by changing the order of multiplication here which may allow you to get the locking plane on a plane you don't use. For moving parallel to the ground this may actually work. But when you go free-form like an airplane, it's going to be a disaster no matter which plane you move the problem to.

(This is actually a very good lesson for you because this happens to almost everyone when they first start doing this stuff because Pitch, Yaw, and Roll seem to be so intuitive. They're not actually when you get into it mathematically and they immediately cause problems. But understanding this problem and how to solve it is really important because you'll see people struggling with it almost every time someone starts out in 3D graphics programming.)

I might also add that I favor matrices over quaternions. If you can prove the quaternion solves a problem more efficiently, I'm all for that. But the shaders usually want to be fed matrices, and if you're already using matrices, it's going to decrease efficiency to constantly be translating back and forth between matrices and quaternions - not to mention make it more confusing. So far, the only place where I know that quaternions are a better choice than matrices is for SLERP (I learn something new all the time and can't guarantee my opinion won't change over time). If you aren't SLERP'ing, you probably shouldn't be using quaternions. Matrices prevent gimbal lock just as well as quaternions and so that's not a valid reason to use quaternions. I tend to think that matrices are a better choice until you need to SLERP (SLERP mostly comes up in skinned animation when you are animating things; in that case you are interpolating rotions in time and need a rotation that is maybe 43% between two rotations. Matrices are pretty terrible at interpolation.).

By the way hashbrown, it's good to see you sticking to this. I'm glad to see someone learning OpenGL and so dedicated to it. All of this stuff will seem confusing at first, but eventually it all starts making sense when you stick to it.

hashbrown
10-11-2016, 07:20 PM
You probably need to get the forward vector from the inverse of the view matrix (which will normally be the same as its transpose).

The view matrix normally transforms world space to eye space, i.e. its columns are eye-space vectors corresponding to the world-space axes.

For motion, it's typically easier to maintain a camera matrix (i.e. one that transforms from eye space to world space), use that for motion, and just use the inverse of the camera matrix as the view matrix.

Thanks! That pointed me toward the right direction. I wasn't thinking this correctly.


See my Gimbal Lock video (https://www.youtube.com/watch?v=4ebJXGlUDbA) as to why this will never work and mathematically is designed to give you the problems that it is giving you.

I was reading about Gimbal Lock on the bus back home and now I read this message :) Going to watch your video right now (also subbed just in case you upload some more).



By the way hashbrown, it's good to see you sticking to this. I'm glad to see someone learning OpenGL and so dedicated to it. All of this stuff will seem confusing at first, but eventually it all starts making sense when you stick to it.

Thanks! I'm glad to read somebody say that in a time where I'd be "re-inventing the wheel". I was a little intimidated at first, but now that I'm walking around a little 3d world (gimbal lock and all), it feels very good. I wish I can do this for a lot more hours a day though, work can really chop off development time, but I slip 40 minutes in during lunch. ;)

I'll drop a like once I finish watching your video. Cheers.

BBeck1
10-11-2016, 08:21 PM
When I was starting out, there wasn't anyone around to help and I spent a decade struggling to even begin to figure any of this out. So, one of my goals since I first started figuring it out was to do everything I can to help everyone else out who's trying to learn it. I'm still learning myself, but if there's anything I have already learned that I can share, I want to help anyone I can learn it.

I hope to get some more videos posted and I'm planning on turning the focus strictly to OpenGL and maybe a little Unity here and there. I was doing XNA and then DirectX when I made the videos. All the math is the same and the HLSL stuff is mostly math. I need to make a video comparing HLSL with GLSL. The shader on my website in the OpenGL section is a GLSL version of the shader at the end of my HLSL videos. I tried to keep the variables names the same for comparison.

I'm disappointed that I haven't published a video in about a year. There's just been so much going on. I decided to switch to DX11 after XNA and had to start all over with the learning process. Then I decided I wanted to do OGL instead of DX and had to learn it all over again. Now I'm leaning towards Vulkan. And this year I got enrolled in a 3D art program that's supposed to prepare you to do game art professionally although I'm just doing it for myself and have no intention of doing it professionally. I just found it really hard to find good models at a price I could afford. Even when good models were available they often were not the models I wanted. Seems like you kind of have to let the models dictate what game you make if you're going to use models you buy. So, anyway, getting my homework assignments in for class is keeping me so busy I don't have time to work much on my programming projects or the videos or website. But hopefully after the two years is up I'll be able to produce my own 3D game art. In fact, by the end of this semester it looks like I'm going to be up to a level of making simple AAA game art. We're already getting into Physical Based Rendering and last semester took me further than I imagined it could.

Anyway, it's all very intimidating at first. The more successes you have the more you figure out you can do it and that there's no part of it you can't learn as long as you stay after it. And it's very rewarding when it starts coming to life.

hashbrown
10-15-2016, 11:10 AM
When I was starting out, there wasn't anyone around to help and I spent a decade struggling to even begin to figure any of this out. So, one of my goals since I first started figuring it out was to do everything I can to help everyone else out who's trying to learn it. I'm still learning myself, but if there's anything I have already learned that I can share, I want to help anyone I can learn it.

I hope to get some more videos posted and I'm planning on turning the focus strictly to OpenGL and maybe a little Unity here and there. I was doing XNA and then DirectX when I made the videos. All the math is the same and the HLSL stuff is mostly math. I need to make a video comparing HLSL with GLSL. The shader on my website in the OpenGL section is a GLSL version of the shader at the end of my HLSL videos. I tried to keep the variables names the same for comparison.

I'm disappointed that I haven't published a video in about a year. There's just been so much going on. I decided to switch to DX11 after XNA and had to start all over with the learning process. Then I decided I wanted to do OGL instead of DX and had to learn it all over again. Now I'm leaning towards Vulkan. And this year I got enrolled in a 3D art program that's supposed to prepare you to do game art professionally although I'm just doing it for myself and have no intention of doing it professionally. I just found it really hard to find good models at a price I could afford. Even when good models were available they often were not the models I wanted. Seems like you kind of have to let the models dictate what game you make if you're going to use models you buy. So, anyway, getting my homework assignments in for class is keeping me so busy I don't have time to work much on my programming projects or the videos or website. But hopefully after the two years is up I'll be able to produce my own 3D game art. In fact, by the end of this semester it looks like I'm going to be up to a level of making simple AAA game art. We're already getting into Physical Based Rendering and last semester took me further than I imagined it could.

Anyway, it's all very intimidating at first. The more successes you have the more you figure out you can do it and that there's no part of it you can't learn as long as you stay after it. And it's very rewarding when it starts coming to life.

I've never tried DX11, but I've read the API is very nice. At some point I'd prefer giving vulkan a chance though, after I'm done with ogl. That's good you want to learn how to make your own models. I'm not a bad modeler, but I'm still learning how to make humans, I can do anything else but humans. I use fuse (https://www.mixamo.com/fuse) for now, check it out..not bad for quick prototyping, at least until you finish your own models.

I saw your video on Matrices and dropped a like and comment. It helped out a lot. I'm going to watch the Gimbal Lock video tonight. You should upload some more videos, as much as I like 10 - 15 min tutorial videos, you definitely can't explain as much in such short time, so 55 mins was good. Keep it up, I know these videos can take time.

Enjoy your 3d art program by the way :)

BBeck1
10-16-2016, 01:53 PM
Humans are tough. I've made a couple following tutorials. The Professor Zombie model at the end of my intro video on my YouTube channel (like the HLSL video series) is one that I did following the tutorial in Chris Totten's book (https://www.amazon.com/Game-Character-Creation-Blender-Unity-ebook/dp/B0089NVPI6/ref=sr_1_3?ie=UTF8&qid=1476646995&sr=8-3&keywords=characters+blender). It's still pretty intimidating to me even after making a couple human models. All the shapes are so organic. It requires a detailed knowledge of anatomy to do it up to today's standards. And everyone has a somewhat innate knowledge of what humans "look" like and so any small mistake is easily spotted by everyone.

Today, one of the things that's "eating my lunch" is that we're supposed to pick a character out of silhouettes we've done and paint them. Great, expect I don't know how to draw or paint to save my life. Over the past month or two I've learned a whole lot by taking a 4 week figure drawing class (https://www.pencilkings.com/figuredrawingchallenge/) and starting to read a couple books like Andrew Loomis's figure drawing book.

The class assignment today is basically to generate a character concept and paint it. Knowing that they make assignments like this and that I can't draw a stick figure, is why I took that class and started reading these books. I'd love to be able to do it, but it seems like I need to take a multi-year art program to learn to draw and paint.

I guess I need to stop procrastinating and get out the iPad and just do it. The assignment's an extremely small part of the overall grade and they aren't expecting Da Vinci (http://www.leonardodavinci.net/).

Thanks for the like and comment! It's greatly appreciated! I'm glad it helped. It makes it worth while when I know it's helping people. I do hope to make a lot more videos and focus more on OGL and GLSL with them in the future. Maybe some Vulkan (if I can ever learn that) and Virtual Reality stuff in the future too (have to build a computer capable of handling VR first though).