Every now and then I like to play around with threejs a little, and when doing so I have found that one thing that is fun is working out expressions for handling the movement of a camera in a scene such as the perspective camera. There are all kinds of ways to go about moving a camera such as having the position of the camera move around an object in a circular pattern while having the camera look at an object in the center over time in an animation loop.
Then there is also having the position and rotation of a camera be subject to event handlers that are attached for pointer events. In other words to control the camera with a mouse, and or keyboard which is nice when I am working on a project and I would like to see how things look from all kinds of different perspectives. There are some official controls that are in the threejs Github repository for this sort of thing in the form of orbit controls, and fly controls for example. However there is also of course making ones own custom controls for a project which is often a required part of working out an over all user interface for some kind of real time project.
You should really look into the Object3d class when it comes to movement of objects in general in three.js
A camera in threejs inherits from a base class in threejs called Object3d, which is also the case with many other objects that will be part of a scene such as Mesh, Group objects, and many helper objects. So my learning how to work with the Object3d class you in turn learn how to work with everything to which is built on top of Object3d which includes cameras.
The main property of interest with the Object3d class in the position property which is an instance of a class known as Vector3, which in turn is another class of interest that applies to many things in three.js when it comes to positions of things. The set method of an instance of this Vector3 class is one way to set the position of a camera when it comes to the position property. However there is also changing the orientation of the camera when doing so, for this there is the rotation property that is also part of the Object3d class. This rotation property is an instance of the Euler Class which is like Vector3, only we are taking angles rather than a matrix position. There is working with this instance of THREE.Euler directly, or there is making use of a method like the Object3d.lookAt method.
All of these classes are worth looking into in depth in order to really know how to move things around, not just cameras but many objects in general.
You might want to check out the Orbit Controls, and other official controls as a way to move a camera by way of user input
When it comes to moving a camera the first thing you might want to figure out is if you just want to move about in the scene using the mouse. I often use the Orbit Controls that are in the examples folder of the Three.js repository for many of my examples as a way to be able to have the basic typical movement right away. There are also a number of other options when it comes to official controls use in the official three.js examples, as well as many other useful libraries to work with in the examples folder.
However when it comes to moving a camera by way of some kind of application loop, or working out custom controls that will work a little differently from that of the orbit controls. Then it would make sense t start working out some examples like the ones in this post here. Still of you think that the official orbit controls will work okay, and you just want to move on to other things you might want to check out my post on orbit controls.
The source code examples that I am writing about here can be found in my test threejs github repository. This is a also a location in which I park the source code for my many other blog posts on threejs beyond just this one.
When I first wrote this post I was using r111 of threejs, and the last time I edited this post I was using r146. Threejs is a fast moving project when it comes to development, so at some point in the future these threejs examples might break because of this. I do get around to fixing up my code every once in a while, but there will often be large gaps of time between major edits. So be sure to always check the version number of threejs when working with these examples, or any threejs examples on the open web for that matter.
I started out this example like that of any other threejs example when it comes to the usual set of objects. What I mean by that is having a scene object, camera, and renderer set up. After that I just created a single mesh object that I will be leaving at the default origin location.
I then have the main app loop in which I will be moving the camera over time, and this is where things can get at least a little involved when it comes to moving a camera this way. There are many ways of making this kind of animation update loop, some of which are a lot less complex than what I am doing here. However I have found that there are at least a few key features that one should always have in this kind of loop in order to have something that is in better shape for production.
The first and foremost thing that comes to mind is to not update the scene each time the loop function is called, rather a date should be used to find out how many seconds have based sense the last update and use that as a means to update the scene or not. This way the client running the threejs program is not getting slammed and I can adjust how low I want to fps to be for updating the scene. However fps values will result in using less CPU overhead, but at the cost of choppy rather than smooth motion.
On top of having a main FPS update value I can also have a septate FPS value for how to go about updating the frame rate of the animation. This way I can update the scene at a very low frame rate, but still update the position of the camera at a higher frame rate.
When this example is up and running the end result is having the camera at a location away from a mesh, and the camera is still looking in the direction of the mesh. In most cases I will not just want to set the position of the camera but also adjust the rotation of the camera as well one way or another, however that will be something I will be getting into more so inn the nest example here. Over time the camera moves, and I am not doing anything to change the rotation of the camera over time so the camera does not stay fixed on the mesh.
There is more to moving the camera that just moving the position of the camera in world space, there is also setting the rotation of the camera that can also be tough of as a kind of movement as well. There is learning how to work out all kinds of ways to manual setting the state of the Euler instance of the rotation property of a camera, but maybe the easiest way to go about setting the rotation of a camera would be to make use of the look at method of the object3d class.
A very useful method in the Vector3 object not just for moving a camera around, but objects in general, as well as points in space in general is the lerp method of the vector3 class. This is a method of vector3 in which I can call the method, then pass another vector3 object that I want to move to along with another argument that is an alpha value between 0 and 1. This alpha value will then set the value of the vector3 obect that I call the lerp method off of between the two vector3 objects based on the given alpha value.
This lerp method will mutate the vector3 object in place, so if I am in a situation in which I have two source vector3 objects that I do not want to mutate I can do something like call the copy method off of the vector3 object that is the position of the camera, and then pass the vector3 object that is the start point to the copy method. This will then copy the state of the start vector3 to the start point without mutating the start point, after that I can then call the lerp method, pass the end point that I want, and then the alpha value that I want to use to lerp between the start and end point.
1.4 - Using Curves to get Vector3 objects along a curve, and using that to set the position of the camera
One great tool for setting the position of a camera would be to look into using curves. The base class of curves has a get point method that allows for me to pass an alpha value and get a vector3 object that is at a corresponding point along the curve. The resulting vector3 object can then be used as a way to get the position of a camera. The same thing could also be done as a way to set the point for the camera to look at as well on top of that as well of course.
So far I have covered some simple examples that have to do with just changing th rotation and the position of the camera. However when it comes to setting the position of the camera I am doing so in terms of world space, or maybe a kind of relative space but relative to that is the scene object rather than a child of the scene object such as the one mesh object that I have in these examples thus far.
I have made a threejs project example cented around the idea of using curves to create paths that will be used to move objects. These objects can be mesh objects, but with the theme of this post in mind it can also be used to move a camera around a scene as well. What is great about this module is that I can not only use it to create a path in which to move a camera along, I can also use it to get the kind of value that I would use to get a point along the curve as well on top of that.
This is R1 of the curve module that I have made for my set of threejs project examples, many of which have to do with modules like this one rather than a full complete project of some kind. There is a whole lot to write about when it comes to the state of this module. However for the most part all I care around for the set of demos that I will be using here is the QB Curve Path method and also the get alpha function method. These are what I will be using to create a curve path for a camera to follow, and also how to create an alpha value to pass when calling the get point method of the curve. The return value of the get point method will be a vector3 object to which I can then copy to the Vector3 of the position property of the camera.
Here I am then using the curve module to create a path in space for which the camera will move along. I am also using the get alpha method to create a get alpha function. It is then the alpha value that will be returned by this get alpha function that will be passed to the get point method of the curve I created.
One thing that I have found that I like to do when making video type projects using threejs is to have some kind of system that can be used to break things down into many parts, or sequences if you prefer. This way I have have some kind of setup where I define an array of objects and each object can be given an update method. Each update method of each object would have access to a main sequence object with values that have to do with the progress of the over all video, but also values that have to do with the progress of the current part, or sequence.
On top of having update methods for each sequence object, I will also want to be able to define hook functions that will be called before and after the call of the current update method in the objects array. This way I can use the before objects hook to update the state of a mesh object that will change over the whole span of the video, or set defaults for things. The after objects hook would be a good place to do anything that I would want to always do with the camera or an object for every frame and override anything that might happen before.
I then just want to have some additional code that I can then use to demo this sequence module as a way to just test out the core features first for camera movement. With that said I can use the before objects hook of this project to define what I want to happen over the while span of a video project. For this demo I am just moving a mesh object of a sphere around in a circle. I can then use the after objects method to make sure that the camera will always look at the first mesh object. I should take care when doing this in the after hooks method as this will override any look at method, or rotation calls over that I might make in the before objects hook, or the current sequence object update method.
For this example I am making a more advanced version of my basic animation loop example that has to do with moving a camera. This time I have a move camera helper that takes a camera as an argument along with an update index number in the range of 0 to 1, and a function that will be used to create the values that will be used to change the camera position and orientation. So now the idea is more or less the same as the first animation loop example, but now I am creating additional code that has to do with creating a camera, with values attached to the userData object of the camera. If you are not aware of the user data object of a camera or any object in threejs that is based off of the object3d class, the user data object is just simply an object where I can pack values that have to do with my own custom system for an object such as a camera.
So moving a camera is more or less the same as moving anything else in three.js that inherits from the Object3d class by making use of the position and rotation properties of the object, and cameras are no exception. So then I could get into making all kinds of examples that have to do with different ways to change the values of the position and rotation of a camera over time, but with the collection of examples thus far the basic idea, and much more beyond that has been covered in this post I think.
I do get around to editing my content on threejs from time to time, and this post is just one of many that I am sure I will come back to again at some point also. I all ready have many more ideas when it comes to additional examples, and improvements to the examples thus far. If you did enjoy what I have wrote about here, or thing that you have obtained something of value there is checking out one of my other posts on threejs.