Using Multiple Cameras

When Shockwave3D renders to a sprite, it steps through a list of cameras assigned to that sprite and draws all the geometry assigned to each camera. Typically, you only have one camera assigned to each sprite, but it can be useful to have multiple cameras associated with a sprite.

Handy Use Cases for Multiple Cameras

This 3D animation is using three cameras: one for the starfield, one for the planet, and one for the moon. The models for the starfield, the planet, and the moon are all about the same size; the difference is in how the cameras are set up.

Whenever you need to control the order in which things are displayed, or whenever you need to have different camera controls for different geometry, using multiple cameras can come in handy.

For instance, suppose you wanted to have a heads-up display which uses more than just 2D textures. You can't use the "overlay" feature, since overlays can only be 2D. And if you use 3D, you would normally have to be careful to not let the 3D geometry of the heads-up display interact with the world geometry. And you wouldn't have the option to, for instance, have orthographic rendering of the heads-up display without having orthographic rendering of the scene, or to have your display be lit differently than the surrounding environment.

Using multiple cameras solves this problem. One camera can render the game world, while the other camera can render the heads-up display. Each camera can have different settings (fog, hither, yon, orthographic rendering, etc.), can look at entirely different geometry, be lit by totally different lights, and can be told to render one after the other.

Another oft-used example would be a skybox. Typically, you don't want your skybox to move in relation to the camera, because it is meant to be very far away; you don't want perspective clues revealing that the skybox is made up of planes nearby. But normally, you wouldn't be able to treat the skybox geometry differently than the game world geometry.

A final example might be a case where you want to have tight control over your hither and yon. Suppose you were writing a big space opera game, and you wanted to have a dogfight in space near a rotating planet. The planet needs to be geometry, since it is rotating, but it needs to be very far away compared to the spaceships. In order to display a planet that far away - and the star field beyond - you would have to set the 'yon' property of your camera to be very far away, which would reduce the rendering precision of the important stuff - the spaceships near the camera. Using multiple cameras, you could have a camera that looks at a starbox with a small sphere planet in it, tied to the direction the player camera is facing, which gets rendered first, and then another camera that only has the spaceships in it, with a reasonable yon value.

Setting Up Multiple Cameras

You need to do a few things in order to get multiple cameras set up in your scene (I'll go over each of these in turn below):

  1. Housekeeping
  2. Create a camera
  3. Add it to the sprite
  4. Set its rect
  5. Turn off its "clear at render" property.
  6. Give it a group to render
  7. Add exclusive geometry to the group
  8. Give it lights or emissive shaders to draw with

At the end of this article, you will find sample code that will do all this for you, but you need to understand why I do each thing because there will often be times you'll want to modify or omit each particular step. All code snippets are in text boxes for easy cut-and-pasting.

Housekeeping

If you're going to use this technique, I've found that it's a good idea to just clean out all cameras from the sprite you're rendering to and start clean.

In the sample code at the end of this article, I've provided a clearCameras() routine that does this for you.

Create a Camera

Before you can start working with multiple cameras, you need to add new camera objects to your Shockwave3D world. This can be handled by cloning an existing camera, as I've seen mentioned in several places online, but that doesn't work for the general case, since you never know what is parented to the camera. I suspect this is done to preserve the same transform, but for most of the effects mentioned above - except for the skybox - you're going to want them to behave separately, so I prefer to start fresh.

So what I recommend is to simply create a new camera from scratch and define its properties. In the code below, I just go with the default properties for the new camera, so you may want to manually set things like hither, yon, fog, etc. if you want them.

You'll also want to set the camera's position and rotation at this step.

Add it to the sprite

Just because you have multiple cameras in the Shockwave3D member, that doesn't mean that they will be used. Cameras don't really do anything other than represent possible views of the 3D world. Only when you tell a sprite to look through those cameras do they start appearing on your stage.

Typically, people would set different sprites to use different cameras, to provide multiple views of a 3D scene. In this case, you would just set the sprite's camera property.

But what we want to do is have multiple cameras in the same sprite. In order to achieve this, we need to use the sprite's addCamera() method.

This method takes two parameters: the camera object to add, and where in the list of cameras to add it. (Contrary to the documentation, you do not pass in the name of the camera, but a reference to the camera object.)

The cameras will be drawn in the order they appear in the sprite list, so when you add the camera, you need to tell it where in the ordering you want the camera to be drawn. High numbered cameras render after low number cameras, so if you want a backdrop effect, use a low number for the camera index. If you want an overlay effect, use a high number for the camera index.

Set the Camera's Rect

When you add a camera to a sprite, you have to tell it where to draw into the sprite. This is as simple as setting a rect, but if you don't do this, your camera view may not show up at all.

The easiest thing to do - and almost always what you'll want to do - is to set it to the sprite's dimensions:

(Note that this is different from the sprite's rect - the camera's rect is measured from the upper left of the sprite, not the stage.)

Turn Off the Camera's "Clear at Render" Property

The default behavior of a camera is to clear its rect to the background color every frame before starting to draw geometry again. That way, if you set the background color to black, each frame, it draws your geometry on top of a clean, black background. This is usually what you want; otherwise, your 3D elements would leave trails wherever they cross over non-geometry screen space.

But when you have multiple cameras, you only want the first camera to clear the stage to the background. Subsequent cameras should not clear the stage, because otherwise, all the rendering work we did with the previous cameras would just be wasted CPU cycles.

Since the default setting for a camera is to clear to the background color, we need to tell it not to clear when it renders. You do this by setting its colorBuffer.clearAtRender property to FALSE:

Note that you don't want to do this if this is going to be the first camera in the sprite list! In that case, you want to leave its clearAtRender property to TRUE. The sample code a the end of this article keeps track of that for you.

Give the Camera a Group to Render

Each camera has a rootNode property which defines what geometry it renders and what it doesn't. Any model that isn't a descendent of the rootNode does not get rendered by the camera.

By default, a camera's rootNode is set to the "world" group, which is basically everything that hasn't been removed from the world. That's why, by default, cameras can always see things you add to the world, and why you don't generally have to fiddle with the rootNode of cameras.

By changing the rootNode to something other than the world group, you tell the camera to only render that item and its descendants, rather than everything in the world.

While you can specify a particular model to be the only thing rendered by the camera, a more generic, flexible practice is to simply have a group that gets rendered by the camera instead. That way, it's a simple matter to add geometry to the camera's view without having to parent a new model to an existing model.

Once you have a group created to render for the camera, you want to removeFromWorld() that group so that no other camera sees it, and then set it to be the camera's "root node" so that the camera renders it and all its descendents:

Once you do that, the camera has it's own little world that it renders into that no other camera sees into.

Add Exclusive Geometry to the Group

Now that you have a group that is exclusive to this camera layer, you can start adding the geometry to the rootNode group that you want to be exclusive to this camera. As you create the geometry, make sure you parent it to the camera's rootNode, or to a descendant of it.

Give the Camera Lights or Emissive Shaders to Draw With

If you were to look at your scene at this stage, you may see your geometry there, but it would be all black. This is because the rootNode doesn't only specify the geometry that gets drawn by the camera; it also determines what lighting gets applied to it.

The geometry that a camera draws is only illuminated by descendents of its rootNode (and the rootNode itself if it happens to be a light). If you want to be able to see the geometry in the camera's group, you also need to add lights somewhere under the rootNode.

If you want the illumination to match the world, you'll have to duplicate the lights into the exclusive group.

Alternately, you could apply a shader with an emissive property other than black, or an emissive texture layer. This is especially handy for things like heads-up displays which you want to be a consistent brightness regardless of light levels.

Sample Code

Here is some sample code you can use in your own projects. Here, there is an addExclusiveCamera() routine which will set up a new camera-view in a given sprite, and a group to which you can add geometry and lights which can only be seen by that camera.

To use it, simply call the routine to get camera objects, then add geometry and lights to the camera's rootNode to add it to the scene.

Made on Mac