Tips and Tricks on Away3D 3.6

Exclusive offer: get 50% off this eBook here
Away3D 3.6 Essentials

Away3D 3.6 Essentials — Save 50%

Take Flash to the next dimension by creating detailed, animated, and interactive 3D worlds with Away3D

$29.99    $15.00
by Matthew Casperson | March 2011 | Open Source Web Graphics & Video

In this article by Matthew Casperson, author of Away3D 3.6 Essentials, you will learn how to optimize your Away3D applications, which will allow you to create spectacular 3D environments while maintaining a high level of performance.

  • Determining the current frame rate
  • Setting the maximum frame rate
  • Setting the stage quality
  • Modifying the size and scaling of the viewport
  • Triangle caching
  • Level Of Detail (LOD) 3D objects
  • The Away3D filter classes

 

Away3D 3.6 Essentials

Away3D 3.6 Essentials

Take Flash to the next dimension by creating detailed, animated, and interactive 3D worlds with Away3D

        Read more about this book      

(For more resources on Away3D, see here.)

Determining the current frame rate

When we talk about the performance of an Away3D application, almost always we are referring to the number of frames per second (FPS) that are being rendered. This is also referred to as the frame rate. Higher frame rates result in a more fluid and visually-appealing experience for the end user. Although it is possible to visually determine if an application has an acceptable frame rate, it can also be useful to get a more objective measurement. Fortunately, Away3D has this functionality built in. By default, when it is constructed, the View3D class will create an instance of the Stats class, which is in the away3d.core.stats package. This Stats object can be accessed via the statsPanel property from the View3D class. You can display the output of the Stats object on the screen using the Away3D project stats option in the context (or right-click) menu of an Away3D application.

To see the Away3D Project stats option in the context menu you will need to click on a visible 3D object. If you click on the empty space around the 3D objects in the scene, you will see the standard Flash context menu.

Tips and Tricks on Away3D 3.6

This will display a window similar to the following screenshot:

Tips and Tricks on Away3D 3.6

This window provides a number of useful measurements:

  • FPS, which measures the current frames per second
  • AFPS, which measures the average number of frames per second
  • Max, which measures the maximum peak value of the frames per second
  • MS, which measures the time it took to render the last frame in milliseconds
  • RAM, which measures how much memory the application is using
  • MESHES, which measures the number of 3D objects in the scene
  • SWF FR, which measures the maximum frame rate of the Flash application
  • T ELEMENTS, which measures the total number of individual elements that make up the 3D objects in the scene
  • R ELEMENTS, which measures the number of individual elements that make up the 3D objects that are being rendered to the screen

These values come in very handy when trying to quantify the performance of an Away3D application.

Setting the maximum frame rate

Recent versions of Flash default to a maximum frame rate of 24 frames per second. This is usually fine for animations, but changing the maximum frame rate for a game may allow you to achieve a more fluid end result. The easiest way to do this is to use the SWF frameRate meta tag, which is a line of code added before the Away3DTemplate class.

[SWF(frameRate=100)]
public class Away3DTemplate extends Sprite
{
// class definition goes here
}

The SWF FR measurement displayed by the Away3D Stats object reflects the maximum frame rate defined by the frameRate meta tag.

Note that setting the maximum frame rate using the frameRate meta tag does not mean that your application will always run at a higher frame rate, just that it can run at a higher frame rate. A slow PC will still run an Away3D application at a low frame rate even if the maximum frame rate has been set to a high value.

You also need to be aware that any calculations performed in the onEnterFrame() function, such as transforming a 3D object, can be dependent on the frame rate of the application. In the following code, we rotate a 3D object by 1 degree around the X-axis every frame.

override protected function onEnterFrame(event:Event):void
{
super.onEnterFrame(event);
shipModel.rotationX += 1;
}

If the frame rate is 30 FPS, the 3D object will rotate around the X-axis by 30 degrees every second. If the frame rate is 90 FPS, the 3D object will rotate around the X-axis by 90 degrees every second. If your application requires these kinds of transformations to be performed consistently regardless of the frame rate, you can use a tweening library.

Setting Flash quality to low

You may have noticed that Flash offers a number of quality settings in its context menu. This quality setting can be set to one of the four options, which are defined in the StageQuality class from the flash.display package. As described by the Flash API documentation, these settings are:

  • StageQuality.LOW: Low rendering quality. Graphics are not anti-aliased, and bitmaps are not smoothed, but runtime still use mip-mapping.
  • StageQuality.MEDIUM: Medium rendering quality. Graphics are anti-aliased using a 2 x 2 pixel grid, bitmap smoothing is dependent on the Bitmap.smoothing setting. Runtimes use mip-mapping. This setting is suitable for movies that do not contain text.
  • StageQuality.HIGH: High rendering quality. Graphics are anti-aliased using a 4 x 4 pixel grid, and bitmap smoothing is dependent on the Bitmap.smoothing setting. Runtimes use mip-mapping. This is the default rendering quality setting that Flash Player uses.
  • StageQuality.BEST: Very high rendering quality. Graphics are anti-aliased using a 4 x 4 pixel grid. If Bitmap.smoothing is true the runtime uses a high-quality downscale algorithm that produces fewer artifacts.

Mip-mapping refers to the use of mip-maps, which are precomputed smaller versions of an original bitmap. They are used instead of the original bitmap when the original is scaled down by more than 50 %. This bitmap scaling may occur when a 3D object with a bitmap material is itself scaled down, or off in the distance within the scene.

The quality setting is defined by assigning one of these values to the quality property on the stage object:

stage.quality = StageQuality.LOW;

A number of demos that are supplied with Away3D set the stage quality by using the SWF quality metatag, like so:

[SWF(quality="LOW")]

The Flex compiler does not support setting the stage quality in this way. Although this code will not raise any errors during compilation, the stage quality will remain at the default value of StageQuality.HIGH.
You can find more information on the metatags supported by the Flex compiler at http://livedocs.adobe.com/flex/3/html/help.html?content=metadata_3.html.

Setting the stage quality to low will improve the performance of your Away3D application. The increase is felt most in applications that display a large number of 3D objects.

The downside to setting the stage quality to low is that it affects all the objects on the stage, not just those drawn by Away3D. The low stage quality is particularly noticeable when rendering text, so the visual quality of controls like textfields and buttons can be significantly degraded.

Using the medium-quality setting offers a good compromise between speed and visual quality.

Reducing the size of the viewport

The fewer pixels that are drawn to the screen, the faster the rendering process will be. The area that the view will draw into can be defined by assigning a ClippingRectangle object to the clipping property on the View3D class.

To use the RectangleClipping class you first need to import it from the away3d.core.clip package. You can then define the area that Away3D will draw into by supplying the minx, maxX, minY, and maxY init object parameters to the RectangleClipping constructor like so:

view.clipping = new RectangleClipping(
{
minX: -100,
maxX: 100,
minY: -100,
maxY: 100
}
);

The preceding code will limit the output of the view to an area 200 x 200 units in size.

The ViewportClippingDemo application, which can be found on the Packt website as code bundle, allows you to modify the size of the clipping rectangle at runtime using the arrow up and arrow down keys. You can see the difference that the clipping rectangle makes in the following image. On the left, the clipping rectangle is set to the full area of the stage. On the right, the clipping rectangle has been reduced.

Away3D 3.6 Essentials Take Flash to the next dimension by creating detailed, animated, and interactive 3D worlds with Away3D
Published: January 2011
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Away3D, see here.)

Scaling the viewport output

Another way to reduce the number of pixels rendered by Away3D is to assign a BitmapSession object to the session property in the View3D class. The Number passed to the BitmapSession constructor defines the internal scale of the bitmap that the scene will be rendered into. The default scale is 2, which will create an internal bitmap whose width and height are half that of the view. This means that the final size of the internal bitmap is one quarter of the size of the view, and thus only a quarter of the number of pixels need to be rendered.

To use the BitmapSession class, it first needs to be imported from the away3d.core.session package. A new BitmapSession object can then be assigned to the session property in the View3D class:

view.session = new BitmapSession(2);

The internal bitmap is scaled up to fill the stage when it is drawn. So unlike clipping the viewport, using the BitmapSession class allows your application to be displayed using the entire area available to it. However, this does result in a pixilated look. You can see this pixelation in the following images. The BitmapSession object used to render the image on the left has a scaling of 1, while the BitmapSession object used to render the image on the right has a scaling of 2.

Tips and Tricks on Away3D 3.6

Triangle caching

Away3D includes a feature called triangle caching, which is enabled by default. Triangle caching removes the need for 3D objects to be re-rendered if the appearance of the canvas they are rendered into has not been modified during the last frame.

By default, all 3D objects are rendered into one common canvas. In this scenario, the triangle caching system will only provide some benefit if none of the 3D objects in the scene are modified. While this results in a massive speed boost for static 3D scenes, the ability to draw a static scene at a high frame rate is actually not all that helpful—you could just as easily display a static image instead. Thankfully, triangle caching can also be separately enabled for an individual 3D object by setting its ownCanvas property to true, or enabled for a group of 3D objects by adding them as children of an ObjectConatiner3D object that has its ownCanvas property set to true. In this way, the 3D objects that are re-rendered each frame can be limited to those individual 3D objects that are modified, or to the children of a container where one of those children was modified. In all cases though, modifying the camera will cause each 3D object to be re-rendered.

Triangle caching is most useful when the camera is static and only a small number of 3D objects need to be modified for a given frame. The TriangleCachingDemo application (code bundle,) demonstrates this by adding 75 complex 3D objects to a scene. Each of these 3D objects has its ownCanvas property set to true, enabling the triangle caching to be applied to each 3D object individually. Each 3D object also responds to the MouseEvent3D.MOUSE_OVER and MouseEevent3D.MOUSE_OUT events to animate it when it is under the mouse cursor. With a maximum of one 3D object being modified at any one time, and therefore a maximum of one 3D object being re-rendered every frame, triangle caching allows the application to run at a much higher frame rate than would be possible if every 3D object was re-rendered for every frame.

package
{
import away3d.containers.ObjectContainer3D;
import away3d.core.base.Mesh;
import away3d.core.utils.Cast;
import away3d.events.MouseEvent3D;
import away3d.loaders.Max3DS;
import away3d.materials.BitmapMaterial;

import flash.events.Event;
import flash.filters.GlowFilter;
import flash.utils.getTimer;

public class TriangleCachingDemo extends Away3DTemplate
{
[Embed(source="ship.3ds", mimeType="application/octet-stream")]
protected var ShipModel:Class;
[Embed(source="ship.jpg")]
protected var ShipTexture:Class;

protected static const MESH_SCALE:Number = 0.02;
protected static const SCALE_FACTOR:Number = Math.PI / 1000;
protected var selectedMesh:ObjectContainer3D;

public function TriangleCachingDemo()
{
super();
}

protected override function initScene():void
{
super.initScene();
this.camera.z = 0;

The initial 3D model is loaded and textured from the embedded files. We will use this as a template for all the 3D objects that will be added to the scene.

var shipMaterial:BitmapMaterial =
new BitmapMaterial(Cast.bitmap(ShipTexture));
var shipModel:ObjectContainer3D =
Max3DS.parse(Cast.bytearray(ShipModel),
{
autoLoadTextures: false,
scale: MESH_SCALE,
rotationY: 180,

For the triangle caching system to work we need to set the ownCanvas property to true.

ownCanvas: true
}
);
for each (var child:Mesh in shipModel.children)
child.material = shipMaterial;

Here we use three for loops, each used to calculate either the x, y, or z position of the individual 3D objects that will be added to the scene.

var meshClone:ObjectContainer3D
for (var xPos:int = -40; xPos <= 40; xPos += 20)
{
for (var yPos:int = -40; yPos <= 40 ; yPos += 20)
{
for (var zPos:int = 100; zPos <= 180 ; zPos += 40)
{

We then clone the template 3D object. This saves us the time it takes to parse the 3D model file (which, as we will see later on, can be quite significant). It also saves some memory, as each of the cloned 3D objects references the geometry of the template 3D object, instead of maintaining their own copy of that data.

meshClone = shipModel.clone() as ObjectContainer3D;

The cloned 3D object is then repositioned and added to the scene.


meshClone.x = xPos;
meshClone.y = yPos;
meshClone.z = zPos;
scene.addChild(meshClone);

Each cloned 3D object is set to respond to the MouseEvent3D.MOUSE_OVER and MouseEvent3D.MOUSE_OUT events.

meshClone.addEventListener(
MouseEvent3D.MOUSE_OVER,
onMouseOver
);
meshClone.addEventListener(
MouseEvent3D.MOUSE_OUT,
onMouseOut
);
}
}
}
}

protected function onMouseOver(event:MouseEvent3D):void
{

We set the selectedMesh property to reflect the currently selected 3D object when the mouse is over it.

selectedMesh = event.target as ObjectContainer3D;

The GlowFilter class is used to visually indicate which 3D object is selected.

selectedMesh.filters = [new GlowFilter()];
}

protected function onMouseOut(event:MouseEvent3D):void
{

When the mouse has been moved off a 3D object, the selectedMesh property is set to null to reflect the fact that no 3D object is currently selected.

selectedMesh = null;
var mesh:ObjectContainer3D =
event.target as ObjectContainer3D;

The filters property of the 3D object is set to an empty array, clearing the GlowFilter object that was assigned to in the onMouseOver() function.

mesh.filters = [];

The scale of the 3D object is set back to its default.

mesh.scale(MESH_SCALE);
}

protected override function onEnterFrame(event:Event):void
{
super.onEnterFrame(event);
if (selectedMesh != null)
{

If there is a 3D object under the mouse pointer, we use some simple math to bounce its scale between 1 and 2. This demonstrates how individual 3D objects can be transformed or animated, while triangle caching is used on the remaining static 3D object to maintain a high frame rate.

var betweenNegOneAndOne:Number =
Math.sin(getTimer() * SCALE_FACTOR);
var betweenZeroAndOne:Number =
(betweenNegOneAndOne + 1) / 2;
var betweenOneAndTwo:Number =
betweenZeroAndOne + 1;
selectedMesh.scale(betweenOneAndTwo * MESH_SCALE);
}
}
}
}

Away3D 3.6 Essentials Take Flash to the next dimension by creating detailed, animated, and interactive 3D worlds with Away3D
Published: January 2011
eBook Price: $29.99
Book Price: $49.99
See more
Select your format and quantity:
        Read more about this book      

(For more resources on Away3D, see here.)

Level of detail models

Level of detail is a technique that is used to display simpler models with a lower polygon count when they are off in the background, while displaying higher quality models with larger polygon counts when they are closer to the camera. Sacrificing the quality of those 3D objects in the background can produce a significant performance boost, and because distant 3D objects are smaller when drawn on the screen quite often there is no noticeable drop in visual quality.

The LODObject class, from the away3d.containers package, is a container that will display its child 3D objects only when they fall within a certain perspective value range. This perspective value is calculated with the formula:

perspective value = camera zoom / (1 + distance from camera / camera focus)

A number of LODObject objects can be used as a group to implement the level of detail technique. As an example, let's take three sphere primitives, each constructed with a different number of polygons:

var sphere0:Sphere = new Sphere(
{
radius:50,
segmentsW: 4,
segmentsH:3
}
);
var sphere1:Sphere = new Sphere(
{
radius:50,
segmentsW: 10,
segmentsH:8
}
);
var sphere2:Sphere = new Sphere(
{
radius:50,
segmentsW: 16,
segmentsH:12
}
);

Each of these spheres is then added as a child of a new LODObject object. The minp init object parameter defines the minimum end of the perspective value range that the children of the LODObject object will be visible at. The maximum end of the range is defined up to (but not including) the value supplied via the maxp parameter.

var lodObject0:LODObject = new LODObject(
{
minp:0,
maxp:0.25
},
sphere0
);
var lodObject1:LODObject = new LODObject(
{
minp:0.25,
maxp:0.5
},
sphere1
);
var lodObject2:LODObject = new LODObject(
{
minp:0.5,
maxp:1
},
sphere2
);

To use these three LODObject objects as a group, they can be added as children of a standard ObjectContainer3D object:

var container:ObjectContainer3D =
new ObjectContainer3D(lodObject0, lodObject1, lodObject2);

When the container is added to the scene, one of these three spheres will then be visible as the distance between the ObjectContainer3D object (and its children LODObject objects) and the camera changes. To work out at what distance the spheres are visible, the preceding formula can be rewritten as:

distance from camera = (1 / perspective value * camera zoom – 1) * camera focus

Using the default values for the cameras zoom (10) and focus (100) properties, we can work out that the sphere0 3D object will be visible when it is greater than 3,900 units from the camera. The sphere1 3D object will be visible when it is between 3,900 and 1,900 units from the camera. And the sphere2 3D object will be visible when it is up to 1,900 units from the camera.

When the application is run, the following images will appear depending on the distance from the camera to the container that holds the LODObject objects:

Tips and Tricks on Away3D 3.6

Away3D filters

The FogFilter class could be used to add a fog effect to the scene. It is to be noted that those 3D objects that were behind the last layer of fog were not rendered at all, providing an increase in performance.

Two additional filters are also included in the away3d.core.filter package: MaxPolyFilter and ZDepthFilter. Neither adds a visual effect to the scene, but they can both be used to reduce the number of mesh elements that get drawn to the screen.

Both filters can be applied like the FogFilter class, by assigning them to the filters property available on both the BasicRenderer and QuadrantRenderer classes. Or they can be passed into either the BasicRenderer or QuadrantRenderer constructors, with the resulting render class then assigned to the renderer property from the View3D class.

ZDepthFilter

The ZDepthFilter class defines a maximum z depth for mesh elements. Those that lie beyond that maximum depth are not drawn to the screen. This provides the same performance benefits as the FogFilter class, but without the fog effect.

The ZDepthFilter constructor takes one parameter, maxZ, which defines the distance from the camera after which mesh elements are culled. In the following example, a new instance of the ZDepthFilter class has been created that will cull all mesh elements that are more than 200 units away from the camera.

var zDepthFilter:ZDepthFilter = new ZDepthFilter(200);
view.renderer = new BasicRenderer(zDepthFilter);

In Away3D 3.6, there is a bug in the ZDepthFilter class that prevents it from working correctly. By using the FrustumClipping class, from the away3d.core.clip package, you can achieve the same visual effect as the ZDepthFilter class, but there is no performance benefit.
view.clipping = new FrustumClipping({maxZ:200});

MaxPolyFilter

The MaxPolyFilter filter will only allow a set number of mesh elements to be drawn to the screen. It does this by retaining only the specified number of mesh elements from the collection that would be drawn to the screen. Since this collection is sorted by depth, this has the effect of discarding those mesh elements that represented the furthest 3D objects in the scene.

The ZDepthFilter constructor takes one parameter, maxP, which defines how many mesh elements will be rendered. The following code creates a new instance of the MaxPolyFilter class that will draw only the closest 500 mesh elements.

var maxPolyFilter:MaxPolyFilter = new MaxPolyFilter(500);
view.renderer = new BasicRenderer(maxPolyFilter);

The value assigned to the maxP property relates directly to the R ELEMENTS value displayed by the stats panel.

Offscreen rendering

There are many instances where a 3D application will display a great number of similar 3D objects. A school of fish, a crowd of people or a city block could easily be created by drawing a handful of individual 3D objects many times over.

Offscreen rendering can speed up these types of scenes considerably. Consider the city scene in the following screenshot:

Tips and Tricks on Away3D 3.6

Even though the scene is made up of hundreds of buildings, each building is displayed using one of five different models. Since each building is situated on a single plane, meaning each is being viewed from roughly the same angle, offscreen rendering can be employed in this situation to provide a performance boost.

The idea behind offscreen rendering is that a 3D object is rendered by a view that has not been added to the stage, and is therefore not visible (or "offscreen"). The image of the rendered 3D object is then displayed by a number of Sprite3D objects within the visible scene. Rendering a single 3D object and displaying the result on multiple Sprite3D objects is much faster than rendering the original 3D object multiple times.

Summary

Optimizing your Away3D application can be the difference between a slow and frustrating or rewarding experience for the end user. In this article, we looked at how the performance of an Away3D application can be easily monitored using the included Stats class. A number of techniques were then presented that can be employed to trade off visual accuracy with performance, such as reducing the stage quality, using filters to reduce the number of elements that are rendered to the screen, reducing the resolution of the rendered output, and reducing the screen area that is drawn to.


Further resources on this subject:


About the Author :


Matthew Casperson

Matthew Casperson has worked in IT for nearly a decade in a variety of roles including development and support, and in his spare time loves nothing more than to experiment with the latest web and multimedia technologies. Many of these experiments can be found on Matthews personal website at http://goo.gl/2Hgr.

Away3D Essentials is Matthews first book, but hopefully won't be the last!

Books From Packt


Away3D 3.6 Cookbook
Away3D 3.6 Cookbook

OGRE 3D 1.7 Beginner's Guide
OGRE 3D 1.7 Beginner's Guide

3D Graphics with XNA Game Studio 4.0
3D Graphics with XNA Game Studio 4.0

Panda3D 1.6 Game Engine Beginner's Guide
Panda3D 1.6 Game Engine Beginner's Guide

Unity 3D Game Development by Example Beginner's Guide
Unity 3D Game Development by Example Beginner's Guide

Papervision3D Essentials
Papervision3D Essentials

Blender 2.5 Materials and Textures Cookbook
Blender 2.5 Materials and Textures Cookbook

SketchUp 7.1 for Architectural Visualization: Beginner's Guide
SketchUp 7.1 for Architectural Visualization: Beginner's Guide


No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
F
h
y
Q
k
X
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software