New Camera Classes in libgdx

I rewrote the classes OrthographicCamera and PerspectiveCamera this week for profit and success. The reason was that the old classes were c&p jobs from old projects off mine and were pretty nasty. I replaced the crap with new, shiny and easy to use classes and fixed a few bugs along the way.

So what is a camera? Here’s how a camera might look like in a scene:

A camera is defined by a position in 3D space, a direction given as a unit length vector and its “up” vector, again given as a unit length vector. Imagine an arrow coming out of the top of your head pointing towards the sky. That’s the “up” vector. Now tilt your head to the left or right. Can you imagine how it changes direction? Together with the direction vector the “up” vector allows us to tell OpenGL how our camera is orientated. The position vector tells it where in our 3D world the camera is located.

Position and orientation are only one part of the puzzle. The second crucial attribute of a camera is its view frustum. In the image above you can see a pyramid with its top cut off (where the eye is). That’s a view frustum. Anything inside this frustum will be visible on screen. The frustum is delimited by 6 so called clipping planes: near, far, left, right, top and bottom. In the image above those are just the sides of the pyramid. The near clipping plane has a special role: You can think of it as the surface were the picture the camera takes is generated. This process, transforming a 3D point to a 2D plane, is called projection. In general you’ll only work with two types of projection: orthographic and perspective projection.

Orthographic projection is mostly used for 2D graphics. It does not matter how far away an object is from the camera, it will always have the same size on the screen. Perspective projection is what we are used to in the real world: the farther an object is away from our eyes the smaller it gets.

The funny thing about those projection types is that in terms of our camera attribute all they do is changing the shape of the view frustum. In case of a perspective projection the view frustum looks like the pyramid above. In case of an orthographic projection the view frustum is a box. The actual projection process is pretty simple: from each point of an object we draw a line to the camera and calculate where it hits the near clipping plane of the frustum (that’s of course not entirely correct and very simplified. for our purposes it will do). Here’s how that looks for both types of projection:

Can you see why objects get smaller after projection in case of a perspective projection? Or why they stay the same size if you use an orthographic projection? The only difference is how the shape of the frustum! In OpenGL you always work in 3D, no matter whether you use SpriteBatch or draw a text. The secret is that you just pretend the z-axis doesn’t exist, while using an orthographic camera. Here’s a 2D sprite in 3D space:

So forget about there being a difference between 2D and 3D. There is no difference. Here’s the view frustum you’d use if you draw via SpriteBatch without setting any matrices:

It’s just a freaking box! All we do is let our sprites move in the x/y plane, ignoring the z-axis, keeping up the illusion that we are actually working in 2D space.

A perspective camera has two attributes that define its projection: the field of view and the aspect ratio. The field of view is an angle that defines how “open” the frustum is:

The aspect ratio is the ratio between the width and the height of the viewport. The viewport is the rectangular area to which the image the camera “takes” will be rendered to. So if you have a screen with 480×320 pixels your aspect ratio is 480 / 320.

An orthographic camera’s projection is merely defined by its viewport dimensions as can be seen in the image above (the ortho view frustum box one :)).

Now that you (somewhat) know how camera’s work let’s check out new camera classes. There’s three of those suckers: the Camera class, which is a base class, the OrthographicCamera class and the PerspectiveCamera class. The last two derrive from the Camera class and thus share the same members and attributes. Let’s look at the Camera class:

The first three public members are the position, direction and up vector of our camera. They default to standard values so that the camera is located at the origin, looking down the negative z-axis. You can access and manipulate those as you please.

Next we have a shitton of matrices. Those will only interest you if you work with OpenGL ES 2.0. The first two hold the projection and (model-)view matrix. The third one holds those two multiplied together and the final matrix is the inverse of the combined matrix. That one is usually use to do things like picking and so on. As i said, you are unlikely to touch those yourself.

Next we have the near and far clipping plane distance to the camera as well as the viewport width and height. The near and far clipping plane must always be 0 <= near < far. A setter would be nice to assert this but i decided i expose those bastards without safeguards. By default the near clipping plane will be 1 unit away from the camera's position. For ortho camera's you'll usually set the near to 0 (the OrthographicCamera does this automatically on construction!). The viewport width and height is used to calculate the aspect ratio for the PerspectiveCamera and to define the box view frustum for an orthographic camera.

The final member is the Frustum. It's composed of the 6 Planes of the view frustum of a camera. This Frustum can be used for so called culling: checking whether an object is within the view frustum or not. In case it isn't you don't have to draw it! The frustum has a couple of methods you can use to determine whether a BoundingBox, sphere or Point are in the frustum and thus visible. See the javadocs.

Next we have a couple of nice methods. The update() method will recalculate the matrices of the camera. Call this after you have changed any of the attributes of the camera, like its position or near/far clipping plane and so on. The apply() method will set the GL_PROJECTION and GL_MODELVIEW matrix according to the camera's matrices. That won't work with OpenGL ES 2.0 of coures, but you shader loving guys probably know what to do instead 🙂

Next we have a couple of methods that allow you to let the camera look at a specific point in space, rotate it around an axis by some angle and move it by some amount. Those are just little helper functions, you could achieve this by directly manipulating the the position/direction/up vectors of the camera.

finally we have some methods that are needed if we want to do more advanced stuff. The unproject method will take a point in window coordinates (or screen coordinates) and generate a 3D point out of it. That's what OrthographicCamera.screenToWorld() did in the old class. It works like gluUnproject. The x and y coordinate could be touch coordinates, the z-coordinate of the parameter you pass is a value between 0 and 1 usually. 0 means that you generate a point on the near clipping plane, 1 generates a point on the far clipping plane. The project() method does the oposite: it takes a point in the 3D world and transforms it to a 2D point on screen. The getPickRay() method will return you a Ray for ray picking. Think of this as a stick in your 3D world, starting at your camera's position. You usually pass in touch coordinates and then use the Ray with the Intersector class to test whether it hit some geometry/object in your world. So that's what both camera classes share. Neat huh? Let's have a look at the OrthographicCamera:

Yeah, that's it. It has one additional member that lets you define a zoom factor. The constructor takes the viewport width and height you want the camera to have. If you want to have pixel perfect rendering you just specify GRaphics.getWidth()/getHeight() here (or whatever you have). If you want to use it in conjunction with say Box2D you'd probably use a different unit scale, say meters (e.g. 42, 32). You can of course also use that camera in 3D (remember: it is actually working in 3D!), just as in a CAD program for example. The PerspectiveCamera is equally simple:

It also only has a single additional member, the field of view given in degrees. The aspect ratio is calculated from the camera's viewport width and height (which you also have to set in the constructor). You can find a couple of tests in SVN that show some of the features, like picking or using the project method:

  • CullTest: shows you how to perform culling
  • PickingTest: shows you how to perform picking (on spheres)
  • ProjectTest: shows you how to transform a 3D point to 2D and use that to render a 2D image with SpriteBatch on top of a 3D object. It’s like anchoring a 2D element to the 3D object

Yay, tl;dr: we have new camera classes…

18 thoughts on “New Camera Classes in libgdx

  1. Hi, keep up the good work, libgdx is a nice library

    This article is very good, I’ve been waiting for this one because there were camera concepts which seemed obscure to me

    Could you write a similar explanation about the use of matrices with SpriteBatch class ? I really misunderstand one or two key concepts to use them properly


  2. Use an OrthographicCamera, then do


    That’s all there is to is really. modify the camera’s attributes like its position and zoom level and you are all set.

  3. Wow, you’ve done more than just update the camera classes: you’ve written the most accessible article on cameras I’ve ever seen. This stuff isn’t so hard once you get the right picture in your mind, and you’ve nailed it. Whenever somebody asks me about this stuff again, I’ll simply point them to here.

  4. Awesome! 😀

    As a GLES 2.0 guy I love that the projection, view , .. , matrices now are more easy available 😀

    Keep up the good work, libgdx rules!

  5. I’m completely new to libgdx (I started looking at it about two hours ago) so correct me if I’m wrong, but I’m assuming that this means quite a few parts of gdxinvaders are broken? Specifically the bits that used the old PerspectiveCamera functions?

  6. I’m going to update gdx-invaders asap. Other examples (like “Super Jumper”, Metagun etc.) work as expected. The change isn’t all that big really, just need to fix the camera instantation and update calls. 5 lines of code.

  7. Is there ANY documentation of the camera class or any reference anywhere? I was trying to look at the demos (outdated), javadoc (camera class is not even there) and wiki (about 5 empty pages)?

    I would really like to use libgdx but at this point I would be happier if you would take donations or siomething and could hire someone to make decent documentation because at this level even me (15+ years of programming experience, over 10 professionally including games) can’t really understand what I should be doing.

    This is not meant as a bad comment by the way, the documentation just is really lacking at this point..

  8. Full Javadocs are included in the nightlies/releases. Examples can be found on the Google Code page, as pointed out on the Google Code page. (which also points out where to find the Javadocs). The blog article above, the one you just commented on, decribes how to use the camera as well. The example games also show you how to use the Camera’s. Am i missing something?

  9. QUOTE:

    The apply() method will set the GL_PROJECTION and GL_MODELVIEW matrix according to the camera’s matrices. That won’t work with OpenGL ES 2.0 of coures, but you shader loving guys probably know what to do instead

    Hello! I am trying to port my GL10 code to GL20, and and make extensive use of the Camera.apply(GL10 gl) method. Could you please provide some guidance to achieve this functionality using shaders? Greatly appreciated. Many thanks, Dan

  10. “The near clipping plane has a special role: You can think of it as the surface were the picture the camera takes is generated.”

    “The viewport is the rectangular area to which the image the camera “takes” will be rendered to.”

    So is near clipping plane = viewport ?
    What is a viewport?

Leave a Reply

Your email address will not be published.