glm/opengl orbital camera C++

Started by
9 comments, last by steven katic 9 years, 8 months ago

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;
Advertisement

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

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);

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

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

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?

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);
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]
PS I <3 GLM.

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?

This topic is closed to new replies.

Advertisement