Jump to content

  • Log In with Google      Sign In   
  • Create Account


glm/opengl orbital camera C++

  • You cannot reply to this topic
10 replies to this topic

#1 mynameisnafe   Members   -  Reputation: 252

Like
1Likes
Like

Posted 02 August 2014 - 11:46 AM

Hi there,

 

I have a gl project with a first person camera that uses glm to do it's math.

My question is thus and I'm sorry for it's quite poor construction: 

Do I need to use/do glm::quat things or do I need rotX, rotY, rotZ, for some code where the camera spins around on a sphere centred on the model?

So, the end goal is that moving the mouse makes it look like the model is spinning, when actually the camera is spinning?

And how would I do that?

For example, how different would this class declaration be?

I'd like to throw a 'orbit-cam' in there, then extract a base class, etc.

class GLCamera
{
protected:


float   _mx, _my;
float   m_frameTime, // how much time has passed
m_moveSpeed, // how many units to move per second
m_mouseSpeed; // how many degrees to rotate per second of


glm::vec3 m_real_up;
glm::vec3 m_origin;


float m_horizontalAngle, m_verticalAngle, m_initFoV;


glm::vec3 position;


glm::vec3 m_direction;
glm::vec3 m_right;
glm::vec3 m_up;


glm::mat4 m_viewMatrix;


Sponsor:

#2 DemonDar   Members   -  Reputation: 143

Like
0Likes
Like

Posted 02 August 2014 - 02:09 PM

You can do that in zillion ways. You need to make a point floating around your model at desired hieght (if your Up vector is Y, then use X = cos(time) and Z = sin(time) to have a circle flying camera). then you need just to build a lookAt matrix camera (position is the point and lookAt is the model feet).

To build the matrix search around the web but I think GLM already have those functionalities



#3 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 02 August 2014 - 03:59 PM

Hi, thanks for replying.

 

Y is my real up, and my existing camera is using glm::lookAt(position, position + direction, up)

 

This is a cool idea, I'll try this tomorrow:


use X = cos(time) and Z = sin(time)


 

I guess orbit is the wrong word, my bad; what I'm looking for is a kind of 3D editor camera - so the models at 0,0,0 but I can 'spin it around on it's origin'.

 

So what I need is glm::lookAt(position, origin, up), and I use the mouse to update something like this?


dx = mouse.dx; // change in
dy = mouse.dy;

//rotate on y axis
rotY += dx;
position = (cos(rotY), 0, sin(rotY));

//rotate on x axis ?? 
rotX += dy;
// ? // position.x += cos(rotX);
// ? // position.y = sin(rotX)
// ? // position.z += cos(rotX);


#4 Sponji   Members   -  Reputation: 1216

Like
0Likes
Like

Posted 02 August 2014 - 04:31 PM

I would probably do something like this:

// Get the rotation matrix from rotY and rotX
glm::mat4 rotation = glm::rotate(glm::mat4(), rotY, glm::vec3(0,1,0)) * glm::rotate(glm::mat4(), rotX, glm::vec3(1,0,0));

// origin * rotation * distance_offset
glm::mat4 final_transform = glm::translate(glm::mat4(), origin) * rotation * glm::translate(glm::mat4(), glm::vec3(0,0,distance));

// And the view matrix is the inverse of whatever's transform
glm::mat4 view = glm::inverse(final_transform);

Not as fast as doing the math directly by yourself, but should work nicely.


Derp

#5 DemonDar   Members   -  Reputation: 143

Like
0Likes
Like

Posted 03 August 2014 - 04:37 AM

Depends if you need to orbit camera only left right or also up/down. You need to move camera position around a circle or on the surface of a sphere then.

 

You could use a quaternion (rotation around 1 axis) but the faster way is to compute directly the coordinate(and it is also simple)

 

Given the camera startin position is on XY plane (Z=0)

 

first rotate around Z axis for vertical scrolling.

currentY+=mouseY*speed; //vertical angle

if(currentY >89.f)
    currentY = 89.f;

if(currentY<-89.f)
    currentY = -89.f;


camPos.x = range*cos(currentY*DEG_TO_RAD);
camPos.y = range*sin(currentY*DEG_TO_RAD);
camPos.z = 0;

camPos = glm::rotate(camPos, rotX ,glm::vec3(0,1,0)); //rotate around vertical axis now

Edited by DemonDar, 03 August 2014 - 04:38 AM.


#6 steven katic   Members   -  Reputation: 272

Like
3Likes
Like

Posted 03 August 2014 - 05:32 AM

RE: Do I need to use/do glm::quat things or do I need rotX, rotY, rotZ, for some code where the camera spins around on a sphere centred on the model?

You can use either strategy (i.e. quaternions or euler angles ala degrees). Some people argue about the pros and cons of each too (there is always arguing going on somewhere [mostly?] in the world so we'll leave that alone).

 

I just so happen to have been playing around with an FPS and Orbit cameras recently, so this might help: It's one in a zillion! ..I mean It's one of those zillion.

glm can be a great tool. You can save yourself a smidge of code by calling something like:

glm::mat4 R = glm::yawPitchRoll(yawf,pitchf,0.0f);

to rotate using quaternions euler angles as input.

 

Then you could do the following to Update() a camera transformation:

glm::vec3 T = glm::vec3(0,0,dist);
T = glm::vec3(R*glm::vec4(T,0.0f));
*position = *target + T;
*look = glm::normalize(*target-*position);
*up = glm::vec3(R*glm::vec4(*UP,0.0f));
*right = glm::cross(*look,*up);
*V = glm::lookAt(*position, *target, *up);

That was easy! A Spam of code!
Now we just have to translate that into english. anyone?

 

But lets back up a bit. As far as I can tell you are describing an orbit camera! Let me try and paraphrase your stated goal:

The orbit camera works the opposite way to FPS, ...yeah?. Rather than the position of the camera

staying fixed, the target remains fixed, while the camera moves or rotates around the target.

How does that sound? That's an orbit camera. An obviously good analogy so let's use it?

Since you don't seem very sure about it, I suggest we just stick with that analogy. Sound OK?

 

Then think about this:

 

Say we have a target position (the centre of a model..say)
We get the distance(dist) between the target position(target) and the camera position(position) and

turn it into a vector:

glm::vec3 T = glm::vec3(0,0,dist); // turn it into a vector

Then translate this vector by the current rotation matrix:

T = glm::vec3(R*glm::vec4(T,0.0f));

Then adding this translation vector to the target position gives us the new camera position:

*position = *target + T;

That's it.. well almost. So far, we have the new position for the camera to moved to. All we do next is update the camera look, up and right vectors(re-orientate the camera) and view matrix or to be really dry we can say "Recalculate the orthonormal basis and then the view matrix" and its done (ready for the next frame).

 

The problem with giving code examples is that sometimes there are diminishing returns from explanations of them.. or maybe not... if you can figure it out yourself ... like a puzzle. Trying to figure out what a piece of code does is all part of programming (and lets me off the hook for being lazy!).

 

You might ask some interesting questions like " What is T or  V or UP"? No,.. just kidding!

They are obvious questions, with hopefully obvious answers.

 

Something a little more interesting you might ask is:

 

* How do we get the distance(dist) between the target position(target) and the camera position(position) ?

//First set the position then the target and use glm::distance():
 void SetPosition(const glm::vec3& p) 
{  *position = p; }
 void SetTarget(const glm::vec3& tgt) 
{  *target = tgt;
   distance = glm::distance(*position, *target); 
}

glm::distance() is like most bits of code: you can do internals of them yourself instead of using glm if you wish.

 

So, most of this (I think) comes from The targetcamera section of an Opengl Cookbook: http://www.packtpub.com/opengl-development-cookbook/book

Nothing particularly new in there, Mainly fairly neat code examples using glm that is good to play with. Not sure if I remember it being very "beginner friendly" though. But it does spell out the full code per class that is very easy to modify and to drop into something suitable. Among other things it contains an (FPS) free camera and (orbit/turntable/arcball?) target camera both derived from a base camera class: just what you seem to be starting on, it seems, from your post.

 

*Another question: What if you wanted to use good ol' fashion degrees (euler angles) ? (that, I assume is your mention of rotX, rotY, rotZ?).
Just replace the quaternion form with the euler form at this line:

An alternative to using glm::yawPitchRoll() is to do it ourselves using glm::rotate():

// correction: this is not quaternions. This is a euler angles version also..sorry 
//glm::mat4 R = glm::yawPitchRoll(yawf,pitchf,0.0f);
// produce equivalent effect using glm::rotate()
float xr = pitchDegrees; 
float yr = yawDegrees;
glm::mat4 ViewTranslate = glm::mat4(1.0f); 
glm::mat4 RY = glm::rotate(ViewTranslate,yr,glm::vec3(0.0f,1.0f,0.0f));//yaw 
glm::mat4 RX = glm::rotate(RY,xr,glm::vec3(1.0f,0.0f,0.0f));//pitch   
glm::mat4 R = glm::rotate(RX,0.0f,glm::vec3(0.0f,0.0f,1.0f));//no roll, currently redundant but exists anyway

Both methods depend on how you get your input angles with something like camera->Rotate() method like so:

Rotate(const float yaw, const float pitch, const float roll)
{ 
	yawf=glm::radians(y); 
	pitchf=glm::radians(p); 
	rollf=glm::radians(r); 
	// and/or you can store angles in degrees.        
	yawDegrees = yaw; 
	pitchDegrees = pitch; 
	rollDegrees = roll;
	Update();
}

What is passed to Rotate can be the mouse Delta positions i.e.The position relative to its last position (or some filtered value thereof). 

 

Getting you hands on the Cookbook may help.  A source code download comes with this bit of leisurely reading. With this target camera you have the added bonus of setting the target to any position you desire (a staple of any good 3D model editor I imagine). Hence the authors' emphasis on this orbit camera being a target camera.

Cameras are fun.

The hard part might be figuring out how to build this into your achitecture? 


Edited by steven katic, 05 August 2014 - 02:23 PM.


#7 mrMatrix   Members   -  Reputation: 154

Like
0Likes
Like

Posted 06 August 2014 - 12:56 PM

I have decent target / orbit cam following this thread but how do I now use the View code onto the camera object itself? I have an obj file that is modelled to be a camera and from above is basically my ViewMatrix

glm::vec3 T = glm::vec3(0,0,dist);
T = glm::vec3(R*glm::vec4(T,0.0f));
*position = *target + T;
*look = glm::normalize(*target-*position);
*up = glm::vec3(R*glm::vec4(*UP,0.0f));
*right = glm::cross(*look,*up);
*V = glm::lookAt(*position, *target, *up);


#8 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 13 August 2014 - 11:19 AM

Okay, I broke my collarbone, apologies for the lack of input.

I shall have a read then edit this post (..surface of a sphere please)..

Steven Katic right on! That analogy is exactly what was in my head. Would you believe I'm a graphics programmer by day as well? Hahaha terrible!

Indeed a target is just a translation of the origin; I haven't got a mesh hierarchy or the right matrix from assimp yet, so just one matrix/position to spin the camera around at the moment.

I got my FPS camera from opengl-tutorials.com. also I bought the blue book instead of the red one and I'm regretting it now.

[keeps reading]

#9 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 13 August 2014 - 11:42 AM

PS I <3 GLM.

#10 mynameisnafe   Members   -  Reputation: 252

Like
0Likes
Like

Posted 17 August 2014 - 09:09 AM

Okay! It kind of works!

 

I'm now suffering from a kind of 'bad handling', where i get some interference and then a big camera jump.. it's not exactly smooth..

 

Here's my messy work in progress :

 

Get mouse position:

case WM_MOUSEMOVE:	
	{
		POINT p;
		GetCursorPos(&p);

		// move into rect/window space
		p.x -= rWindow.left;
		p.y -= rWindow.top;

		// relative to rect/window centre
		//p.x -= (rWindow.right - rWindow.left)/2;
		//p.y -= (rWindow.bottom-rWindow.top)/2;
		OnMouseMove(MouseMove(p));
         }

Then update the camera:

	if( pMouse->bRight == true )
	{
		POINT mPos = pMouse->pos; // pixels
		mPos.x -= pMouse->prev.x;
		mPos.y -= pMouse->prev.y;

		if(mPos.x != 0 || mPos.y != 0)
		{
			float hMag = _radians(millisElapsed * m_mouseSpeed * mPos.x);
			float vMag = _radians(millisElapsed * m_mouseSpeed * mPos.y);

			if( fabs(hMag) > _pi || fabs(vMag) > _pi)
				return;

			m_horizontalAngle -= hMag;
			m_verticalAngle -= vMag;
			
		}
	}

	//to rotate using quaternions euler angles as input.
	glm::mat4 R = glm::yawPitchRoll(m_horizontalAngle, m_verticalAngle, 0.0f);		
 
	//Then you could do the following to Update() a camera transformation:
	glm::vec3 T = glm::vec3(0, 0, -dist);
	position = glm::vec3(R * glm::vec4(T, 0.0f));

	m_direction = origin;// glm::normalize(position);
	m_up = glm::vec3(R * glm::vec4(m_real_up, 0.0f));
	m_right = glm::cross(m_direction, m_up);

	m_viewMatrix = glm::lookAt(position, m_direction, m_up);

Any suggestions?


Edited by mynameisnafe, 17 August 2014 - 09:17 AM.


#11 steven katic   Members   -  Reputation: 272

Like
1Likes
Like

Posted 18 August 2014 - 09:43 PM

Hi nate?(oops) mynameisnafe,

I see you've worked out the correct angles of rotation input to the camera rotate method: It is the total angle since its creation, and not

the delta angle, as I incorrectly suggested in my first post.

I do not understand what your code is doing. It can get tricky fine tuning the operation of a camera so it's a good idea to come up with a plan.

So, for example, I think about the following:

Decouple the input angles from the input source. Looks like you have done that with the m_horizontalAngle and m_verticalAngle variables. So that's good.

Next, How do we update them? This is not clear to me from the code you posted.
Here is a typical usecase/scenario I would expect to see for the user rotating the camera with the mouse:

Operation: The user presses and holds down a designated mouse button, then drags the mouse to move the camera view around.

The recipe:
 

// two new variables to add to the current camera rotation angles.
//(Perhaps a little overengineering going on here. But it does indicate our explicit intent for now).
int m_pitchDelta;// = 0; member vars: init somewhere else or make static?
int m_yawDelta;// = 0;

On mouse button down (say typically the left button): Intialise the camera system input parameters for the next rotation.

I see this line "if( pMouse->bRight == true )" in your code and assume it means "if the right mouse button is down"?.
 

// on right button down set the start position of the new transformation
// values that will modify the current values (in m_horizontalAngle and m_verticalAngle variables) 
if( pMouse->bRight == true) 
{  
	POINT p;  
	GetCursorPos(&p);    
	m_pitchDelta = p.x;  
 	m_yawDelta = p.y;  
}

Note: That's all you need at this stage. I would keep it simple until it works, then add all your other bells & whistles (i.e. stuff related to the angle restrictions

and filtered/timing speeds) they just get in the way for me at the moment.

So, ideally at this moment in the process the Modelview Matrix *V has already been initialized and typically the full MVP has also already been constructed and now all

that is happening each frame is The MVP is passed to the shader, the shader is activated and scene drawn.

Oh,...And you are holding that right mouse button down....(ready to move the mouse).

Now the only change we are interested in is when the mouse moves (with that right button down, basically dragging the camera view around).
We want the Camera to move around relative to the mouse pointer position as it moves.

I cannot quite see this happening in your code. But you can put the camera update code in the WM_MOUSEMOVE case section to make this intention explicit:
 

case WM_MOUSEMOVE:  {  
 POINT p;  
 GetCursorPos(&p);     
// get the amount of the last mouse movement  
m_pitchDelta -= p.x; 
m_yawDelta -= p.y; 
// modify the current angles   
m_horizontalAngle -= m_yawDelta; 
m_verticalAngle -= m_pitchDelta;     
// ?please explain? I wouldn't modify the source of our input(yet, if ever?)  
// move into rect/window space   
//p.x -= rWindow.left; 
//p.y -= rWindow.top;   
// relative to rect/window centre 
//p.x -= (rWindow.right - rWindow.left)/2;   
//p.y -= (rWindow.bottom-rWindow.top)/2;   
//********* Camera->Update()********************************************// 
//to rotate using euler angles as input.   
glm::mat4 R = glm::yawPitchRoll(m_horizontalAngle, m_verticalAngle,0.0f);      
//Then you could do the following to Update() a camera transformation:   
glm::vec3 T = glm::vec3(0, 0,-dist);   
position = glm::vec3(R * glm::vec4(T,0.0f)); 
m_direction = origin;//glm::normalize(position);  
m_up = glm::vec3(R * glm::vec4(m_real_up, 0.0f)); 
m_right = glm::cross(m_direction,m_up);   
m_viewMatrix = glm::lookAt(position, m_direction,m_up);  
 //************************************************************************//  
 //then update the MVP that typically goes to the shader at render time  
 OnMouseMove(MouseMove(p));        
}

Four important lines here are:

The 2 lines that update the mouse movement since the last movement (would typically be one raw pixel on a responsive ui thread) and,

The 2 lines that Just modify the current angles of camera rotation.

Can you see them?

That makes sense to me so far, what do you think?

Have I missed anything? Give it a go, and let us know maybe.

 

ps. Ok. Heres's one minor part I missed: where the parameters m_horizontalAngle, m_verticalAngle are converted to radians before being fed to glm::yawPitchRoll() ?

Could be more I missed.


Edited by steven katic, 19 August 2014 - 02:46 PM.






PARTNERS