|
YOUR FEEDBACK
Did you read today's front page stories & breaking news?
SYS-CON.TV SYS-CON.TV WEBCASTS |
MXDJ TOP LINKS YOU MUST CLICK ON ! Director Using Multiple Cameras
Creating efficient overlays and scenegraph control
Dec. 21, 2004 12:00 AM
Shockwave3D (S3D), despite a number of advantages such as delivery on the Web and small plug-in size, suffers from a few disadvantages in terms of its architecture. One of the "classic problems" with regard to the way S3D is built is the inability of the developer to manipulate exactly when objects are drawn to the screen, or more correctly the order in which they are drawn to the screen. This is in contrast to almost any traditional C or C++ graphics engine on the market today, which since they are written directly against DirectX or OpenGL can simply order the calls to the various draw commands to make sure that certain objects are drawn before or after others. This technique is commonly used, for example, to make sure that skyboxes draw before any other objects in the scene, or to ensure that the Heads-Up Display that features the player controls are always drawn "on top of" any of the other elements in the scene. In this article, we will explore using multiple cameras that are linked together through a management structure in such a way as to give back to the Lingo developer what is missing in Shockwave3D: semi-direct scenegraph manipulation. The authors have successfully used this technique in a game development project entitled "Broadsword," and all demo code is taken from that source tree. The technique, however, is extractable to any Shockwave3D environment, and can be used regardless of subject matter. For example, Brian Robbins presented on this technique and its use in his own work at Macromedia MAX. To begin, it is often a good idea to review what we know about how the computer renders a 3D scene. Objects are made up of triangles, which are rendered onto a two-dimensional image plane by projection from the viewer (i.e. the camera in the scene). As triangles are rendered on to the plane, two pieces of information are stored per pixel. First, the color of the pixel is stored, based on several calculations in 3D space such as texture, material, lighting, etc. Second, a depth value is stored per-pixel, which represents the depth of that pixel from the viewer. The collection of all of the depth-values for the 2D image that the scene is projected onto is called the depth buffer, which is simply a memory area in which the depth value for each pixel is stored. When a second triangle is drawn to the scene, as it renders its pixels onto the plane it looks at the depth information of the pixels it would be writing to. If the pixel that would be written is closer to the viewer than the pixel already stored, then the new pixel is written. If it isn?t, then no change is made. This is how 3D determines that one triangle is "behind" or "in front of" another triangle. After all of the triangles in the viewable scene area (read: "inside the camera frustum") have been drawn to the image, the image is flipped onto the screen. But imagine that you could, whenever desirable, clear the depth buffer, and by that we mean to reset all the values in it to a default value. The next time something was drawn to the screen, it would have no existing depth information, and so it would be either completely behind or completely in front of the existing information. In a true game-like environment, this is how skyboxes and overlays are drawn. Unfortunately, in S3D we don?t have a way of clearing the depth buffer directly (this is not entirely true, there are some workarounds using the unsupported #frontNoDepth flag. There is, however, a solution. Each camera in the scene, because it projects any objects that are its children independently of other cameras in the scene, does not share its depth information with any other camera. Thus, if there are two cameras, camera A and camera B, and A draws before B, when B renders triangles onto the buffer it cannot read from the depth buffer of A. The benefit of this is that any object projected through camera B will draw on top of any objects projected through camera A, regardless of their actual positions in 3D space. And with that, we can now explore a system that allows us to assign order to specific sets of objects, based on which camera they are seen through. For ease of use, we term each set of objects grouped with its own camera as a "render group," because it is drawn separately from its peers. Render groups are drawn in the order they are initially created, but objects can be added or deleted from a specific group at any time. To begin, we create the initial camera for the entire scene by calling ghCameraStartup (see Listing 1), and create an initial set of render groups by calling ghSetupRenderGroups (see Listing 2). In Listing 2 we identify five render groups - Ground, Weapons, Objects, Explosions, and Windows -which are always drawn in that order, back to front. Thus, the ground is always behind all objects in the scene, and any Windows, which we use instead of Lingo overlays, are always on top). The ghSetupRenderGroups handler is really just a call to establish a render group manager (see Listing 3), which in turn is merely a collection of actual render group objects. Our manager is simply a glorified property list, and developers should feel free to use a base properly list if that is their preference. The only caveat is that you cannot sort the list to determine draw order: the reason the render group created first renders before the render group that is created second is because the first camera is before the second in the list of cameras in the 3D cast member. That is the order that determines which camera draws when, not the property list of Lingo objects. The real "meat" of a render group is in the render group object, which is an object that creates a new camera, parents itself directly to the main scene camera that is set up in the script in Listing 4, and then provides mechanisms for any object, light, or group in the scene to be added or removed from that render group. The entire script of the render group object is presented in Listing 4, with comments immediately afterward. Clearly, a good 90% of that script is repetitive structure for adding and removing (or more formally "parenting and un-parenting") lights, models, and groups. There are some interesting issues to be noted in the new handler, however, where actual setup takes place. First, we create a new camera by cloning the exiting scene camera. However, once we parent a first render group to the main camera, we cannot clone it a second time, or we would not only copy the original camera, but also the child. Thus we always copy the last camera created, or the lowest child on the tree. Second, because more than one camera will be rendering, we turn off their clearColorBuffer property, ensuring that no single render group can clear any of the others. The only camera in the entire scene that should be clearing the color buffer is the main scene camera. Finally, we create a group to hold all of the lights and objects for this render group. NOTE: When we began using this technique, we were surprised that all of our objects rendered solid black. This is because each camera needs its own lights. This was originally thought of as a drawback to the system; however, we have since used this to great effect, having lights in the scene that affect some objects and not others, and have come to rely on this technique in several of our Shockwave3D worlds. With all of that setup work in place, objects are created as per any other 3D world, through primitive shapes such as #sphere and #box, imported from 3dsMAX or a similar program, or built from scratch with the newMesh() call. Regardless of how an object is built, the final step in adding it to the world now looks like Listing 5. This technique has been invaluable. It has allowed us to construct our own overlays by texturing two camera-oriented triangles. It has allowed us to control draw order regardless of 3D position, and to effectively layer different elements in a scene. Finally, it has been a useful tool in "sky-boxing" several worlds and ensuring that the size of the skybox has nothing to do with the size of the world. NOTE: By setting a camera for a specific group to orthographic projection mode (#ortho) and positioning its rect to the rect of the 3D sprite, there is a 1:1 relationship between screen-space and world-space along the x/z plane. (Also note that in fact these coordinates are off by exactly 0.5 pixels when using the DirectX renderer, because of a bug in the underlying library). As a final example, Figure 1 shows two examples of Broadsword running in tech-demo mode, with notations about camera groups and render order. We believe that this technique should prove useful in a wide variety of situations commonly encountered in Shockwave3D, and encourage developers to build for themselves their own grouping systems to take more complete control of the scenegraph. References LATEST FLEX STORIES & POSTS
SUBSCRIBE TO THE WORLD'S MOST POWERFUL NEWSLETTERS SUBSCRIBE TO OUR RSS FEEDS & GET YOUR SYS-CON NEWS LIVE!
|
SYS-CON FEATURED WHITEPAPERS MOST READ THIS WEEK |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||